Merge branch 'int-new' of ssh://gitlab.labs.nic.cz/labs/bird into int-new

This commit is contained in:
Ondrej Filip 2018-03-22 13:25:58 +01:00
commit 966602602a
16 changed files with 687 additions and 544 deletions

12
NEWS
View file

@ -1,3 +1,15 @@
Version 2.0.2 (2018-03-xx)
o Source-specific routing support for Linux kernel and Babel
o BGP: New option 'disable after cease'
o Filter: Allow silent filter execution
o Filter: Fixed stack overflow in BGP mask expressions.
o Several bugfixes
Notes:
Syntax prefix:netmask for IPv4 prefixes was dropped. Just use prefix/pxlen.
Version 2.0.1 (2018-01-16)
o Linux MPLS kernel support
o Better handling of channels inherited from templates

View file

@ -49,6 +49,8 @@ CF_DECLS
struct rtable_config *r;
struct channel_config *cc;
struct f_inst *x;
struct f_dynamic_attr fda;
struct f_static_attr fsa;
struct filter *f;
struct f_tree *e;
struct f_trie *trie;
@ -177,10 +179,6 @@ pxlen4:
if ($2 > IP4_MAX_PREFIX_LENGTH) cf_error("Invalid prefix length %u", $2);
$$ = $2;
}
| ':' IP4 {
$$ = ip4_masklen($2);
if ($$ == 255) cf_error("Invalid netmask %I4", $2);
}
;
net_ip4_: IP4 pxlen4

View file

@ -1,222 +1,204 @@
/*
* This is an example configuration file
* (for version 1.x.x, obsolete)
*/
# Yes, even shell-like comments work...
# This is a basic configuration file, which contains boilerplate options and
# some basic examples. It allows the BIRD daemon to start but will not cause
# anything else to happen.
#
# Please refer to the BIRD User's Guide documentation, which is also available
# online at http://bird.network.cz/ in HTML format, for more information on
# configuring BIRD and adding routing protocols.
# Configure logging
#log syslog { debug, trace, info, remote, warning, error, auth, fatal, bug };
#log stderr all;
#log "tmp" all;
log syslog all;
# log "/var/log/bird.log" { debug, trace, info, remote, warning, error, auth, fatal, bug };
# Override router ID
#router id 198.51.100.1;
# Set router ID. It is a unique identification of your router, usually one of
# IPv4 addresses of the router. It is recommended to configure it explicitly.
# router id 198.51.100.1;
# You can define your own symbols...
#define xyzzy = (120+10);
#define '1a-a1' = (30+40);
# Define a route filter...
#filter test_filter {
# if net ~ 10.0.0.0/16 then accept;
# else reject;
#}
#filter sink { reject; }
#filter okay { accept; }
#include "filters.conf";
# Define another routing table
#table testable;
# Turn on global debugging of all protocols
#debug protocols all;
# Turn on global debugging of all protocols (all messages or just selected classes)
# debug protocols all;
# debug protocols { events, states };
# Turn on internal watchdog
#watchdog warning 5 s;
#watchdog timeout 30 s;
# watchdog warning 5 s;
# watchdog timeout 30 s;
# The direct protocol automatically generates device routes to
# all network interfaces. Can exist in as many instances as you wish
# if you want to populate multiple routing tables with device routes.
#protocol direct {
# interface "-eth*", "*"; # Restrict network interfaces it works with
#}
# You can define your own constants
# define my_asn = 65000;
# define my_addr = 198.51.100.1;
# This pseudo-protocol performs synchronization between BIRD's routing
# tables and the kernel. If your kernel supports multiple routing tables
# (as Linux 2.2.x does), you can run multiple instances of the kernel
# protocol and synchronize different kernel tables with different BIRD tables.
protocol kernel {
# learn; # Learn all alien routes from the kernel
persist; # Don't remove routes on bird shutdown
scan time 20; # Scan kernel routing table every 20 seconds
# import none; # Default is import all
export all; # Default is export none
# kernel table 5; # Kernel table to synchronize with (default: main)
}
# Tables master4 and master6 are defined by default
# ipv4 table master4;
# ipv6 table master6;
# This pseudo-protocol watches all interface up/down events.
# Define more tables, e.g. for policy routing or as MRIB
# ipv4 table mrib4;
# ipv6 table mrib6;
# The Device protocol is not a real routing protocol. It does not generate any
# routes and it only serves as a module for getting information about network
# interfaces from the kernel. It is necessary in almost any configuration.
protocol device {
scan time 10; # Scan interfaces every 10 seconds
}
# Static routes (again, there can be multiple instances, so that you
# can disable/enable various groups of static routes on the fly).
# The direct protocol is not a real routing protocol. It automatically generates
# direct routes to all network interfaces. Can exist in as many instances as you
# wish if you want to populate multiple routing tables with direct routes.
protocol direct {
disabled; # Disable by default
ipv4; # Connect to default IPv4 table
ipv6; # ... and to default IPv6 table
}
# The Kernel protocol is not a real routing protocol. Instead of communicating
# with other routers in the network, it performs synchronization of BIRD
# routing tables with the OS kernel. One instance per table.
protocol kernel {
ipv4 { # Connect protocol to IPv4 table by channel
# table master4; # Default IPv4 table is master4
# import all; # Import to table, default is import all
export all; # Export to protocol. default is export none
};
# learn; # Learn alien routes from the kernel
# kernel table 10; # Kernel table to synchronize with (default: main)
}
# Another instance for IPv6, skipping default options
protocol kernel {
ipv6 { export all; };
}
# Static routes (Again, there can be multiple instances, for different address
# families and to disable/enable various groups of static routes on the fly).
protocol static {
# disabled; # Disable by default
# table testable; # Connect to a non-default table
# preference 1000; # Default preference of routes
# debug { states, routes, filters, interfaces, events, packets };
# debug all;
# route 0.0.0.0/0 via 198.51.100.13;
# route 198.51.100.0/25 unreachable;
ipv4; # Again, IPv4 channel with default options
# route 0.0.0.0/0 via 198.51.100.10;
# route 192.0.2.0/24 blackhole;
# route 10.0.0.0/8 unreachable;
# route 10.1.1.0:255.255.255.0 via 198.51.100.3;
# route 10.1.2.0:255.255.255.0 via 198.51.100.3;
# route 10.1.3.0:255.255.255.0 via 198.51.100.4;
# route 10.2.0.0/24 via "arc0";
# route 10.2.0.0/24 via "eth0";
# # Static routes can be defined with optional attributes
# route 10.1.1.0/24 via 198.51.100.3 { rip_metric = 3; };
# route 10.1.2.0/24 via 198.51.100.3 { ospf_metric1 = 100; };
# route 10.1.3.0/24 via 198.51.100.4 { ospf_metric2 = 100; };
}
# Pipe protocol connects two routing tables... Beware of loops.
#protocol pipe {
# peer table testable;
# Define what routes do we export to this protocol / import from it.
# import all; # default is all
# export all; # default is none
# import none; # If you wish to disable imports
# import filter test_filter; # Use named filter
# import where source = RTS_DEVICE; # Use explicit filter
#}
# Pipe protocol connects two routing tables. Beware of loops.
# protocol pipe {
# table master4; # No ipv4/ipv6 channel definition like in other protocols
# peer table mrib4;
# import all; # Direction peer table -> table
# export all; # Direction table -> peer table
# }
# RIP aka Rest In Pieces...
#protocol rip MyRIP { # You can also use an explicit name
# preference xyzzy;
# debug all;
# port 1520;
# period 7;
# infinity 16;
# garbage time 60;
# interface "*" { mode broadcast; };
# honor neighbor; # To whom do we agree to send the routing table
# honor always;
# honor never;
# passwords {
# password "nazdar";
# RIP example, both RIP and RIPng are supported
# protocol rip {
# ipv4 {
# # Export direct, static routes and ones from RIP itself
# import all;
# export where source ~ [ RTS_DEVICE, RTS_STATIC, RTS_RIP ];
# };
# authentication none;
# import filter { print "importing"; accept; };
# export filter { print "exporting"; accept; };
#}
# interface "eth*" {
# update time 10; # Default period is 30
# timeout time 60; # Default timeout is 180
# authentication cryptographic; # No authentication by default
# password "hello" { algorithm hmac sha256; }; # Default is MD5
# };
# }
#protocol ospf MyOSPF {
# tick 2;
# rfc1583compat yes;
# area 0.0.0.0 {
# stub no;
# OSPF example, both OSPFv2 and OSPFv3 are supported
# protocol ospf v3 {
# ipv6 {
# import all;
# export where source = RTS_STATIC;
# };
# area 0 {
# interface "eth*" {
# hello 9;
# retransmit 6;
# cost 10;
# transmit delay 5;
# dead count 5;
# wait 50;
# type broadcast;
# authentication simple;
# password "pass";
# type broadcast; # Detected by default
# cost 10; # Interface metric
# hello 5; # Default hello perid 10 is too long
# };
# interface "arc0" {
# rx buffer large;
# type nonbroadcast;
# poll 14;
# dead 75;
# neighbors {
# 10.1.1.2 eligible;
# 10.1.1.4;
# };
# strict nonbroadcast yes;
# interface "tun*" {
# type ptp; # PtP mode, avoids DR selection
# cost 100; # Interface metric
# hello 5; # Default hello perid 10 is too long
# };
# interface "xxx0" {
# passwords {
# password "abc" {
# id 1;
# generate to "22-04-2003 11:00:06";
# accept to "17-01-2004 12:01:05";
# };
# password "def" {
# id 2;
# generate from "22-04-2003 11:00:07";
# accept from "17-01-2003 12:01:05";
# };
# };
# authentication cryptographic;
# interface "dummy0" {
# stub; # Stub interface, just propagate it
# };
# };
# area 20 {
# stub 1;
# interface "ppp1" {
# hello 8;
# authentication none;
# };
# interface "fr*";
# virtual link 192.168.0.1 {
# password "sdsdffsdfg";
# authentication cryptographic;
# };
# };
#}
# Define simple filter as an example for BGP import filter
# See https://gitlab.labs.nic.cz/labs/bird/wikis/BGP_filtering for more examples
# filter rt_import
# {
# if bgp_path.first != 64496 then accept;
# if bgp_path.len > 64 then accept;
# if bgp_next_hop != from then accept;
# reject;
# }
#protocol bgp {
# disabled;
# BGP example, explicit name 'uplink1' is used instead of default 'bgp1'
# protocol bgp uplink1 {
# description "My BGP uplink";
# local as 65000;
# neighbor 198.51.100.130 as 64496;
# multihop;
# hold time 240;
# startup hold time 240;
# connect retry time 120;
# keepalive time 80; # defaults to hold time / 3
# start delay time 5; # How long do we wait before initial connect
# error wait time 60, 300;# Minimum and maximum time we wait after an error (when consecutive
# # errors occur, we increase the delay exponentially ...
# error forget time 300; # ... until this timeout expires)
# disable after error; # Disable the protocol automatically when an error occurs
# next hop self; # Disable next hop processing and always advertise our local address as nexthop
# path metric 1; # Prefer routes with shorter paths (like Cisco does)
# default bgp_med 0; # MED value we use for comparison when none is defined
# default bgp_local_pref 0; # The same for local preference
# source address 198.51.100.14; # What local address we use for the TCP connection
# local 198.51.100.1 as 65000;
# neighbor 198.51.100.10 as 64496;
# hold time 90; # Default is 240
# password "secret"; # Password used for MD5 authentication
# rr client; # I am a route reflector and the neighor is my client
# rr cluster id 1.0.0.1; # Use this value for cluster id instead of my router id
# export where source=RTS_STATIC;
# export filter {
# if source = RTS_STATIC then {
# bgp_community = -empty-; bgp_community = add(bgp_community,(65000,5678));
# bgp_origin = 0;
# bgp_community = -empty-; bgp_community.add((65000,5678));
# if (65000,64501) ~ bgp_community then
# bgp_community.add((0, 1));
# if bgp_path ~ [= 65000 =] then
# bgp_path.prepend(65000);
# accept;
# }
# reject;
#
# ipv4 { # regular IPv4 unicast (1/1)
# import filter rt_import;
# export where source ~ [ RTS_STATIC, RTS_BGP ];
# };
#
# ipv6 { # regular IPv6 unicast (2/1)
# import filter rt_import;
# export filter { # The same as 'where' expression above
# if source ~ [ RTS_STATIC, RTS_BGP ]
# then accept;
# else reject;
# };
# };
#
# ipv4 multicast { # IPv4 multicast topology (1/2)
# table mrib4; # explicit IPv4 table
# import filter rt_import;
# export all;
# };
#
# ipv6 multicast { # IPv6 multicast topology (2/2)
# table mrib6; # explicit IPv6 table
# import filter rt_import;
# export all;
# };
#}
#
# Template usage example
#template bgp rr_client {
# disabled;
# local as 65000;
# multihop;
# Template example. Using templates to define IBGP route reflector clients.
# template bgp rr_clients {
# local 10.0.0.1 as 65000;
# neighbor as 65000;
# rr client;
# rr cluster id 1.0.0.1;
#}
#
#protocol bgp rr_abcd from rr_client {
# neighbor 10.1.4.7 as 65000;
#}
# ipv4 {
# import all;
# export where source = RTS_BGP;
# };
#
# ipv6 {
# import all;
# export where source = RTS_BGP;
# };
# }
#
# protocol bgp client1 from rr_clients {
# neighbor 10.0.1.1;
# }
#
# protocol bgp client2 from rr_clients {
# neighbor 10.0.2.1;
# }
#
# protocol bgp client3 from rr_clients {
# neighbor 10.0.3.1;
# }

View file

@ -293,6 +293,20 @@ routes are:
<item>Route next hops (see below)
</itemize>
<sect1>IPv6 source-specific routes
<label id="ip-sadr-routes">
<p>The IPv6 routes containing both destination and source prefix. They are used
for source-specific routing (SSR), also called source-address dependent routing
(SADR), see <rfc id="8043">. Currently limited mostly to the Babel protocol.
Configuration keyword is <cf/ipv6 sadr/.
<itemize>
<item>(PK) Route destination (IP prefix together with its length)
<item>(PK) Route source (IP prefix together with its length)
<item>Route next hops (see below)
</itemize>
<sect1>VPN IPv4 and IPv6 routes
<label id="vpn-routes">
@ -646,9 +660,9 @@ agreement").
restricted to interfaces assigned to the VRF and will use sockets bound
to the VRF. Appropriate VRF interface must exist on OS level. For kernel
protocol, an appropriate table still must be explicitly selected by
<cf/table/ option. Note that the VRF support in BIRD and Linux kernel
(4.11) is still in development and is currently problematic outside of
multihop BGP.
<cf/table/ option. Note that for proper VRF support it is necessary to
use Linux kernel version at least 4.14, older versions have limited VRF
implementation.
<tag><label id="proto-channel"><m/channel name/ [{<m/channel config/}]</tag>
Every channel must be explicitly stated. See the protocol-specific
@ -1234,12 +1248,17 @@ foot).
operator <cf/.type/. The type may be:
<cf/NET_IP4/ and <cf/NET_IP6/ prefixes hold an IP prefix. The literals
are written as <cf><m/ipaddress//<m/pxlen/</cf>,
or <cf><m>ipaddress</m>/<m>netmask</m></cf>. There are two special
are written as <cf><m/ipaddress//<m/pxlen/</cf>. There are two special
operators on these: <cf/.ip/ which extracts the IP address from the
pair, and <cf/.len/, which separates prefix length from the pair.
So <cf>1.2.0.0/16.len = 16</cf> is true.
<cf/NET_IP6_SADR/ nettype holds both destination and source IPv6
prefix. The literals are written as <cf><m/ipaddress//<m/pxlen/ from
<m/ipaddress//<m/pxlen/</cf>, 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.
<cf/NET_VPN4/ and <cf/NET_VPN6/ prefixes hold an IP prefix with VPN
Route Distinguisher (<rfc id="4364">). They support the same special
operators as IP prefixes, and also <cf/.rd/ which extracts the Route
@ -1460,6 +1479,7 @@ foot).
lclists, with LCs instead of pairs as arguments.
</descrip>
<sect>Operators
<label id="operators">
@ -1652,19 +1672,25 @@ cases desirable.
routes over the same IPv6 transport. For sending and receiving Babel packets,
only a link-local IPv6 address is needed.
<p>BIRD does not implement any Babel extensions, but will coexist with
implementations using extensions (and will just ignore extension messages).
<p>BIRD implements an extension for IPv6 source-specific routing (SSR or SADR),
but must be configured accordingly to use it. SADR-enabled Babel router can
interoperate with non-SADR Babel router, but the later would ignore routes
with specific (non-zero) source prefix.
<sect1>Configuration
<label id="babel-config">
<p>Babel supports no global configuration options apart from those common to all
other protocols, but supports the following per-interface configuration options:
<p>The Babel protocol support both IPv4 and IPv6 channels; both can be
configured simultaneously. It can also be configured with <ref
id="ip-sadr-routes" name="IPv6 SADR"> channel instead of regular IPv6
channel, in such case SADR support is enabled. Babel supports no global
configuration options apart from those common to all other protocols, but
supports the following per-interface configuration options:
<code>
protocol babel [<name>] {
ipv4 { <channel config> };
ipv6 { <channel config> };
ipv6 [sadr] { <channel config> };
interface <interface pattern> {
type <wired|wireless>;
rxcost <number>;
@ -1684,8 +1710,8 @@ protocol babel [<name>] {
</code>
<descrip>
<tag><label id="babel-channel">ipv4|ipv6 <m/channel config/</tag>
The supported channels are IPv4 and IPv6.
<tag><label id="babel-channel">ipv4 | ipv6 [sadr] <m/channel config/</tag>
The supported channels are IPv4, IPv6, and IPv6 SADR.
<tag><label id="babel-type">type wired|wireless </tag>
This option specifies the interface type: Wired or wireless. On wired
@ -2119,22 +2145,24 @@ to set routing policy and all the other parameters differently for each neighbor
using the following configuration parameters:
<descrip>
<tag><label id="bgp-local">local [<m/ip/] as <m/number/</tag>
<tag><label id="bgp-local">local [<m/ip/] [port <m/number/] [as <m/number/]</tag>
Define which AS we are part of. (Note that contrary to other IP routers,
BIRD is able to act as a router located in multiple AS'es simultaneously,
but in such cases you need to tweak the BGP paths manually in the filters
to get consistent behavior.) Optional <cf/ip/ argument specifies a source
address, equivalent to the <cf/source address/ option (see below). This
parameter is mandatory.
address, equivalent to the <cf/source address/ option (see below).
Optional <cf/port/ argument specifies the local BGP port instead of
standard port 179. The parameter may be used multiple times with
different sub-options (e.g., both <cf/local 10.0.0.1 as 65000;/ and
<cf/local 10.0.0.1; local as 65000;/ are valid). This parameter is
mandatory.
<tag><label id="bgp-neighbor">neighbor [<m/ip/] [port <m/number/] [as <m/number/]</tag>
Define neighboring router this instance will be talking to and what AS
it is located in. In case the neighbor is in the same AS as we are, we
automatically switch to iBGP. Optionally, the remote port may also be
specified. The parameter may be used multiple times with different
sub-options (e.g., both <cf/neighbor 10.0.0.1 as 65000;/ and
<cf/neighbor 10.0.0.1; neighbor as 65000;/ are valid). This parameter is
mandatory.
specified. Like <cf/local/ parameter, this parameter may also be used
multiple times with different sub-options. This parameter is mandatory.
<tag><label id="bgp-iface">interface <m/string/</tag>
Define interface we should use for link-local BGP IPv6 sessions.
@ -2744,16 +2772,17 @@ protocol device {
<p>The Direct protocol is a simple generator of device routes for all the
directly connected networks according to the list of interfaces provided by the
kernel via the Device protocol. The Direct protocol supports both IPv4 and IPv6
channels.
channels; both can be configured simultaneously. It can also be configured with
<ref id="ip-sadr-routes" name="IPv6 SADR"> channel instead of regular IPv6
channel in order to be used together with SADR-enabled Babel protocol.
<p>The question is whether it is a good idea to have such device routes in BIRD
routing table. OS kernel usually handles device routes for directly connected
networks by itself so we don't need (and don't want) to export these routes to
the kernel protocol. OSPF protocol creates device routes for its interfaces
itself and BGP protocol is usually used for exporting aggregate routes. Although
there are some use cases that use the direct protocol (like abusing eBGP as an
IGP routing protocol), in most cases it is not needed to have these device
routes in BIRD routing table and to use the direct protocol.
itself and BGP protocol is usually used for exporting aggregate routes. But the
Direct protocol is necessary for distance-vector protocols like RIP or Babel to
announce local networks.
<p>There is one notable case when you definitely want to use the direct protocol
-- running BIRD on BSD systems. Having high priority device routes for directly
@ -2828,8 +2857,10 @@ kernel protocols to the same routing table and changing route destination
(gateway) in an export filter of a kernel protocol does not work. Both
limitations can be overcome using another routing table and the pipe protocol.
<p>The Kernel protocol supports both IPv4 and IPv6 channels; only one of them
can be configured in each protocol instance.
<p>The Kernel protocol supports both IPv4 and IPv6 channels; only one channel
can be configured in each protocol instance. On Linux, it also supports <ref
id="ip-sadr-routes" name="IPv6 SADR"> and <ref id="mpls-routes" name="MPLS">
channels.
<sect1>Configuration
<label id="krt-config">
@ -4280,6 +4311,7 @@ protocol rip {
<sect>RPKI
<label id="rpki">
<sect1>Introduction

View file

@ -12,8 +12,6 @@ CF_HDR
CF_DEFINES
#define P(a,b) ((a << 8) | b)
static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; }
static inline u32 pair_a(u32 p) { return p >> 16; }
static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
@ -157,12 +155,11 @@ f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3)
}
static inline struct f_inst *
f_generate_empty(struct f_inst *dyn)
f_generate_empty(struct f_dynamic_attr dyn)
{
struct f_inst *e = f_new_inst();
e->code = 'E';
struct f_inst *e = f_new_inst(FI_EMPTY);
switch (dyn->aux & EAF_TYPE_MASK) {
switch (dyn.type & EAF_TYPE_MASK) {
case EAF_TYPE_AS_PATH:
e->aux = T_PATH;
break;
@ -179,9 +176,9 @@ f_generate_empty(struct f_inst *dyn)
cf_error("Can't empty that attribute");
}
dyn->code = P('e','S');
dyn->a1.p = e;
return dyn;
struct f_inst *s = f_new_inst_da(FI_EA_SET, dyn);
s->a1.p = e;
return s;
}
@ -190,21 +187,19 @@ f_generate_dpair(struct f_inst *t1, struct f_inst *t2)
{
struct f_inst *rv;
if ((t1->code == 'c') && (t2->code == 'c')) {
if ((t1->fi_code == FI_CONSTANT) && (t2->fi_code == FI_CONSTANT)) {
if ((t1->aux != T_INT) || (t2->aux != T_INT))
cf_error( "Can't operate with value of non-integer type in pair constructor");
check_u16(t1->a2.i);
check_u16(t2->a2.i);
rv = f_new_inst();
rv->code = 'c';
rv = f_new_inst(FI_CONSTANT);
rv->aux = T_PAIR;
rv->a2.i = pair(t1->a2.i, t2->a2.i);
}
else {
rv = f_new_inst();
rv->code = P('m', 'p');
rv = f_new_inst(FI_PAIR_CONSTRUCT);
rv->a1.p = t1;
rv->a2.p = t2;
}
@ -219,7 +214,7 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
int c1 = 0, c2 = 0, ipv4_used = 0;
u32 key = 0, val2 = 0;
if (tk->code == 'c') {
if (tk->fi_code == FI_CONSTANT) {
c1 = 1;
if (tk->aux == T_INT) {
@ -233,7 +228,7 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
}
/* IP->Quad implicit conversion */
else if (tk->code == 'C') {
else if (tk->fi_code == FI_CONSTANT_INDIRECT) {
c1 = 1;
struct f_val *val = tk->a1.p;
@ -250,7 +245,7 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor");
}
if (tv->code == 'c') {
if (tv->fi_code == FI_CONSTANT) {
if (tv->aux != T_INT)
cf_error("Can't operate with value of non-integer type in EC constructor");
c2 = 1;
@ -276,15 +271,13 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
}
NEW_F_VAL;
rv = f_new_inst();
rv->code = 'C';
rv = f_new_inst(FI_CONSTANT_INDIRECT);
rv->a1.p = val;
val->type = T_EC;
val->val.ec = ec;
}
else {
rv = f_new_inst();
rv->code = P('m','c');
rv = f_new_inst(FI_EC_CONSTRUCT);
rv->aux = kind;
rv->a1.p = tk;
rv->a2.p = tv;
@ -298,12 +291,11 @@ f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3)
{
struct f_inst *rv;
if ((t1->code == 'c') && (t2->code == 'c') && (t3->code == 'c')) {
if ((t1->fi_code == FI_CONSTANT) && (t2->fi_code == FI_CONSTANT) && (t3->fi_code == FI_CONSTANT)) {
if ((t1->aux != T_INT) || (t2->aux != T_INT) || (t3->aux != T_INT))
cf_error( "LC - Can't operate with value of non-integer type in tuple constructor");
rv = f_new_inst();
rv->code = 'C';
rv = f_new_inst(FI_CONSTANT_INDIRECT);
NEW_F_VAL;
rv->a1.p = val;
@ -314,7 +306,7 @@ f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3)
{
rv = cfg_allocz(sizeof(struct f_inst3));
rv->lineno = ifs->lino;
rv->code = P('m','l');
rv->fi_code = FI_LC_CONSTRUCT;
rv->a1.p = t1;
rv->a2.p = t2;
INST3(rv).p = t3;
@ -323,6 +315,27 @@ f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3)
return rv;
}
static inline struct f_inst *
f_generate_path_mask(struct f_path_mask *t)
{
for (struct f_path_mask *tt = t; tt; tt = tt->next) {
if (tt->kind == PM_ASN_EXPR) {
struct f_inst *mrv = f_new_inst(FI_PATHMASK_CONSTRUCT);
mrv->a1.p = t;
return mrv;
}
}
NEW_F_VAL;
val->type = T_PATH_MASK;
val->val.path_mask = t;
struct f_inst *rv = f_new_inst(FI_CONSTANT_INDIRECT);
rv->a1.p = val;
return rv;
}
/*
* Remove all new lines and doubled whitespaces
* and convert all tabulators to spaces
@ -372,8 +385,7 @@ static struct f_inst *
assert_done(struct f_inst *expr, const char *start, const char *end)
{
struct f_inst *i;
i = f_new_inst();
i->code = P('a','s');
i = f_new_inst(FI_ASSERT);
i->a1.p = expr;
if (end >= start)
@ -412,7 +424,9 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%nonassoc THEN
%nonassoc ELSE
%type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn dynamic_attr static_attr function_call symbol bgp_path_expr
%type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn function_call symbol bgp_path_expr
%type <fda> dynamic_attr
%type <fsa> static_attr
%type <f> filter filter_body where_filter
%type <i> type break_command ec_kind
%type <i32> cnum
@ -543,16 +557,13 @@ where_filter:
/* Construct 'IF term THEN ACCEPT; REJECT;' */
struct filter *f = cfg_alloc(sizeof(struct filter));
struct f_inst *i, *acc, *rej;
acc = f_new_inst(); /* ACCEPT */
acc->code = P('p',',');
acc = f_new_inst(FI_PRINT_AND_DIE); /* ACCEPT */
acc->a1.p = NULL;
acc->a2.i = F_ACCEPT;
rej = f_new_inst(); /* REJECT */
rej->code = P('p',',');
rej = f_new_inst(FI_PRINT_AND_DIE); /* REJECT */
rej->a1.p = NULL;
rej->a2.i = F_REJECT;
i = f_new_inst(); /* IF */
i->code = '?';
i = f_new_inst(FI_CONDITION); /* IF */
i->a1.p = $2;
i->a2.p = acc;
i->next = rej;
@ -571,8 +582,7 @@ function_body:
decls '{' cmds '}' {
if ($1) {
/* Prepend instruction to clear local variables */
$$ = f_new_inst();
$$->code = P('c','v');
$$ = f_new_inst(FI_CLEAR_LOCAL_VARS);
$$->a1.p = $1;
$$->next = $3;
} else
@ -755,7 +765,7 @@ switch_body: /* EMPTY */ { $$ = NULL; }
}
;
/* CONST '(' expr ')' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $3; } */
/* CONST '(' expr ')' { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_INT; $$->a2.i = $3; } */
bgp_path_expr:
symbol { $$ = $1; }
@ -776,23 +786,23 @@ bgp_path_tail:
;
constant:
NUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $1; }
| TRUE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 1; }
| FALSE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 0; }
| TEXT { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_STRING; $$->a2.p = $1; }
| fipa { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
| VPN_RD { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_RD; val->val.ec = $1; $$->a1.p = val; }
| net_ { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_NET; val->val.net = $1; $$->a1.p = val; }
| '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); }
| '[' fprefix_set ']' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PREFIX_SET; $$->a2.p = $2; }
| ENUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; }
| bgp_path { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_PATH_MASK; val->val.path_mask = $1; $$->a1.p = val; }
NUM { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_INT; $$->a2.i = $1; }
| TRUE { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_BOOL; $$->a2.i = 1; }
| FALSE { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_BOOL; $$->a2.i = 0; }
| TEXT { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_STRING; $$->a2.p = $1; }
| fipa { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); $$->a1.p = val; *val = $1; }
| VPN_RD { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); val->type = T_RD; val->val.ec = $1; $$->a1.p = val; }
| net_ { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); val->type = T_NET; val->val.net = $1; $$->a1.p = val; }
| '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(FI_CONSTANT); $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); }
| '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_PREFIX_SET; $$->a2.p = $2; }
| ENUM { $$ = f_new_inst(FI_CONSTANT); $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; }
;
constructor:
'(' term ',' term ')' { $$ = f_generate_dpair($2, $4); }
| '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); }
| '(' term ',' term ',' term ')' { $$ = f_generate_lc($2, $4, $6); }
| bgp_path { $$ = f_generate_path_mask($1); }
;
@ -801,7 +811,7 @@ constructor:
* For such cases, we force the dynamic_attr list to contain
* at least an invalid token, so it is syntantically correct.
*/
CF_ADDTO(dynamic_attr, INVALID_TOKEN { $$ = NULL; })
CF_ADDTO(dynamic_attr, INVALID_TOKEN { $$ = (struct f_dynamic_attr) {}; })
rtadot: /* EMPTY, we are not permitted RTA. prefix */
;
@ -813,8 +823,7 @@ function_call:
if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really.");
DBG("You are calling function %s\n", $1->name);
$$ = f_new_inst();
$$->code = P('c','a');
$$ = f_new_inst(FI_CALL);
$$->a1.p = inst;
$$->a2.p = $1->def;
sym = $1->aux2;
@ -831,11 +840,9 @@ function_call:
symbol:
SYM {
$$ = f_new_inst();
switch ($1->class & 0xff00) {
case SYM_CONSTANT: $$->code = 'C'; break;
case SYM_VARIABLE: $$->code = 'V'; break;
case SYM_CONSTANT: $$ = f_new_inst(FI_CONSTANT_INDIRECT); break;
case SYM_VARIABLE: $$ = f_new_inst(FI_VARIABLE); break;
default: cf_error("%s: variable expected.", $1->name);
}
@ -844,57 +851,57 @@ symbol:
}
static_attr:
FROM { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = SA_FROM; $$->a1.i = 1; }
| GW { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = SA_GW; $$->a1.i = 1; }
| NET { $$ = f_new_inst(); $$->aux = T_NET; $$->a2.i = SA_NET; }
| PROTO { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_PROTO; }
| SOURCE { $$ = f_new_inst(); $$->aux = T_ENUM_RTS; $$->a2.i = SA_SOURCE; }
| SCOPE { $$ = f_new_inst(); $$->aux = T_ENUM_SCOPE; $$->a2.i = SA_SCOPE; $$->a1.i = 1; }
| DEST { $$ = f_new_inst(); $$->aux = T_ENUM_RTD; $$->a2.i = SA_DEST; $$->a1.i = 1; }
| IFNAME { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_IFNAME; }
| IFINDEX { $$ = f_new_inst(); $$->aux = T_INT; $$->a2.i = SA_IFINDEX; }
FROM { $$ = f_new_static_attr(T_IP, SA_FROM, 1); }
| GW { $$ = f_new_static_attr(T_IP, SA_GW, 1); }
| NET { $$ = f_new_static_attr(T_NET, SA_NET, 0); }
| PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 0); }
| SOURCE { $$ = f_new_static_attr(T_ENUM_RTS, SA_SOURCE, 0); }
| SCOPE { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE, 1); }
| DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 1); }
| IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
| IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 0); }
;
term:
'(' term ')' { $$ = $2; }
| term '+' term { $$ = f_new_inst(); $$->code = '+'; $$->a1.p = $1; $$->a2.p = $3; }
| term '-' term { $$ = f_new_inst(); $$->code = '-'; $$->a1.p = $1; $$->a2.p = $3; }
| term '*' term { $$ = f_new_inst(); $$->code = '*'; $$->a1.p = $1; $$->a2.p = $3; }
| term '/' term { $$ = f_new_inst(); $$->code = '/'; $$->a1.p = $1; $$->a2.p = $3; }
| term AND term { $$ = f_new_inst(); $$->code = '&'; $$->a1.p = $1; $$->a2.p = $3; }
| term OR term { $$ = f_new_inst(); $$->code = '|'; $$->a1.p = $1; $$->a2.p = $3; }
| term '=' term { $$ = f_new_inst(); $$->code = P('=','='); $$->a1.p = $1; $$->a2.p = $3; }
| term NEQ term { $$ = f_new_inst(); $$->code = P('!','='); $$->a1.p = $1; $$->a2.p = $3; }
| term '<' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $1; $$->a2.p = $3; }
| term LEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $1; $$->a2.p = $3; }
| term '>' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $3; $$->a2.p = $1; }
| term GEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $3; $$->a2.p = $1; }
| term '~' term { $$ = f_new_inst(); $$->code = '~'; $$->a1.p = $1; $$->a2.p = $3; }
| term NMA term { $$ = f_new_inst(); $$->code = P('!','~'); $$->a1.p = $1; $$->a2.p = $3; }
| '!' term { $$ = f_new_inst(); $$->code = '!'; $$->a1.p = $2; }
| DEFINED '(' term ')' { $$ = f_new_inst(); $$->code = P('d','e'); $$->a1.p = $3; }
'(' term ')' { $$ = $2; }
| term '+' term { $$ = f_new_inst(FI_ADD); $$->a1.p = $1; $$->a2.p = $3; }
| term '-' term { $$ = f_new_inst(FI_SUBTRACT); $$->a1.p = $1; $$->a2.p = $3; }
| term '*' term { $$ = f_new_inst(FI_MULTIPLY); $$->a1.p = $1; $$->a2.p = $3; }
| term '/' term { $$ = f_new_inst(FI_DIVIDE); $$->a1.p = $1; $$->a2.p = $3; }
| term AND term { $$ = f_new_inst(FI_AND); $$->a1.p = $1; $$->a2.p = $3; }
| term OR term { $$ = f_new_inst(FI_OR); $$->a1.p = $1; $$->a2.p = $3; }
| term '=' term { $$ = f_new_inst(FI_EQ); $$->a1.p = $1; $$->a2.p = $3; }
| term NEQ term { $$ = f_new_inst(FI_NEQ); $$->a1.p = $1; $$->a2.p = $3; }
| term '<' term { $$ = f_new_inst(FI_LT); $$->a1.p = $1; $$->a2.p = $3; }
| term LEQ term { $$ = f_new_inst(FI_LTE); $$->a1.p = $1; $$->a2.p = $3; }
| term '>' term { $$ = f_new_inst(FI_LT); $$->a1.p = $3; $$->a2.p = $1; }
| term GEQ term { $$ = f_new_inst(FI_LTE); $$->a1.p = $3; $$->a2.p = $1; }
| term '~' term { $$ = f_new_inst(FI_MATCH); $$->a1.p = $1; $$->a2.p = $3; }
| term NMA term { $$ = f_new_inst(FI_NOT_MATCH);$$->a1.p = $1; $$->a2.p = $3; }
| '!' term { $$ = f_new_inst(FI_NOT); $$->a1.p = $2; }
| DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED); $$->a1.p = $3; }
| symbol { $$ = $1; }
| constant { $$ = $1; }
| constructor { $$ = $1; }
| PREFERENCE { $$ = f_new_inst(); $$->code = 'P'; }
| PREFERENCE { $$ = f_new_inst(FI_PREF_GET); }
| rtadot static_attr { $$ = $2; $$->code = 'a'; }
| rtadot static_attr { $$ = f_new_inst_sa(FI_RTA_GET, $2); }
| rtadot dynamic_attr { $$ = $2; $$->code = P('e','a'); }
| rtadot dynamic_attr { $$ = f_new_inst_da(FI_EA_GET, $2); }
| term '.' IS_V4 { $$ = f_new_inst(); $$->code = P('I','i'); $$->a1.p = $1; }
| term '.' TYPE { $$ = f_new_inst(); $$->code = 'T'; $$->a1.p = $1; }
| term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p = $1; $$->aux = T_IP; }
| term '.' RD { $$ = f_new_inst(); $$->code = P('R','D'); $$->a1.p = $1; $$->aux = T_RD; }
| term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; }
| term '.' MAXLEN { $$ = f_new_inst(); $$->code = P('R','m'); $$->a1.p = $1; }
| term '.' ASN { $$ = f_new_inst(); $$->code = P('R','a'); $$->a1.p = $1; }
| term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; }
| term '.' FIRST { $$ = f_new_inst(); $$->code = P('a','f'); $$->a1.p = $1; }
| term '.' LAST { $$ = f_new_inst(); $$->code = P('a','l'); $$->a1.p = $1; }
| term '.' LAST_NONAGGREGATED { $$ = f_new_inst(); $$->code = P('a','L'); $$->a1.p = $1; }
| term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4); $$->a1.p = $1; }
| term '.' TYPE { $$ = f_new_inst(FI_TYPE); $$->a1.p = $1; }
| term '.' IP { $$ = f_new_inst(FI_IP); $$->a1.p = $1; $$->aux = T_IP; }
| term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER); $$->a1.p = $1; $$->aux = T_RD; }
| term '.' LEN { $$ = f_new_inst(FI_LENGTH); $$->a1.p = $1; }
| term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN); $$->a1.p = $1; }
| term '.' ASN { $$ = f_new_inst(FI_ROA_ASN); $$->a1.p = $1; }
| term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK); $$->a1.p = $1; $$->a2.p = $5; }
| term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST); $$->a1.p = $1; }
| term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST); $$->a1.p = $1; }
| term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG); $$->a1.p = $1; }
/* Communities */
/* This causes one shift/reduce conflict
@ -904,19 +911,19 @@ term:
| rtadot dynamic_attr '.' RESET{ }
*/
| '+' EMPTY '+' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_PATH; }
| '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; }
| '-' '-' EMPTY '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_ECLIST; }
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_LCLIST; }
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('A','p'); $$->a1.p = $3; $$->a2.p = $5; }
| ADD '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; }
| DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; }
| FILTER '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; }
| '+' EMPTY '+' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_PATH; }
| '-' EMPTY '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_CLIST; }
| '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_ECLIST; }
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_LCLIST; }
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND); $$->a1.p = $3; $$->a2.p = $5; }
| ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; }
| DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; }
| FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; }
| ROA_CHECK '(' rtable ')' { $$ = f_generate_roa_check($3, NULL, NULL); }
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); }
| FORMAT '(' term ')' { $$ = f_new_inst(); $$->code = P('f','m'); $$->a1.p = $3; }
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT); $$->a1.p = $3; }
/* | term '.' LEN { $$->code = P('P','l'); } */
@ -927,8 +934,7 @@ term:
if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really.");
DBG("You are calling function %s\n", $1->name);
$$ = f_new_inst();
$$->code = P('c','a');
$$ = f_new_inst(FI_CALL);
$$->a1.p = inst;
$$->a2.p = $1->def;
sym = $1->aux2;
@ -953,7 +959,7 @@ break_command:
;
print_one:
term { $$ = f_new_inst(); $$->code = 'p'; $$->a1.p = $1; $$->a2.p = NULL; }
term { $$ = f_new_inst(FI_PRINT); $$->a1.p = $1; $$->a2.p = NULL; }
;
print_list: /* EMPTY */ { $$ = NULL; }
@ -967,15 +973,13 @@ print_list: /* EMPTY */ { $$ = NULL; }
;
var_listn: term {
$$ = f_new_inst();
$$->code = 's';
$$ = f_new_inst(FI_SET);
$$->a1.p = NULL;
$$->a2.p = $1;
$$->next = NULL;
}
| term ',' var_listn {
$$ = f_new_inst();
$$->code = 's';
$$ = f_new_inst(FI_SET);
$$->a1.p = NULL;
$$->a2.p = $1;
$$->next = $3;
@ -988,73 +992,63 @@ var_list: /* EMPTY */ { $$ = NULL; }
cmd:
IF term THEN block {
$$ = f_new_inst();
$$->code = '?';
$$ = f_new_inst(FI_CONDITION);
$$->a1.p = $2;
$$->a2.p = $4;
}
| IF term THEN block ELSE block {
struct f_inst *i = f_new_inst();
i->code = '?';
struct f_inst *i = f_new_inst(FI_CONDITION);
i->a1.p = $2;
i->a2.p = $4;
$$ = f_new_inst();
$$->code = '?';
$$ = f_new_inst(FI_CONDITION);
$$->a1.p = i;
$$->a2.p = $6;
}
| SYM '=' term ';' {
$$ = f_new_inst();
DBG( "Ook, we'll set value\n" );
if (($1->class & ~T_MASK) != SYM_VARIABLE)
cf_error( "You may set only variables." );
$$->code = 's';
$$ = f_new_inst(FI_SET);
$$->a1.p = $1;
$$->a2.p = $3;
}
| RETURN term ';' {
$$ = f_new_inst();
DBG( "Ook, we'll return the value\n" );
$$->code = 'r';
$$ = f_new_inst(FI_RETURN);
$$->a1.p = $2;
}
| rtadot dynamic_attr '=' term ';' {
$$ = $2;
$$->code = P('e','S');
$$ = f_new_inst_da(FI_EA_SET, $2);
$$->a1.p = $4;
}
| rtadot static_attr '=' term ';' {
$$ = $2;
$$ = f_new_inst_sa(FI_RTA_SET, $2);
if (!$$->a1.i)
cf_error( "This static attribute is read-only.");
$$->code = P('a','S');
$$->a1.p = $4;
}
| PREFERENCE '=' term ';' {
$$ = f_new_inst();
$$->code = P('P','S');
$$ = f_new_inst(FI_PREF_SET);
$$->a1.p = $3;
}
| UNSET '(' rtadot dynamic_attr ')' ';' {
$$ = $4;
$$ = f_new_inst_da(FI_EA_SET, $4);
$$->aux = EAF_TYPE_UNDEF | EAF_TEMP;
$$->code = P('e','S');
$$->a1.p = NULL;
}
| break_command print_list ';' { $$ = f_new_inst(); $$->code = P('p',','); $$->a1.p = $2; $$->a2.i = $1; }
| break_command print_list ';' { $$ = f_new_inst(FI_PRINT_AND_DIE); $$->a1.p = $2; $$->a2.i = $1; }
| function_call ';' { $$ = $1; }
| CASE term '{' switch_body '}' {
$$ = f_new_inst();
$$->code = P('S','W');
$$ = f_new_inst(FI_SWITCH);
$$->a1.p = $2;
$$->a2.p = build_tree( $4 );
}
| rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); }
| rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( P('A','p'), 'x', $2, $6 ); }
| rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'a', $2, $6 ); }
| rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'd', $2, $6 ); }
| rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'f', $2, $6 ); }
| rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, 'x', $2, $6 ); }
| rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'a', $2, $6 ); }
| rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'd', $2, $6 ); }
| rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'f', $2, $6 ); }
| BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
;

View file

@ -2,6 +2,7 @@
* Filters: utility functions
*
* Copyright 1998 Pavel Machek <pavel@ucw.cz>
* 2017 Jan Maria Matejka <mq@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -13,43 +14,48 @@
#define P(a,b) ((a<<8) | b)
struct f_inst *
f_new_inst(void)
f_new_inst(enum f_instruction_code fi_code)
{
struct f_inst * ret;
ret = cfg_alloc(sizeof(struct f_inst));
ret->code = ret->aux = 0;
ret->arg1 = ret->arg2 = ret->next = NULL;
ret = cfg_allocz(sizeof(struct f_inst));
ret->fi_code = fi_code;
ret->lineno = ifs->lino;
return ret;
}
struct f_inst *
f_new_dynamic_attr(int type, int f_type, int code)
f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da)
{
/* FIXME: Remove the f_type parameter? */
struct f_inst *f = f_new_inst();
f->aux = (f_type << 8) | type;
f->a2.i = code;
return f;
struct f_inst *ret = f_new_inst(fi_code);
ret->aux = (da.f_type << 8) | da.type;
ret->a2.i = da.ea_code;
return ret;
}
struct f_inst *
f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa)
{
struct f_inst *ret = f_new_inst(fi_code);
ret->aux = sa.f_type;
ret->a2.i = sa.sa_code;
ret->a1.i = sa.readonly;
return ret;
}
/*
* Generate set_dynamic( operation( get_dynamic(), argument ) )
*/
struct f_inst *
f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct f_inst *argument)
f_generate_complex(int operation, int operation_aux, struct f_dynamic_attr da, struct f_inst *argument)
{
struct f_inst *set_dyn = f_new_inst(),
*oper = f_new_inst(),
*get_dyn = dyn;
struct f_inst *set_dyn = f_new_inst_da(FI_EA_SET, da),
*oper = f_new_inst(operation),
*get_dyn = f_new_inst_da(FI_EA_GET, da);
*set_dyn = *get_dyn;
get_dyn->code = P('e','a');
oper->code = operation;
oper->aux = operation_aux;
oper->a1.p = get_dyn;
oper->a2.p = argument;
set_dyn->code = P('e','S');
set_dyn->a1.p = oper;
return set_dyn;
}
@ -58,7 +64,7 @@ struct f_inst *
f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn)
{
struct f_inst_roa_check *ret = cfg_allocz(sizeof(struct f_inst_roa_check));
ret->i.code = P('R','C');
ret->i.fi_code = FI_ROA_CHECK;
ret->i.lineno = ifs->lino;
ret->i.arg1 = prefix;
ret->i.arg2 = asn;

View file

@ -48,8 +48,6 @@
#include "conf/conf.h"
#include "filter/filter.h"
#define P(a,b) ((a<<8) | b)
#define CMP_ERROR 999
void (*bt_assert_hook)(int result, struct f_inst *assert);
@ -99,8 +97,7 @@ pm_format(struct f_path_mask *p, buffer *buf)
break;
case PM_ASN_EXPR:
buffer_print(buf, "%u ", f_eval_asn((struct f_inst *) p->val));
break;
ASSERT(0);
}
p = p->next;
@ -462,7 +459,6 @@ val_in_range(struct f_val v1, struct f_val v2)
if (((v1.type == T_PAIR) || (v1.type == T_QUAD)) && (v2.type == T_CLIST))
return int_set_contains(v2.val.ad, v1.val.i);
/* IP->Quad implicit conversion */
if (val_is_ip4(v1) && (v2.type == T_CLIST))
return int_set_contains(v2.val.ad, ipa_to_u32(v1.val.ip));
@ -632,22 +628,16 @@ static struct f_val
interpret(struct f_inst *what)
{
struct symbol *sym;
struct f_val v1, v2, res, *vp;
struct f_val v1, v2, res = { .type = T_VOID }, *vp;
unsigned u1, u2;
int i;
u32 as;
for ( ; what; what = what->next) {
res.type = T_VOID;
if (!what)
return res;
switch(what->code) {
case ',':
TWOARGS;
break;
switch(what->fi_code) {
/* Binary operators */
case '+':
case FI_ADD:
TWOARGS_C;
switch (res.type = v1.type) {
case T_VOID: runtime( "Can't operate with values of type void" );
@ -655,7 +645,7 @@ interpret(struct f_inst *what)
default: runtime( "Usage of unknown type" );
}
break;
case '-':
case FI_SUBTRACT:
TWOARGS_C;
switch (res.type = v1.type) {
case T_VOID: runtime( "Can't operate with values of type void" );
@ -663,7 +653,7 @@ interpret(struct f_inst *what)
default: runtime( "Usage of unknown type" );
}
break;
case '*':
case FI_MULTIPLY:
TWOARGS_C;
switch (res.type = v1.type) {
case T_VOID: runtime( "Can't operate with values of type void" );
@ -671,7 +661,7 @@ interpret(struct f_inst *what)
default: runtime( "Usage of unknown type" );
}
break;
case '/':
case FI_DIVIDE:
TWOARGS_C;
switch (res.type = v1.type) {
case T_VOID: runtime( "Can't operate with values of type void" );
@ -681,12 +671,12 @@ interpret(struct f_inst *what)
}
break;
case '&':
case '|':
case FI_AND:
case FI_OR:
ARG(v1, a1.p);
if (v1.type != T_BOOL)
runtime( "Can't do boolean operation on non-booleans" );
if (v1.val.i == (what->code == '|')) {
if (v1.val.i == (what->fi_code == FI_OR)) {
res.type = T_BOOL;
res.val.i = v1.val.i;
break;
@ -699,7 +689,7 @@ interpret(struct f_inst *what)
res.val.i = v2.val.i;
break;
case P('m','p'):
case FI_PAIR_CONSTRUCT:
TWOARGS;
if ((v1.type != T_INT) || (v2.type != T_INT))
runtime( "Can't operate with value of non-integer type in pair constructor" );
@ -711,7 +701,7 @@ interpret(struct f_inst *what)
res.type = T_PAIR;
break;
case P('m','c'):
case FI_EC_CONSTRUCT:
{
TWOARGS;
@ -757,7 +747,7 @@ interpret(struct f_inst *what)
break;
}
case P('m','l'):
case FI_LC_CONSTRUCT:
{
TWOARGS;
@ -775,6 +765,32 @@ interpret(struct f_inst *what)
break;
}
case FI_PATHMASK_CONSTRUCT:
{
struct f_path_mask *tt = what->a1.p, *vbegin, **vv = &vbegin;
while (tt) {
*vv = lp_alloc(f_pool, sizeof(struct f_path_mask));
if (tt->kind == PM_ASN_EXPR) {
struct f_val res = interpret((struct f_inst *) tt->val);
(*vv)->kind = PM_ASN;
if (res.type != T_INT) {
runtime( "Error resolving path mask template: value not an integer" );
return (struct f_val) { .type = T_VOID };
}
(*vv)->val = res.val.i;
} else {
**vv = *tt;
}
tt = tt->next;
vv = &((*vv)->next);
}
res = (struct f_val) { .type = T_PATH_MASK, .val.path_mask = vbegin };
break;
}
/* Relational operators */
#define COMPARE(x) \
@ -793,12 +809,12 @@ interpret(struct f_inst *what)
res.val.i = (x); \
break;
case P('!','='): SAME(!i);
case P('=','='): SAME(i);
case '<': COMPARE(i==-1);
case P('<','='): COMPARE(i!=1);
case FI_NEQ: SAME(!i);
case FI_EQ: SAME(i);
case FI_LT: COMPARE(i==-1);
case FI_LTE: COMPARE(i!=1);
case '!':
case FI_NOT:
ONEARG;
if (v1.type != T_BOOL)
runtime( "Not applied to non-boolean" );
@ -806,7 +822,7 @@ interpret(struct f_inst *what)
res.val.i = !res.val.i;
break;
case '~':
case FI_MATCH:
TWOARGS;
res.type = T_BOOL;
res.val.i = val_in_range(v1, v2);
@ -815,7 +831,7 @@ interpret(struct f_inst *what)
res.val.i = !!res.val.i;
break;
case P('!','~'):
case FI_NOT_MATCH:
TWOARGS;
res.type = T_BOOL;
res.val.i = val_in_range(v1, v2);
@ -824,12 +840,12 @@ interpret(struct f_inst *what)
res.val.i = !res.val.i;
break;
case P('d','e'):
case FI_DEFINED:
ONEARG;
res.type = T_BOOL;
res.val.i = (v1.type != T_VOID) && !undef_value(v1);
break;
case 'T':
case FI_TYPE:
ONEARG;
switch (v1.type)
{
@ -841,7 +857,7 @@ interpret(struct f_inst *what)
runtime( "Can't determine type of this item" );
}
break;
case P('I','i'):
case FI_IS_V4:
ONEARG;
if (v1.type != T_IP)
runtime( "IP version check needs an IP address" );
@ -850,7 +866,7 @@ interpret(struct f_inst *what)
break;
/* Set to indirect value, a1 = variable, a2 = value */
case 's':
case FI_SET:
ARG(v2, a2.p);
sym = what->a1.p;
vp = sym->def;
@ -869,7 +885,7 @@ interpret(struct f_inst *what)
break;
/* some constants have value in a2, some in *a1.p, strange. */
case 'c': /* integer (or simple type) constant, string, set, or prefix_set */
case FI_CONSTANT: /* integer (or simple type) constant, string, set, or prefix_set */
res.type = what->aux;
if (res.type == T_PREFIX_SET)
@ -881,15 +897,15 @@ interpret(struct f_inst *what)
else
res.val.i = what->a2.i;
break;
case 'V':
case 'C':
case FI_VARIABLE:
case FI_CONSTANT_INDIRECT:
res = * ((struct f_val *) what->a1.p);
break;
case 'p':
case FI_PRINT:
ONEARG;
val_format(v1, &f_buf);
break;
case '?': /* ? has really strange error value, so we can implement if ... else nicely :-) */
case FI_CONDITION: /* ? has really strange error value, so we can implement if ... else nicely :-) */
ONEARG;
if (v1.type != T_BOOL)
runtime( "If requires boolean expression" );
@ -899,10 +915,10 @@ interpret(struct f_inst *what)
} else res.val.i = 1;
res.type = T_BOOL;
break;
case '0':
case FI_NOP:
debug( "No operation\n" );
break;
case P('p',','):
case FI_PRINT_AND_DIE:
ONEARG;
if ((what->a2.i == F_NOP || (what->a2.i != F_NONL && what->a1.p)) &&
!(f_flags & FF_SILENT))
@ -925,7 +941,7 @@ interpret(struct f_inst *what)
bug( "unknown return type: Can't happen");
}
break;
case 'a': /* rta access */
case FI_RTA_GET: /* rta access */
{
ACCESS_RTE;
struct rta *rta = (*f_rte)->attrs;
@ -948,7 +964,7 @@ interpret(struct f_inst *what)
}
}
break;
case P('a','S'):
case FI_RTA_SET:
ACCESS_RTE;
ONEARG;
if (what->aux != v1.type)
@ -1000,7 +1016,7 @@ interpret(struct f_inst *what)
}
}
break;
case P('e','a'): /* Access to extended attributes */
case FI_EA_GET: /* Access to extended attributes */
ACCESS_RTE;
{
eattr *e = NULL;
@ -1094,7 +1110,7 @@ interpret(struct f_inst *what)
}
}
break;
case P('e','S'):
case FI_EA_SET:
ACCESS_RTE;
ONEARG;
{
@ -1198,12 +1214,12 @@ interpret(struct f_inst *what)
}
}
break;
case 'P':
case FI_PREF_GET:
ACCESS_RTE;
res.type = T_INT;
res.val.i = (*f_rte)->pref;
break;
case P('P','S'):
case FI_PREF_SET:
ACCESS_RTE;
ONEARG;
if (v1.type != T_INT)
@ -1213,7 +1229,7 @@ interpret(struct f_inst *what)
f_rte_cow();
(*f_rte)->pref = v1.val.i;
break;
case 'L': /* Get length of */
case FI_LENGTH: /* Get length of */
ONEARG;
res.type = T_INT;
switch(v1.type) {
@ -1225,7 +1241,7 @@ interpret(struct f_inst *what)
default: runtime( "Prefix, path, clist or eclist expected" );
}
break;
case P('R','m'): /* Get ROA max prefix length */
case FI_ROA_MAXLEN: /* Get ROA max prefix length */
ONEARG;
if (v1.type != T_NET || !net_is_roa(v1.val.net))
runtime( "ROA expected" );
@ -1235,7 +1251,7 @@ interpret(struct f_inst *what)
((net_addr_roa4 *) v1.val.net)->max_pxlen :
((net_addr_roa6 *) v1.val.net)->max_pxlen;
break;
case P('R','a'): /* Get ROA ASN */
case FI_ROA_ASN: /* Get ROA ASN */
ONEARG;
if (v1.type != T_NET || !net_is_roa(v1.val.net))
runtime( "ROA expected" );
@ -1245,14 +1261,14 @@ interpret(struct f_inst *what)
((net_addr_roa4 *) v1.val.net)->asn :
((net_addr_roa6 *) v1.val.net)->asn;
break;
case P('c','p'): /* Convert prefix to ... */
case FI_IP: /* Convert prefix to ... */
ONEARG;
if (v1.type != T_NET)
runtime( "Prefix expected" );
res.type = T_IP;
res.val.ip = net_prefix(v1.val.net);
break;
case P('R','D'):
case FI_ROUTE_DISTINGUISHER:
ONEARG;
if (v1.type != T_NET)
runtime( "Prefix expected" );
@ -1261,7 +1277,7 @@ interpret(struct f_inst *what)
res.type = T_RD;
res.val.ec = net_rd(v1.val.net);
break;
case P('a','f'): /* Get first ASN from AS PATH */
case FI_AS_PATH_FIRST: /* Get first ASN from AS PATH */
ONEARG;
if (v1.type != T_PATH)
runtime( "AS path expected" );
@ -1271,7 +1287,7 @@ interpret(struct f_inst *what)
res.type = T_INT;
res.val.i = as;
break;
case P('a','l'): /* Get last ASN from AS PATH */
case FI_AS_PATH_LAST: /* Get last ASN from AS PATH */
ONEARG;
if (v1.type != T_PATH)
runtime( "AS path expected" );
@ -1281,7 +1297,7 @@ interpret(struct f_inst *what)
res.type = T_INT;
res.val.i = as;
break;
case P('a','L'): /* Get last ASN from non-aggregated part of AS PATH */
case FI_AS_PATH_LAST_NAG: /* Get last ASN from non-aggregated part of AS PATH */
ONEARG;
if (v1.type != T_PATH)
runtime( "AS path expected" );
@ -1289,23 +1305,23 @@ interpret(struct f_inst *what)
res.type = T_INT;
res.val.i = as_path_get_last_nonaggregated(v1.val.ad);
break;
case 'r':
case FI_RETURN:
ONEARG;
res = v1;
res.type |= T_RETURN;
return res;
case P('c','a'): /* CALL: this is special: if T_RETURN and returning some value, mask it out */
case FI_CALL: /* CALL: this is special: if T_RETURN and returning some value, mask it out */
ONEARG;
res = interpret(what->a2.p);
if (res.type == T_RETURN)
return res;
res.type &= ~T_RETURN;
break;
case P('c','v'): /* Clear local variables */
case FI_CLEAR_LOCAL_VARS: /* Clear local variables */
for (sym = what->a1.p; sym != NULL; sym = sym->aux2)
((struct f_val *) sym->def)->type = T_VOID;
break;
case P('S','W'):
case FI_SWITCH:
ONEARG;
{
struct f_tree *t = find_tree(what->a2.p, v1);
@ -1324,7 +1340,7 @@ interpret(struct f_inst *what)
return res;
}
break;
case P('i','M'): /* IP.MASK(val) */
case FI_IP_MASK: /* IP.MASK(val) */
TWOARGS;
if (v2.type != T_INT)
runtime( "Integer expected");
@ -1337,11 +1353,11 @@ interpret(struct f_inst *what)
ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i)));
break;
case 'E': /* Create empty attribute */
case FI_EMPTY: /* Create empty attribute */
res.type = what->aux;
res.val.ad = adata_empty(f_pool, 0);
break;
case P('A','p'): /* Path prepend */
case FI_PATH_PREPEND: /* Path prepend */
TWOARGS;
if (v1.type != T_PATH)
runtime("Can't prepend to non-path");
@ -1352,7 +1368,7 @@ interpret(struct f_inst *what)
res.val.ad = as_path_prepend(f_pool, v1.val.ad, v2.val.i);
break;
case P('C','a'): /* (Extended) Community list add or delete */
case FI_CLIST_ADD_DEL: /* (Extended) Community list add or delete */
TWOARGS;
if (v1.type == T_PATH)
{
@ -1518,8 +1534,7 @@ interpret(struct f_inst *what)
break;
case P('R','C'): /* ROA Check */
case FI_ROA_CHECK: /* ROA Check */
if (what->arg1)
{
TWOARGS;
@ -1559,14 +1574,14 @@ interpret(struct f_inst *what)
break;
case P('f','m'): /* Format */
case FI_FORMAT: /* Format */
ONEARG;
res.type = T_STRING;
res.val.s = val_format_str(v1);
break;
case P('a','s'): /* Birdtest Assert */
case FI_ASSERT: /* Birdtest Assert */
ONEARG;
if (v1.type != T_BOOL)
@ -1579,10 +1594,8 @@ interpret(struct f_inst *what)
break;
default:
bug( "Unknown instruction %d (%c)", what->code, what->code & 0xff);
}
if (what->next)
return interpret(what->next);
bug( "Unknown instruction %d (%c)", what->fi_code, what->fi_code & 0xff);
}}
return res;
}
@ -1609,40 +1622,40 @@ i_same(struct f_inst *f1, struct f_inst *f2)
return 1;
if (f1->aux != f2->aux)
return 0;
if (f1->code != f2->code)
if (f1->fi_code != f2->fi_code)
return 0;
if (f1 == f2) /* It looks strange, but it is possible with call rewriting trickery */
return 1;
switch(f1->code) {
case ',': /* fall through */
case '+':
case '-':
case '*':
case '/':
case '|':
case '&':
case P('m','p'):
case P('m','c'):
case P('!','='):
case P('=','='):
case '<':
case P('<','='): TWOARGS; break;
switch(f1->fi_code) {
case FI_ADD: /* fall through */
case FI_SUBTRACT:
case FI_MULTIPLY:
case FI_DIVIDE:
case FI_OR:
case FI_AND:
case FI_PAIR_CONSTRUCT:
case FI_EC_CONSTRUCT:
case FI_NEQ:
case FI_EQ:
case FI_LT:
case FI_LTE: TWOARGS; break;
case '!': ONEARG; break;
case P('!', '~'):
case '~': TWOARGS; break;
case P('d','e'): ONEARG; break;
case 'T': ONEARG; break;
case P('n','T'): break;
case FI_PATHMASK_CONSTRUCT: if (!pm_same(f1->a1.p, f2->a1.p)) return 0; break;
case P('m','l'):
case FI_NOT: ONEARG; break;
case FI_NOT_MATCH:
case FI_MATCH: TWOARGS; break;
case FI_DEFINED: ONEARG; break;
case FI_TYPE: ONEARG; break;
case FI_LC_CONSTRUCT:
TWOARGS;
if (!i_same(INST3(f1).p, INST3(f2).p))
return 0;
break;
case 's':
case FI_SET:
ARG(v2, a2.p);
{
struct symbol *s1, *s2;
@ -1655,7 +1668,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
}
break;
case 'c':
case FI_CONSTANT:
switch (f1->aux) {
case T_PREFIX_SET:
@ -1678,44 +1691,44 @@ i_same(struct f_inst *f1, struct f_inst *f2)
}
break;
case 'C':
case FI_CONSTANT_INDIRECT:
if (!val_same(* (struct f_val *) f1->a1.p, * (struct f_val *) f2->a1.p))
return 0;
break;
case 'V':
case FI_VARIABLE:
if (strcmp((char *) f1->a2.p, (char *) f2->a2.p))
return 0;
break;
case 'p': case 'L': ONEARG; break;
case '?': TWOARGS; break;
case '0': case 'E': break;
case P('p',','): ONEARG; A2_SAME; break;
case 'P':
case 'a': A2_SAME; break;
case P('e','a'): A2_SAME; break;
case P('P','S'):
case P('a','S'):
case P('e','S'): ONEARG; A2_SAME; break;
case FI_PRINT: case FI_LENGTH: ONEARG; break;
case FI_CONDITION: TWOARGS; break;
case FI_NOP: case FI_EMPTY: break;
case FI_PRINT_AND_DIE: ONEARG; A2_SAME; break;
case FI_PREF_GET:
case FI_RTA_GET: A2_SAME; break;
case FI_EA_GET: A2_SAME; break;
case FI_PREF_SET:
case FI_RTA_SET:
case FI_EA_SET: ONEARG; A2_SAME; break;
case 'r': ONEARG; break;
case P('c','p'): ONEARG; break;
case P('R','D'): ONEARG; break;
case P('c','a'): /* Call rewriting trickery to avoid exponential behaviour */
case FI_RETURN: ONEARG; break;
case FI_IP: ONEARG; break;
case FI_ROUTE_DISTINGUISHER: ONEARG; break;
case FI_CALL: /* Call rewriting trickery to avoid exponential behaviour */
ONEARG;
if (!i_same(f1->a2.p, f2->a2.p))
return 0;
f2->a2.p = f1->a2.p;
break;
case P('c','v'): break; /* internal instruction */
case P('S','W'): ONEARG; if (!same_tree(f1->a2.p, f2->a2.p)) return 0; break;
case P('i','M'): TWOARGS; break;
case P('A','p'): TWOARGS; break;
case P('C','a'): TWOARGS; break;
case P('a','f'):
case P('a','l'):
case P('a','L'): ONEARG; break;
case P('R','C'):
case FI_CLEAR_LOCAL_VARS: break; /* internal instruction */
case FI_SWITCH: ONEARG; if (!same_tree(f1->a2.p, f2->a2.p)) return 0; break;
case FI_IP_MASK: TWOARGS; break;
case FI_PATH_PREPEND: TWOARGS; break;
case FI_CLIST_ADD_DEL: TWOARGS; break;
case FI_AS_PATH_FIRST:
case FI_AS_PATH_LAST:
case FI_AS_PATH_LAST_NAG: ONEARG; break;
case FI_ROA_CHECK:
TWOARGS;
/* Does not really make sense - ROA check results may change anyway */
if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name,
@ -1723,7 +1736,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
return 0;
break;
default:
bug( "Unknown instruction %d in same (%c)", f1->code, f1->code & 0xff);
bug( "Unknown instruction %d in same (%c)", f1->fi_code, f1->fi_code & 0xff);
}
return i_same(f1->next, f2->next);
}
@ -1853,14 +1866,6 @@ f_eval_int(struct f_inst *expr)
return res.val.i;
}
u32
f_eval_asn(struct f_inst *expr)
{
/* Called as a part of another interpret call, therefore no log_reset() */
struct f_val res = interpret(expr);
return (res.type == T_INT) ? res.val.i : 0;
}
/**
* filter_same - compare two filters
* @new: first filter to be compared

View file

@ -14,9 +14,74 @@
#include "nest/route.h"
#include "nest/attrs.h"
/* Filter instruction types */
#define FI__TWOCHAR(a,b) ((a<<8) | b)
#define FI__LIST \
F(FI_ADD, 0, '+') \
F(FI_SUBTRACT, 0, '-') \
F(FI_MULTIPLY, 0, '*') \
F(FI_DIVIDE, 0, '/') \
F(FI_AND, 0, '&') \
F(FI_OR, 0, '|') \
F(FI_PAIR_CONSTRUCT, 'm', 'p') \
F(FI_EC_CONSTRUCT, 'm', 'c') \
F(FI_LC_CONSTRUCT, 'm', 'l') \
F(FI_PATHMASK_CONSTRUCT, 'm', 'P') \
F(FI_NEQ, '!', '=') \
F(FI_EQ, '=', '=') \
F(FI_LT, 0, '<') \
F(FI_LTE, '<', '=') \
F(FI_NOT, 0, '!') \
F(FI_MATCH, 0, '~') \
F(FI_NOT_MATCH, '!', '~') \
F(FI_DEFINED, 'd', 'e') \
F(FI_TYPE, 0, 'T') \
F(FI_IS_V4, 'I', 'i') \
F(FI_SET, 0, 's') \
F(FI_CONSTANT, 0, 'c') \
F(FI_VARIABLE, 0, 'V') \
F(FI_CONSTANT_INDIRECT, 0, 'C') \
F(FI_PRINT, 0, 'p') \
F(FI_CONDITION, 0, '?') \
F(FI_NOP, 0, '0') \
F(FI_PRINT_AND_DIE, 'p', ',') \
F(FI_RTA_GET, 0, 'a') \
F(FI_RTA_SET, 'a', 'S') \
F(FI_EA_GET, 'e', 'a') \
F(FI_EA_SET, 'e', 'S') \
F(FI_PREF_GET, 0, 'P') \
F(FI_PREF_SET, 'P', 'S') \
F(FI_LENGTH, 0, 'L') \
F(FI_ROA_MAXLEN, 'R', 'M') \
F(FI_ROA_ASN, 'R', 'A') \
F(FI_IP, 'c', 'p') \
F(FI_ROUTE_DISTINGUISHER, 'R', 'D') \
F(FI_AS_PATH_FIRST, 'a', 'f') \
F(FI_AS_PATH_LAST, 'a', 'l') \
F(FI_AS_PATH_LAST_NAG, 'a', 'L') \
F(FI_RETURN, 0, 'r') \
F(FI_CALL, 'c', 'a') \
F(FI_CLEAR_LOCAL_VARS, 'c', 'V') \
F(FI_SWITCH, 'S', 'W') \
F(FI_IP_MASK, 'i', 'M') \
F(FI_EMPTY, 0, 'E') \
F(FI_PATH_PREPEND, 'A', 'p') \
F(FI_CLIST_ADD_DEL, 'C', 'a') \
F(FI_ROA_CHECK, 'R', 'C') \
F(FI_FORMAT, 0, 'F') \
F(FI_ASSERT, 'a', 's')
enum f_instruction_code {
#define F(c,a,b) \
c = FI__TWOCHAR(a,b),
FI__LIST
#undef F
} PACKED;
struct f_inst { /* Instruction */
struct f_inst *next; /* Structure is 16 bytes, anyway */
u16 code; /* Instruction code, see the interpret() function and P() macro */
enum f_instruction_code fi_code;
u16 aux; /* Extension to instruction code, T_*, EA_*, EAF_* */
union {
uint i;
@ -70,15 +135,32 @@ struct f_val {
} val;
};
struct f_dynamic_attr {
int type;
int f_type;
int ea_code;
};
struct f_static_attr {
int f_type;
int sa_code;
int readonly;
};
struct filter {
char *name;
struct f_inst *root;
};
struct f_inst *f_new_inst(void);
struct f_inst *f_new_dynamic_attr(int type, int f_type, int code); /* Type as core knows it, type as filters know it, and code of dynamic attribute */
struct f_inst *f_new_inst(enum f_instruction_code fi_code);
struct f_inst *f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da);
struct f_inst *f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa);
static inline struct f_dynamic_attr f_new_dynamic_attr(int type, int f_type, int code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */
{ return (struct f_dynamic_attr) { .type = type, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */
static inline struct f_static_attr f_new_static_attr(int f_type, int code, int readonly)
{ return (struct f_static_attr) { .f_type = f_type, .sa_code = code, .readonly = readonly }; }
struct f_tree *f_new_tree(void);
struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct f_inst *argument);
struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_dynamic_attr da, struct f_inst *argument);
struct f_inst *f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn);
@ -100,7 +182,6 @@ int f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, s
struct f_val f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool);
struct f_val f_eval(struct f_inst *expr, struct linpool *tmp_pool);
uint f_eval_int(struct f_inst *expr);
u32 f_eval_asn(struct f_inst *expr);
char *filter_name(struct filter *filter);
int filter_same(struct filter *new, struct filter *old);

View file

@ -55,29 +55,28 @@ protocol static {
rip_metric = rip_metric + 5;
print rip_metric;
#
# TODO: uncomment this part after finishing BGP integration version
#
# bgp_community = -empty-;
# print "hi";
# bgp_community = add(bgp_community, (1,2));
# print "hello";
# bgp_community = add(bgp_community, (2,3));
# bgp_community.add((4,5));
# print "community = ", bgp_community;
# bgp_community.delete((2,3));
# print "community = ", bgp_community;
# bgp_community.empty;
# print "community = ", bgp_community;
# print "done";
bgp_community = -empty-;
print "hi";
bgp_community = add(bgp_community, (1,2));
print "hello";
bgp_community = add(bgp_community, (2,3));
bgp_community.add((4,5));
print "community = ", bgp_community;
bgp_community.delete((2,3));
print "community = ", bgp_community;
bgp_community.empty;
print "community = ", bgp_community;
print "done";
accept;
};
};
route 0.0.0.0/0 via 195.113.31.113;
route 62.168.0.0/25 reject;
route 1.2.3.4/32 via 195.113.31.124;
route 10.0.0.0/8 reject;
route 10.1.1.0:255.255.255.0 via 62.168.0.3;
route 10.1.2.0:255.255.255.0 via 62.168.0.3;
route 10.1.3.0:255.255.255.0 via 62.168.0.4;
route 10.1.1.0/24 via 62.168.0.3;
route 10.1.2.0/24 via 62.168.0.3;
route 10.1.3.0/24 via 62.168.0.4;
route 10.2.0.0/24 via "arc0";
}

View file

@ -1,6 +1,6 @@
Summary: BIRD Internet Routing Daemon
Name: bird
Version: 2.0.1
Version: 2.0.2
Release: 1
Copyright: GPL
Group: Networking/Daemons

View file

@ -805,8 +805,7 @@ as_path_match(const struct adata *path, struct f_path_mask *mask)
val2 = val = mask->val;
goto step;
case PM_ASN_EXPR:
val2 = val = f_eval_asn((struct f_inst *) mask->val);
goto step;
ASSERT(0);
case PM_ASN_RANGE:
val = mask->val;
val2 = mask->val2;

View file

@ -31,6 +31,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
struct rt_dev_proto *p = (void *) P;
struct rt_dev_config *cf = (void *) P->cf;
struct channel *c;
net_addr *net = &ad->prefix;
if (!EMPTY_LIST(cf->iface_list) &&
!iface_patt_find(&cf->iface_list, ad->iface, ad))
@ -53,13 +54,20 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
if (!c)
return;
/* For IPv6 SADR, replace regular prefix with SADR prefix */
if (c->net_type == NET_IP6_SADR)
{
net = alloca(sizeof(net_addr_ip6_sadr));
net_fill_ip6_sadr(net, net6_prefix(&ad->prefix), net6_pxlen(&ad->prefix), IP6_NONE, 0);
}
if (flags & IF_CHANGE_DOWN)
{
DBG("dev_if_notify: %s:%I going down\n", ad->iface->name, ad->ip);
/* Use iface ID as local source ID */
struct rte_src *src = rt_get_source(P, ad->iface->index);
rte_update2(c, &ad->prefix, NULL, src);
rte_update2(c, net, NULL, src);
}
else if (flags & IF_CHANGE_UP)
{
@ -85,7 +93,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
a = rta_lookup(&a0);
e = rte_get_temp(a);
e->pflags = 0;
rte_update2(c, &ad->prefix, e, src);
rte_update2(c, net, e, src);
}
}
@ -107,16 +115,32 @@ dev_if_notify(struct proto *p, uint c, struct iface *iface)
}
}
static void
dev_postconfig(struct proto_config *CF)
{
struct rt_dev_config *cf = (void *) CF;
struct channel_config *ip4, *ip6, *ip6_sadr;
ip4 = proto_cf_find_channel(CF, NET_IP4);
ip6 = proto_cf_find_channel(CF, NET_IP6);
ip6_sadr = proto_cf_find_channel(CF, NET_IP6_SADR);
if (ip6 && ip6_sadr)
cf_error("Both ipv6 and ipv6-sadr channels");
cf->ip4_channel = ip4;
cf->ip6_channel = ip6 ?: ip6_sadr;
}
static struct proto *
dev_init(struct proto_config *CF)
{
struct proto *P = proto_new(CF);
struct rt_dev_proto *p = (void *) P;
// struct rt_dev_config *cf = (void *) CF;
struct rt_dev_config *cf = (void *) CF;
proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4));
proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6));
proto_configure_channel(P, &p->ip4_channel, cf->ip4_channel);
proto_configure_channel(P, &p->ip6_channel, cf->ip6_channel);
P->if_notify = dev_if_notify;
P->ifa_notify = dev_ifa_notify;
@ -136,8 +160,8 @@ dev_reconfigure(struct proto *P, struct proto_config *CF)
return 0;
return
proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)) &&
proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6));
proto_configure_channel(P, &p->ip4_channel, n->ip4_channel) &&
proto_configure_channel(P, &p->ip6_channel, n->ip6_channel);
return 1;
}
@ -162,9 +186,10 @@ struct protocol proto_device = {
.name = "Direct",
.template = "direct%d",
.preference = DEF_PREF_DIRECT,
.channel_mask = NB_IP,
.channel_mask = NB_IP | NB_IP6_SADR,
.proto_size = sizeof(struct rt_dev_proto),
.config_size = sizeof(struct rt_dev_config),
.postconfig = dev_postconfig,
.init = dev_init,
.reconfigure = dev_reconfigure,
.copy_config = dev_copy_config

View file

@ -13,6 +13,9 @@ struct rt_dev_config {
struct proto_config c;
list iface_list; /* list of struct iface_patt */
int check_link;
struct channel_config *ip4_channel;
struct channel_config *ip6_channel;
};
struct rt_dev_proto {

View file

@ -2076,6 +2076,13 @@ rt_unlock_table(rtable *r)
}
}
static struct rtable_config *
rt_find_table_config(struct config *cf, char *name)
{
struct symbol *sym = cf_find_symbol(cf, name);
return (sym && (sym->class == SYM_TABLE)) ? sym->def : NULL;
}
/**
* rt_commit - commit new routing table configuration
* @new: new configuration
@ -2101,11 +2108,10 @@ rt_commit(struct config *new, struct config *old)
rtable *ot = o->table;
if (!ot->deleted)
{
struct symbol *sym = cf_find_symbol(new, o->name);
if (sym && sym->class == SYM_TABLE && !new->shutdown)
r = rt_find_table_config(new, o->name);
if (r && (r->addr_type == o->addr_type) && !new->shutdown)
{
DBG("\t%s: same\n", o->name);
r = sym->def;
r->table = ot;
ot->name = r->name;
ot->config = r;

View file

@ -22,9 +22,10 @@ CF_DEFINES
CF_DECLS
CF_KEYWORDS(BABEL, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT, WIRED,
WIRELESS, RX, TX, BUFFER, LENGTH, CHECK, LINK, BABEL_METRIC, NEXT, HOP,
IPV4, IPV6)
CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK,
NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
ENTRIES)
CF_GRAMMAR

View file

@ -13,7 +13,7 @@
#ifdef GIT_LABEL
#define BIRD_VERSION XSTR1(GIT_LABEL)
#else
#define BIRD_VERSION "2.0.1"
#define BIRD_VERSION "2.0.2"
#endif
/* Include parameters determined by configure script */