BIRD contains a simple programming language. (No, it can't yet read mail :-). There are two objects in this language: filters and functions. Filters are interpreted by BIRD core when a route is being passed between protocols and routing tables. The filter language contains control structures such as if's and switches, but it allows no loops. An example of a filter using many features can be found in filter/test.conf.
Filter gets the route, looks at its attributes and
modifies some of them if it wishes. At the end, it decides whether to
pass the changed route through (using accept
) or whether to reject
it. A simple filter looks
like this:
filter not_too_far int var; { if defined( rip_metric ) then var = rip_metric; else { var = 1; rip_metric = 1; } if rip_metric > 10 then reject "RIP metric is too big"; else accept "ok"; }
As you can see, a filter has a header, a list of local variables, and a body. The header consists of
the filter
keyword followed by a (unique) name of filter. The list of local variables consists of
type name;
pairs where each pair defines one local variable. The body consists of
{ statements }
. Each statement is terminated by a ;
. You can group
several statements to a single compound statement by using braces ({ statements }
) which is useful if
you want to make a bigger block of code conditional.
BIRD supports functions, so that you don't have to repeat the same blocks of code over and over. Functions can have zero or more parameters and they can have local variables. Recursion is not allowed. Function definitions look like this:
function name () int local_variable; { local_variable = 5; } function with_parameters (int parameter) { print parameter; }
Unlike in C, variables are declared after the function
line, but before the first {
. You can't declare
variables in nested blocks. Functions are called like in C: name();
with_parameters(5);
. Function may return values using the return [expr]
command. Returning a value exits from current function (this is similar to C).
Filters are declared in a way similar to functions except they can't have explicit
parameters. They get a route table entry as an implicit parameter, it is also passed automatically
to any functions called. The filter must terminate with either
accept
or reject
statement. If there's a runtime error in filter, the route
is rejected.
A nice trick to debug filters is to use show route filter
name
from the command line client. An example session might look
like:
pavel@bug:~/bird$ ./birdc -s bird.ctl BIRD 0.0.0 ready. bird> show route 10.0.0.0/8 dev eth0 [direct1 23:21] (240) 195.113.30.2/32 dev tunl1 [direct1 23:21] (240) 127.0.0.0/8 dev lo [direct1 23:21] (240) bird> show route ? show route [<prefix>] [table <t>] [filter <f>] [all] [primary]... bird> show route filter { if 127.0.0.5 ~ net then accept; } 127.0.0.0/8 dev lo [direct1 23:21] (240) bird>
Each variable and each value has certain type. Booleans, integers and enums are incompatible with each other (that is to prevent you from shooting in the foot).
bool
This is a boolean type, it can have only two values, true
and
false
. Boolean is the only type you can use in if
statements.
int
This is a general integer type, you can expect it to store signed values from -2000000000
to +2000000000. Overflows are not checked. You can use 0x1234
syntax to write hexadecimal values.
pair
This is a pair of two short integers. Each component can have values from 0 to
65535. Literals of this type is written as (1234,5678)
.
string
This is a string of characters. There are no ways to modify strings in
filters. You can pass them between functions, assign them to variables of type string
, print
such variables, but you can't concatenate two strings. String literals
are written as "This is a string constant"
.
ip
This type can hold a single IP address. Depending on the compile-time configuration of BIRD you are using, it
is either an IPv4 or IPv6 address. IP addresses are written in the standard notation (10.20.30.40
or fec0:3:4::1
). You can apply special operator .mask(num)
on values of type ip. It masks out all but first num
bits from the IP
address. So 1.2.3.4.mask(8) = 1.0.0.0
is true.
prefix
This type can hold a network prefix consisting of IP address and prefix length. Prefix literals are written as
ipaddress/pxlen
, or
ipaddress/netmask
. There are two special
operators on prefixes:
.ip
which extracts the IP address from the pair, and .len
, which separates prefix
length from the pair. So 1.2.0.0/16.pxlen = 16
is true.
int|ip|prefix|pair|enum set
Filters recognize four types of sets. Sets are similar to strings: you can pass them around
but you can't modify them. Literals of type set int
look like [ 1, 2, 5..7 ]
. As you can see, both simple values and ranges are permitted in
sets. Sets of prefixes are special: you can specify which prefix lengths should match them by
using [ 1.0.0.0/8+, 2.0.0.0/8-, 3.0.0.0/8{5,6} ]
. 3.0.0.0/8{5,6}
matches
prefixes 3.X.X.X
whose prefix length is 5 to 6. address/num+
is a shorthand for address/{0,num}
,
address/num-
is a shorthand for address/{0,num-1}
. For example,
1.2.0.0/16 ~ [ 1.0.0.0/8{ 15 , 17 } ]
is true, but
1.0.0.0/8 ~ [ 1.0.0.0/8- ]
is false.
enum
Enumeration types are fixed sets of possibilities. You can't define your own variables of such type, but some route attributes are of enumeration type. Enumeration types are incompatible with each other.
bgppath
BGP path is a list of autonomous system numbers. You can't write literals of this type.
bgpmask
BGP masks are patterns used for BGP path matching
(using path ~ /2 3 5 ?/
syntax). The masks
resemble wildcard patterns as used by UNIX shells. Autonomous
system numbers match themselves, ?
matches any (even empty)
sequence of arbitrary AS numbers (*
hasn't been chosen, because
/*
starts a comment). For example:
/4 3 2 1/ ~ /? 4 3 ?/
is true, but
/4 3 2 1/ ~ /? 4 5 ?/
is false.
clist
Community list is similar to set of pairs, except that unlike other sets, it can be modified. There exist no literals of this type.
The filter language supports common integer operators (+,-,*,/)
, parentheses (a*(b+c))
, comparison
(a=b, a!=b, a<b, a>=b)
. Logical operations include unary not (!
), and (&&
) and or (||
).
Special operators include ~
for "is element of a set" operation - it can be
used on element and set of elements of the same type (returning true if element is contained in the given set), or on IP and prefix (returning true if IP is within the range defined by that prefix), or on
prefix and prefix (returning true if first prefix is more specific than second one) or on bgppath and bgpmask (returning true if the path matches the mask) or on pair and clist (returning true if the community is element of the community list).
Filters support two control structures: conditions and case switches.
Syntax of a condition is: if
boolean expression then command1; else command2;
and you can use {
command_1; command_2; ... }
instead of either command. The else
clause may be omitted. If the boolean expression
is true, command1
is executed, otherwise command2
is executed.
The case
is similar to case from Pascal. Syntax is case expr { else |
num_or_prefix [ .. num_or_prefix]: statement ; [ ... ] }
. The expression after
case
can be of any type which can be on the left side of the ~ operator and anything that could
be a member of a set is allowed before :
. Multiple commands are allowed without {}
grouping.
If expr
matches one of the :
clauses, statements between it and next :
statement are executed. If expr
matches neither of the :
clauses, the statements after else:
are executed.
Here is example that uses if
and case
structures:
case arg1 { 2: print "two"; print "I can do more commands without {}"; 3 .. 5: print "three to five"; else: print "something else"; } if 1234 = i then printn "."; else { print "not 1234"; print "You need {} around multiple commands"; }
A filter is implicitly passed a route, and it can access its
attributes just like it accesses variables. Attempts to access undefined
attribute result in a runtime error; you can check if an attribute is
defined by using the defined( attribute )
operator.
prefix net
Network the route is talking about. Read-only. (See the chapter about routing tables.)
enum scope
Address scope of the network (SCOPE_HOST
for addresses local to this host, SCOPE_LINK
for those specific for a physical link, SCOPE_SITE
and SCOPE_ORGANIZATION
for private addresses, SCOPE_UNIVERSE
for globally visible addresses).
int preference
Preference of the route. (See the chapter about routing tables.)
ip from
The router which the route has originated from. Read-only.
ip gw
Next hop packets routed using this route should be forwarded to.
enum source
what protocol has told me about this route. Possible values: RTS_DUMMY
, RTS_STATIC
, RTS_INHERIT
, RTS_DEVICE
, RTS_STATIC_DEVICE
, RTS_REDIRECT
, RTS_RIP
, RTS_OSPF
, RTS_OSPF_IA
, RTS_OSPF_EXT
, RTS_BGP
, RTS_PIPE
.
enum cast
Route type (RTC_UNICAST
for normal routes, RTC_BROADCAST
, RTC_MULTICAST
, RTC_ANYCAST
for broadcast, multicast and anycast routes). Read-only.
enum dest
Type of destination the packets should be sent to (RTD_ROUTER
for forwarding to a neighboring router, RTD_NETWORK
for routing to a directly-connected network, RTD_BLACKHOLE
for packets to be silently discarded, RTD_UNREACHABLE
, RTD_PROHIBIT
for packets that should be returned with ICMP host unreachable / ICMP administratively prohibited messages). Read-only.
There also exist some protocol-specific attributes which are described in the corresponding protocol sections.
The following statements are available:
variable = expr
Set variable to a given value.
accept|reject [ expr ]
Accept or reject the route, possibly printing expr
.
return expr
Return expr
from the current function, the function ends at this point.
print|printn expr [, expr...]
Prints given expressions; useful mainly while debugging
filters. The printn
variant does not terminate the line.
quitbird
Terminates BIRD. Useful when debugging the filter interpreter.