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).
boolThis is a boolean type, it can have only two values, true and
false. Boolean is the only type you can use in if
statements.
intThis 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.
pairThis is a pair of two short integers. Each component can have values from 0 to
65535. Literals of this type are written as (1234,5678). The same syntax can also be
used to construct a pair from two arbitrary integer expressions (for example (1+2,a)).
quadThis is a dotted quad of numbers used to represent router IDs (and others). Each component can have a value from 0 to 255. Literals of this type are written like IPv4 addresses.
stringThis 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".
ipThis 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.
prefixThis 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.
ecThis is a specialized type used to represent BGP
extended community values. It is essentially a 64bit value,
literals of this type are usually written as (kind,
key, value), where kind is a kind of
extended community (e.g. rt / ro for a route
target / route origin communities), the format and possible
values of key and value are usually integers, but
it depends on the used kind. Similarly to pairs, ECs can be
constructed using expressions for key and
value parts, (e.g. (ro, myas, 3*10), where
myas is an integer variable).
int|pair|quad|ip|prefix|ec|enum setFilters recognize four types of sets. Sets are similar to strings: you can pass them around
but you can't modify them. Literals of type int set look like [ 1, 2, 5..7 ]. As you can see, both simple values and ranges are permitted in
sets.
For pair sets, expressions like (123,*) can be used to denote ranges (in
that case (123,0)..(123,65535)). You can also use (123,5..100) for range
(123,5)..(123,100). You can also use * and a..b expressions
in the first part of a pair, note that such expressions are translated to a set
of intervals, which may be memory intensive. E.g. (*,4..20) is translated to
(0,4..20), (1,4..20), (2,4..20), ... (65535, 4..20).
EC sets use similar expressions like pair sets, e.g. (rt, 123, 10..20)
or (ro, 123, *). Expressions requiring the translation (like (rt, *, 3))
are not allowed (as they usually have 4B range for ASNs).
You can also use expressions for int, pair and EC set values. However it must be possible to evaluate these expressions before daemon boots. So you can use only constants inside them. E.g.
define one=1;
define myas=64500;
int set odds;
pair set ps;
ec set es;
odds = [ one, 2+1, 6-one, 2*2*2-1, 9, 11 ];
ps = [ (1,one+one), (3,4)..(4,8), (5,*), (6,3..6), (7..9,*) ];
es = [ (rt, myas, 3*10), (rt, myas+one, 0..16*16*16-1), (ro, myas+2, *) ];
Sets of prefixes are special: their literals does not allow ranges, but allows
prefix patterns that are written as ipaddress/pxlen{low,high}.
Prefix ip1/len1 matches prefix pattern ip2/len2{l,h} if
the first min(len1, len2) bits of ip1 and ip2 are identical and len1 <= ip1 <= len2.
A valid prefix pattern has to satisfy low <= high, but pxlen is not constrained by low
or high. Obviously, a prefix matches a prefix set literal if it matches any prefix pattern in the
prefix set literal.
There are also two shorthands for prefix patterns: address/len+ is a shorthand for
address/len{len,maxlen} (where maxlen is 32 for IPv4 and 128 for IPv6),
that means network prefix address/len and all its subnets. address/len-
is a shorthand for address/len{0,len}, that means network prefix address/len
and all its supernets (network prefixes that contain it).
For example, [ 1.0.0.0/8, 2.0.0.0/8+, 3.0.0.0/8-, 4.0.0.0/8{16,24} ] matches
prefix 1.0.0.0/8, all subprefixes of 2.0.0.0/8, all superprefixes of 3.0.0.0/8 and prefixes
4.X.X.X whose prefix length is 16 to 24. [ 0.0.0.0/0{20,24} ] matches all prefixes (regardless of
IP address) whose prefix length is 20 to 24, [ 1.2.3.4/32- ] matches any prefix that contains IP address
1.2.3.4. 1.2.0.0/16 ~ [ 1.0.0.0/8{15,17} ] is true,
but 1.0.0.0/16 ~ [ 1.0.0.0/8- ] is false.
Cisco-style patterns like 10.0.0.0/8 ge 16 le 24 can be expressed
in BIRD as 10.0.0.0/8{16,24}, 192.168.0.0/16 le 24 as
192.168.0.0/16{16,24} and 192.168.0.0/16 ge 24 as
192.168.0.0/16{24,32}.
enumEnumeration 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.
bgppathBGP path is a list of autonomous system numbers. You can't write literals of this type. There are several special operators on bgppaths:
P.first returns the first ASN (the neighbor ASN) in path P.
P.last returns the last ASN (the source ASN) in path P.
Both first and last return zero if there is no appropriate ASN,
for example if the path contains an AS set element as the first (or the last) part.
P.len returns the length of path P.
prepend(P,A) prepends ASN A to path P and returns the result.
Statement P = prepend(P, A); can be shortened to
P.prepend(A); if P is appropriate route attribute
(for example bgp_path).
bgpmaskBGP 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 and ? matches one arbitrary AS number.
For example, if bgp_path is 4 3 2 1, then:
bgp_path ~ [= * 4 3 * =] is true, but
bgp_path ~ [= * 4 5 * =] is false.
BGP mask expressions can also contain integer expressions enclosed in parenthesis
and integer variables, for example [= * 4 (1+2) a =].
There is also old syntax that uses / .. / instead of [= .. =] and ? instead of *.
clistClist is similar to a set, except that unlike other sets, it can be modified. The type is used for community list (a set of pairs) and for cluster list (a set of quads). There exist no literals of this type. There are three special operators on clists:
add(C,P) adds pair (or quad) P to clist
C and returns the result. If item P is already in
clist C, it does nothing.
delete(C,P) deletes pair (or quad)
P from clist C and returns the result. If clist
C does not contain item P, it does nothing.
P may also be a pair (or quad) set, in that case the
operator deletes all items from clist C that are also
members of set P.
filter(C,P) deletes all items from clist
C that are not members of pair (or quad) set P.
I.e., filter do the same as delete with inverted
set P.
Statement C = add(C, P); can be shortened to
C.add(P); if C is appropriate route
attribute (for example bgp_community). Similarly for
delete and filter.
eclistEclist is a data type used for BGP extended community lists.
Eclists are very similar to clists, but they are sets of ECs
instead of pairs. The same operations (like add,
delete, or ~ membership operator) can be
used to modify or test eclists, with ECs instead of pairs as
arguments.
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 two strings (returning true if first string matches a shell-like pattern stored in second string) 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/quad and clist (returning true if the pair/quad is element of the clist) or on clist and pair/quad set (returning true if there is an element of the clist that is also a member of the pair/quad set).
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.
One notable exception to this rule are attributes of clist type, where
undefined value is regarded as empty clist for most purposes.
prefix netNetwork the route is talking about. Read-only. (See the chapter about routing tables.)
enum scopeThe scope of the route. Possible values: SCOPE_HOST for
routes local to this host, SCOPE_LINK for those specific
for a physical link, SCOPE_SITE and
SCOPE_ORGANIZATION for private routes and
SCOPE_UNIVERSE for globally visible routes. This
attribute is not interpreted by BIRD and can be used to mark
routes in filters. The default value for new routes is
SCOPE_UNIVERSE.
int preferencePreference of the route. Valid values are 0-65535. (See the chapter about routing tables.)
ip fromThe router which the route has originated from. Read-only.
ip gwNext hop packets routed using this route should be forwarded to.
string protoThe name of the protocol which the route has been imported from. Read-only.
enum sourcewhat 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_EXT1, RTS_OSPF_EXT2, RTS_BGP, RTS_PIPE.
enum castRoute type (Currently RTC_UNICAST for normal routes,
RTC_BROADCAST, RTC_MULTICAST, RTC_ANYCAST will
be used in the future for broadcast, multicast and anycast
routes). Read-only.
enum destType of destination the packets should be sent to (RTD_ROUTER for forwarding to a neighboring router, RTD_DEVICE 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.
int igp_metricThe optional attribute that can be used to specify a distance
to the network for routes that do not have a native protocol
metric attribute (like ospf_metric1 for OSPF routes). It
is used mainly by BGP to compare internal distances to boundary
routers (see below). It is also used when the route is exported
to OSPF as a default value for OSPF type 1 metric.
There also exist some protocol-specific attributes which are described in the corresponding protocol sections.
The following statements are available:
variable = exprSet variable to a given value.
accept|reject [ expr ]Accept or reject the route, possibly printing expr.
return exprReturn 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.
quitbirdTerminates BIRD. Useful when debugging the filter interpreter.