5. Filters
5.1 Introduction
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 declares 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 not have to repeat the same blocks of code over and over. Functions can have zero or more parameters and they can have local variables. If the function returns value, then you should always specify its return type. Direct recursion is possible. Function definitions look like this:
function name() -> int
{
int local_variable;
int another_variable = 5;
return 42;
}
function with_parameters(int parameter) -> pair
{
print parameter;
return (1, 2);
}
Like in C programming language, variables are declared inside function body,
either at the beginning, or mixed with other statements. Declarations may
contain initialization. You can also declare variables in nested blocks, such
variables have scope restricted to such block. There is a deprecated syntax to
declare variables after the function line, but before the first {.
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 defined in a way similar to functions except they cannot have
explicit parameters and cannot return. 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 is 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>
5.2 Data types
Each variable and each value has certain type. Booleans, integers and enums are incompatible with each other (that is to prevent you from shooting oneself in the foot).
boolThis is a boolean type, it can have only two values,
trueandfalse. Boolean is the only type you can use inifstatements.intThis is a general integer type. It is an unsigned 32bit type; i.e., you can expect it to store values from 0 to 4294967295. Overflows are not checked. You can use
0x1234syntax 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)).Operators
.asnand.datacan be used to extract corresponding components of a pair:(asn, data).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, use standard string comparison operations (e.g.=, !=, <, >, <=, >=), but you can't concatenate two strings. String literals are written as"This is a string constant". Additionally matching (~, !~) operators could be used to match a string value against a shell pattern (represented also as a string).bytestringThis is a sequence of arbitrary bytes. There are no ways to modify bytestrings in filters. You can pass them between functions, assign them to variables of type
bytestring, print such values, and compare bytestings (=, !=).Bytestring literals are written as a sequence of hexadecimal digit pairs, optionally colon-separated. A bytestring specified this way must be either at least 16 bytes (32 digits) long, or prefixed by the
hex:prefix:01:23:45:67:89:ab:cd:ef:01:23:45:67:89:ab:cd:ef,0123456789abcdef0123456789abcdef,hex:,hex:12:34:56,hex:12345678.A bytestring can be made from a hex string using
from_hex()function. Source strings can use any number of dots, colons, hyphens and spaces as byte separators:from_hex(" 12.34 56:78 ab-cd-ef ").ipThis type can hold a single IP address. The IPv4 addresses are stored as IPv4-Mapped IPv6 addresses so one data type for both of them is used. Whether the address is IPv4 or not may be checked by
.is_v4which returns abool. IP addresses are written in the standard notation (10.20.30.40orfec0:3:4::1). You can apply special operator.mask(num)on values of type ip. It masks out all but firstnumbits from the IP address. So1.2.3.4.mask(8) = 1.0.0.0is true.prefixThis type can hold a network prefix consisting of IP address, prefix length and several other values. This is the key in route tables.
Prefixes may be of several types, which can be determined by the special operator
.type. The type may be:NET_IP4andNET_IP6prefixes hold an IP prefix. The literals are written asipaddress/pxlen. There are two special operators on these:.ipwhich extracts the IP address from the pair, and.len, which separates prefix length from the pair. So1.2.0.0/16.len = 16is true.NET_IP6_SADRnettype holds both destination and source IPv6 prefix. The literals are written asipaddress/pxlen from ipaddress/pxlen, where the first part is the destination prefix and the second art is the source prefix. They support the same operators as IP prefixes, but just for the destination part. They also support.srcand.dstoperators to get respective parts of the address as separateNET_IP6values.NET_VPN4andNET_VPN6prefixes hold an IP prefix with VPN Route Distinguisher (RFC 4364). They support the same special operators as IP prefixes, and also.rdwhich extracts the Route Distinguisher. Their literals are written asrd ipprefixNET_ROA4andNET_ROA6prefixes hold an IP prefix range together with an ASN. They support the same special operators as IP prefixes, and also.maxlenwhich extracts maximal prefix length, and.asnwhich extracts the ASN.NET_FLOW4andNET_FLOW6hold an IP prefix together with a flowspec rule. Filters currently do not support much flowspec parsing, only.srcand.dstoperators to get source and destination parts of the flowspec as separateNET_IP4/NET_IP6values.NET_MPLSholds a single MPLS label and its handling is currently not implemented.rdThis is a route distinguisher according to RFC 4364. There are three kinds of RDs:
asn:32bit int,asn4:16bit intandIPv4 address:32bit intecThis 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), wherekindis a kind of extended community (e.g.rt/rofor a route target / route origin communities), the format and possible values ofkeyandvalueare usually integers, but it depends on the used kind. Similarly to pairs, ECs can be constructed using expressions forkeyandvalueparts, (e.g.(ro, myas, 3*10), wheremyasis an integer variable).lcThis is a specialized type used to represent BGP large community values. It is essentially a triplet of 32bit values, where the first value is reserved for the AS number of the issuer, while meaning of remaining parts is defined by the issuer. Literals of this type are written as
(123, 456, 789), with any integer values. Similarly to pairs, LCs can be constructed using expressions for its parts, (e.g.(myas, 10+20, 3*10), wheremyasis an integer variable).Operators
.asn,.data1, and.data2can be used to extract corresponding components of LCs:(asn, data1, data2).int|pair|quad|ip|prefix|ec|lc|rd|enum setFilters recognize several types of sets. Sets are similar to strings: you can pass them around but you cannot modify them. Literals of type
int setlook 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*anda..bexpressions 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).Also LC sets use similar expressions like pair sets. You can use ranges and wildcards, but if one field uses that, more specific (later) fields must be wildcards. E.g.,
(10, 20..30, *)or(10, 20, 30..40)is valid, while(10, *, 20..30)or(10, 20..30, 40)is not valid.You can also use named constants or compound expressions for non-prefix set values. However, it must be possible to evaluate these expressions before daemon boots. So you can use only constants inside them. Also, in case of compound expressions, they require parentheses around them. E.g.
define one=1; define myas=64500; int set odds = [ one, (2+1), (6-one), (2*2*2-1), 9, 11 ]; pair set ps = [ (1,one+one), (3,4)..(4,8), (5,*), (6,3..6), (7..9,*) ]; ec set es = [ (rt, myas, *), (rt, myas+2, 0..16*16*16-1) ];
Sets of prefixes are special: their literals does not allow ranges, but allows prefix patterns that are written as
ipaddress/pxlen{low,high}. Prefixip1/len1matches prefix patternip2/len2{l,h}if the firstmin(len1, len2)bits ofip1andip2are identical andl <= len1 <= h. A valid prefix pattern has to satisfylow <= high, butpxlenis not constrained byloworhigh. 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 foraddress/len{len,maxlen}(wheremaxlenis 32 for IPv4 and 128 for IPv6), that means network prefixaddress/lenand all its subnets.address/len-is a shorthand foraddress/len{0,len}, that means network prefixaddress/lenand 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 prefix1.0.0.0/8, all subprefixes of2.0.0.0/8, all superprefixes of3.0.0.0/8and prefixes4.X.X.Xwhose 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 address1.2.3.4.1.2.0.0/16 ~ [ 1.0.0.0/8{15,17} ]is true, but1.0.0.0/16 ~ [ 1.0.0.0/8- ]is false.Cisco-style patterns like
10.0.0.0/8 ge 16 le 24can be expressed in BIRD as10.0.0.0/8{16,24},192.168.0.0/16 le 24as192.168.0.0/16{16,24}and192.168.0.0/16 ge 24as192.168.0.0/16{24,32}.It is not possible to mix IPv4 and IPv6 prefixes in a prefix set. It is currently possible to mix IPv4 and IPv6 addresses in an ip set, but that behavior may change between versions without any warning; don't do it unless you are more than sure what you are doing. (Really, don't do it.)
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.firstreturns the first ASN (the neighbor ASN) in path P.P.lastreturns the last ASN (the source ASN) in path P.P.last_nonaggregatedreturns the last ASN in the non-aggregated part of the path P.Both
firstandlastreturn zero if there is no appropriate ASN, for example if the path contains an AS set element as the first (or the last) part. If the path ends with an AS set,last_nonaggregatedmay be used to get last ASN before any AS set.P.lenreturns the length of path P.P.emptymakes the path P empty. Can't be used as a value, always modifies the object.P.prepend(A)prepends ASN A to path P and returns the result.P.delete(A)deletes all instances of ASN A from from path P and returns the result. A may also be an integer set, in that case the operator deletes all ASNs from path P that are also members of set A.P.filter(A)deletes all ASNs from path P that are not members of integer set A, and returns the result. I.e.,filterdo the same asdeletewith inverted set A.Methods
prepend,deleteandfilterkeep the original object intact as long as you use the result in any way. You can also write e.g.P.prepend(A);as a standalone statement. This variant does modify the original object with the result of the operation.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, ifbgp_pathis 4 3 2 1, then:bgp_path ~ [= * 4 3 * =]is true, butbgp_path ~ [= * 4 5 * =]is false. There is also+operator which matches one or multiple instances of previous expression, e.g.[= 1 2+ 3 =]matches both path 1 2 3 and path 1 2 2 2 3, but not 1 3 nor 1 2 4 3. Note that while*and?are wildcard-style operators,+is regex-style operator.BGP mask expressions can also contain integer expressions enclosed in parenthesis and integer variables, for example
[= * 4 (1+2) a =]. You can also use ranges (e.g.[= * 3..5 2 100..200 * =]) and sets (e.g.[= 1 2 [3, 5, 7] * =]).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 special operators on clists:
C.lenreturns the length of clist C.C.emptymakes the list C empty. Can't be used as a value, always modifies the object.C.add(P)adds pair (or quad) P to clist C and returns the result. If item P is already in clist C, it does nothing. P may also be a clist, in that case all its members are added; i.e., it works as clist union.C.delete(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. Moreover, P may also be a clist, which works analogously; i.e., it works as clist difference.C.filter(P)deletes all items from clist C that are not members of pair (or quad) set P, and returns the result. I.e.,filterdo the same asdeletewith inverted set P. P may also be a clist, which works analogously; i.e., it works as clist intersection.Methods
add,deleteandfilterkeep the original object intact as long as you use the result in any way. You can also write e.g.P.add(A);as a standalone statement. This variant does modify the original object with the result of the operation.C.minreturns the minimum element of clist C.C.maxreturns the maximum element of clist C.Operators
.min,.maxcan be used together withfilterto extract the community from the specific subset of communities (e.g. localpref or prepend) without the need to check every possible value (e.g.filter(bgp_community, [(23456, 1000..1099)]).min).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,deleteor~and!~membership operators) can be used to modify or test eclists, with ECs instead of pairs as arguments.lclistLclist is a data type used for BGP large community lists. Like eclists, lclists are very similar to clists, but they are sets of LCs instead of pairs. The same operations (like
add,deleteor~and!~membership operators) can be used to modify or test lclists, with LCs instead of pairs as arguments.
5.3 Operators
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 (not) 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)
- two strings (returning true if the first string matches a shell-like pattern stored in the second string)
- IP and prefix (returning true if IP is within the range defined by that prefix)
- prefix and prefix (returning true if the first prefix is more specific than the second one)
- bgppath and bgpmask (returning true if the path matches the mask)
- number and bgppath (returning true if the number is in the path)
- bgppath and int (number) set (returning true if any ASN from the path is in the set)
- pair/quad and clist (returning true if the pair/quad is element of the clist)
- 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).
There are also operators related to RPKI infrastructure used to run RFC 6483 route origin validation and (draft) AS path validation.
roa_check(table)checks the current route in the specified ROA table and returnsROA_UNKNOWN,ROA_INVALIDorROA_VALID, if the validation result is unknown, invalid, or valid, respectively. The result is valid if there is a matching ROA, it is invalid if there is either matching ROA with a different ASN, or any covering ROA with shorter maximal prefix length.roa_check(table, prefix, asn)is an explicit version of the ROA check if the user for whatever reason needs to check a different prefix or different ASN than the default one. The equivalent call of the short variant isroa_check(table, net, bgp_path.last)and it is faster to call the short variant.aspa_check_downstream(table)checks the current route in the specified ASPA table and returnsASPA_UNKNOWN,ASPA_INVALID, orASPA_VALIDif the validation result is unknown, invalid, or valid, respectively. The result is valid if there is a full coverage of matching ASPA records according to the Algorithm for Downstream Paths by the (draft). This operator is not present if BGP is not compiled in.aspa_check_upstream(table)checks the current route in the specified ASPA table as the former operator, but it applies the (stricter) Algorithm for Upstream Paths by the (draft). This operator is not present if BGP is not compiled in.aspa_check(table, path, is_upstream)is an explicit version of the former two ASPA check operators. The equivalent ofaspa_check_downstreamisaspa_check(table, bgp_path, false)and foraspa_check_upstreamit isaspa_check(table, bgp_path, true). Note: the ASPA check does not include the local ASN in the AS path. Also,ASPA_INVALIDis returned for an empty AS path or for AS path containingCONFED_SETorCONFED_SEQUENCEblocks, as the (draft) stipulates.
The following example checks for ROA and ASPA on routes from a customer:
roa6 table r6;
aspa table at;
attribute int valid_roa;
attribute int valid_aspa;
filter customer_check {
case roa_check(r6) {
ROA_INVALID: reject "Invalid ROA";
ROA_VALID: valid_roa = 1;
}
case aspa_check_upstream(at) {
ASPA_INVALID: reject "Invalid ASPA";
ASPA_VALID: valid_aspa = 1;
}
accept;
}
5.4 Control structures
Filters support several control structures: conditions, for loops and case switches.
Syntax of a condition is: if boolean expression then commandT;
else commandF; and you can use { command1; command2;
... } instead of either command. The else clause may be
omitted. If the boolean expression is true, commandT is
executed, otherwise commandF is executed.
For loops allow to iterate over elements in compound data like BGP paths or
community lists. The syntax is: for [ type ] variable in expr
do command; and you can also use compound command like in conditions.
The expression is evaluated to a compound data, then for each element from such
data the command is executed with the item assigned to the variable. A variable
may be an existing one (when just name is used) or a locally defined (when type
and name is used). In both cases, it must have the same type as elements.
The case is similar to case from Pascal. Syntax is case
expr { else: | set_body_expr /: statement ; [... ] }.
The expression after case can be of any type that could be a member of
a set, while the set_body_expr before : can be anything (constants,
intervals, expressions) that could be a part of a set literal. One exception is
prefix type, which can be used in sets bud not in case structure. 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:
if 1234 = i then printn "."; else {
print "not 1234";
print "You need {} around multiple commands";
}
for int asn in bgp_path do {
printn "ASN: ", asn;
if asn < 65536 then print " (2B)"; else print " (4B)";
}
case arg1 {
2: print "two"; print "I can do more commands without {}";
3 .. 5: print "three to five";
else: print "something else";
}
5.5 Route attributes
A filter is implicitly passed a route, and it can access its attributes just
like it accesses variables. There are common route attributes, protocol-specific
route attributes and custom route attributes. Most common attributes are
mandatory (always defined), while remaining are optional. 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 bgppath and *clist types, where
undefined value is regarded as empty bgppath/*clist for most purposes.
Attributes can be defined by just setting them in filters. Custom attributes
have to be first declared by
attribute global
option. You can also undefine optional attribute back to non-existence by using
the unset( attribute ) operator.
Common route attributes are:
prefix netThe network prefix or anything else the route is talking about. The primary key of the routing table. Read-only. (See the chapter about routes.)
enum scopeThe scope of the route. Possible values:
SCOPE_HOSTfor routes local to this host,SCOPE_LINKfor those specific for a physical link,SCOPE_SITEandSCOPE_ORGANIZATIONfor private routes andSCOPE_UNIVERSEfor 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 isSCOPE_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.
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_STATIC,RTS_INHERIT,RTS_DEVICE,RTS_RIP,RTS_OSPF,RTS_OSPF_IA,RTS_OSPF_EXT1,RTS_OSPF_EXT2,RTS_BGP,RTS_PIPE,RTS_BABEL.enum destType of destination the packets should be sent to (
RTD_ROUTERfor forwarding to a neighboring router,RTD_DEVICEfor routing to a directly-connected network,RTD_MULTIPATHfor multipath destinations,RTD_BLACKHOLEfor packets to be silently discarded,RTD_UNREACHABLE,RTD_PROHIBITfor packets that should be returned with ICMP host unreachable / ICMP administratively prohibited messages). Can be changed, but only toRTD_BLACKHOLE,RTD_UNREACHABLEorRTD_PROHIBIT.string ifnameName of the outgoing interface. Sink routes (like blackhole, unreachable or prohibit) and multipath routes have no interface associated with them, so
ifnamereturns an empty string for such routes. Setting it would also change route to a direct one (remove gateway).int ifindexIndex of the outgoing interface. System wide index of the interface. May be used for interface matching, however indexes might change on interface creation/removal. Zero is returned for routes with undefined outgoing interfaces. Read-only.
bool onlinkOnlink flag means that the specified nexthop is accessible on the interface regardless of IP prefixes configured on the interface. The attribute can be used to configure such next hops by first setting
onlink = trueandifname, and then settinggw. Possible use case for setting this flag is to automatically build overlay IP-IP networks on linux.int weightMultipath weight of route next hops. Valid values are 1-256. Reading returns the weight of the first next hop, setting it sets weights of all next hops to the specified value. Therefore, this attribute is not much useful for manipulating individual next hops of an ECMP route, but can be used in BGP multipath setup to set weights of individual routes that are merged to one ECMP route during export to the Kernel protocol (with active marge paths option).
int gw_mplsOutgoing MPLS label attached to route (i.e., incoming MPLS label on the next hop router for this label-switched path). Reading returns the label value and setting it sets it to the start of the label stack. Setting implicit-NULL label (3) disables the MPLS label stack. Only the first next hop and only one label in the label stack supported right now. This is experimental option, will be likely changed in the future to handle full MPLS label stack.
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_metric1for OSPF routes). It is used mainly by BGP to compare internal distances to boundary routers (see below).int mpls_labelLocal MPLS label attached to the route. This attribute is produced by MPLS-aware protocols for labeled routes. It can also be set in import filters to assign static labels, but that also requires static MPLS label policy.
enum mpls_policyFor MPLS-aware protocols, this attribute defines which MPLS label policy will be used for the route. It can be set in import filters to change it on per-route basis. Valid values are
MPLS_POLICY_NONE(no label),MPLS_POLICY_STATIC(static label),MPLS_POLICY_PREFIX(per-prefix label),MPLS_POLICY_AGGREGATE(aggregated label), andMPLS_POLICY_VRF(per-VRF label). See MPLS label policy for details.int mpls_classWhen MPLS label policy is set to
aggregate, it may be useful to apply more fine-grained aggregation than just one based on next hops. When routes have different value of this attribute, they will not be aggregated under one local label even if they have the same next hops.
Protocol-specific route attributes are described in the corresponding protocol sections.
5.6 Other statements
The following statements are available:
variable = exprSet variable (or route attribute) to a given value.
accept|reject [ expr ]Accept or reject the route, possibly printing
expr.return exprReturn
exprfrom the current function, the function ends at this point.print|printn expr [, expr...]Prints given expressions; useful mainly while debugging filters. The
printnvariant does not terminate the line.
Next Previous Contents
