mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-11-09 12:48:43 +00:00
Merge branch 'int-new' of ssh://gitlab.labs.nic.cz/labs/bird into int-new
This commit is contained in:
commit
966602602a
12
NEWS
12
NEWS
@ -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)
|
Version 2.0.1 (2018-01-16)
|
||||||
o Linux MPLS kernel support
|
o Linux MPLS kernel support
|
||||||
o Better handling of channels inherited from templates
|
o Better handling of channels inherited from templates
|
||||||
|
@ -49,6 +49,8 @@ CF_DECLS
|
|||||||
struct rtable_config *r;
|
struct rtable_config *r;
|
||||||
struct channel_config *cc;
|
struct channel_config *cc;
|
||||||
struct f_inst *x;
|
struct f_inst *x;
|
||||||
|
struct f_dynamic_attr fda;
|
||||||
|
struct f_static_attr fsa;
|
||||||
struct filter *f;
|
struct filter *f;
|
||||||
struct f_tree *e;
|
struct f_tree *e;
|
||||||
struct f_trie *trie;
|
struct f_trie *trie;
|
||||||
@ -177,10 +179,6 @@ pxlen4:
|
|||||||
if ($2 > IP4_MAX_PREFIX_LENGTH) cf_error("Invalid prefix length %u", $2);
|
if ($2 > IP4_MAX_PREFIX_LENGTH) cf_error("Invalid prefix length %u", $2);
|
||||||
$$ = $2;
|
$$ = $2;
|
||||||
}
|
}
|
||||||
| ':' IP4 {
|
|
||||||
$$ = ip4_masklen($2);
|
|
||||||
if ($$ == 255) cf_error("Invalid netmask %I4", $2);
|
|
||||||
}
|
|
||||||
;
|
;
|
||||||
|
|
||||||
net_ip4_: IP4 pxlen4
|
net_ip4_: IP4 pxlen4
|
||||||
|
@ -1,222 +1,204 @@
|
|||||||
/*
|
# This is a basic configuration file, which contains boilerplate options and
|
||||||
* This is an example configuration file
|
# some basic examples. It allows the BIRD daemon to start but will not cause
|
||||||
* (for version 1.x.x, obsolete)
|
# anything else to happen.
|
||||||
*/
|
#
|
||||||
|
# Please refer to the BIRD User's Guide documentation, which is also available
|
||||||
# Yes, even shell-like comments work...
|
# online at http://bird.network.cz/ in HTML format, for more information on
|
||||||
|
# configuring BIRD and adding routing protocols.
|
||||||
|
|
||||||
# Configure logging
|
# Configure logging
|
||||||
#log syslog { debug, trace, info, remote, warning, error, auth, fatal, bug };
|
log syslog all;
|
||||||
#log stderr all;
|
# log "/var/log/bird.log" { debug, trace, info, remote, warning, error, auth, fatal, bug };
|
||||||
#log "tmp" all;
|
|
||||||
|
|
||||||
# Override router ID
|
# Set router ID. It is a unique identification of your router, usually one of
|
||||||
#router id 198.51.100.1;
|
# IPv4 addresses of the router. It is recommended to configure it explicitly.
|
||||||
|
# router id 198.51.100.1;
|
||||||
|
|
||||||
# You can define your own symbols...
|
# Turn on global debugging of all protocols (all messages or just selected classes)
|
||||||
#define xyzzy = (120+10);
|
# debug protocols all;
|
||||||
#define '1a-a1' = (30+40);
|
# debug protocols { events, states };
|
||||||
|
|
||||||
# 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 internal watchdog
|
# Turn on internal watchdog
|
||||||
#watchdog warning 5 s;
|
# watchdog warning 5 s;
|
||||||
#watchdog timeout 30 s;
|
# watchdog timeout 30 s;
|
||||||
|
|
||||||
# The direct protocol automatically generates device routes to
|
# You can define your own constants
|
||||||
# all network interfaces. Can exist in as many instances as you wish
|
# define my_asn = 65000;
|
||||||
# if you want to populate multiple routing tables with device routes.
|
# define my_addr = 198.51.100.1;
|
||||||
#protocol direct {
|
|
||||||
# interface "-eth*", "*"; # Restrict network interfaces it works with
|
|
||||||
#}
|
|
||||||
|
|
||||||
# This pseudo-protocol performs synchronization between BIRD's routing
|
# Tables master4 and master6 are defined by default
|
||||||
# tables and the kernel. If your kernel supports multiple routing tables
|
# ipv4 table master4;
|
||||||
# (as Linux 2.2.x does), you can run multiple instances of the kernel
|
# ipv6 table master6;
|
||||||
# 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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 {
|
protocol device {
|
||||||
scan time 10; # Scan interfaces every 10 seconds
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Static routes (again, there can be multiple instances, so that you
|
# The direct protocol is not a real routing protocol. It automatically generates
|
||||||
# can disable/enable various groups of static routes on the fly).
|
# 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 {
|
protocol static {
|
||||||
# disabled; # Disable by default
|
ipv4; # Again, IPv4 channel with default options
|
||||||
# table testable; # Connect to a non-default table
|
|
||||||
# preference 1000; # Default preference of routes
|
# route 0.0.0.0/0 via 198.51.100.10;
|
||||||
# debug { states, routes, filters, interfaces, events, packets };
|
# route 192.0.2.0/24 blackhole;
|
||||||
# debug all;
|
|
||||||
# route 0.0.0.0/0 via 198.51.100.13;
|
|
||||||
# route 198.51.100.0/25 unreachable;
|
|
||||||
# route 10.0.0.0/8 unreachable;
|
# route 10.0.0.0/8 unreachable;
|
||||||
# route 10.1.1.0:255.255.255.0 via 198.51.100.3;
|
# route 10.2.0.0/24 via "eth0";
|
||||||
# route 10.1.2.0:255.255.255.0 via 198.51.100.3;
|
# # Static routes can be defined with optional attributes
|
||||||
# route 10.1.3.0:255.255.255.0 via 198.51.100.4;
|
# route 10.1.1.0/24 via 198.51.100.3 { rip_metric = 3; };
|
||||||
# route 10.2.0.0/24 via "arc0";
|
# 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.
|
# Pipe protocol connects two routing tables. Beware of loops.
|
||||||
#protocol pipe {
|
# protocol pipe {
|
||||||
# peer table testable;
|
# table master4; # No ipv4/ipv6 channel definition like in other protocols
|
||||||
# Define what routes do we export to this protocol / import from it.
|
# peer table mrib4;
|
||||||
# import all; # default is all
|
# import all; # Direction peer table -> table
|
||||||
# export all; # default is none
|
# export all; # Direction table -> peer table
|
||||||
# import none; # If you wish to disable imports
|
# }
|
||||||
# import filter test_filter; # Use named filter
|
|
||||||
# import where source = RTS_DEVICE; # Use explicit filter
|
|
||||||
#}
|
|
||||||
|
|
||||||
# RIP aka Rest In Pieces...
|
# RIP example, both RIP and RIPng are supported
|
||||||
#protocol rip MyRIP { # You can also use an explicit name
|
# protocol rip {
|
||||||
# preference xyzzy;
|
# ipv4 {
|
||||||
# debug all;
|
# # Export direct, static routes and ones from RIP itself
|
||||||
# port 1520;
|
# import all;
|
||||||
# period 7;
|
# export where source ~ [ RTS_DEVICE, RTS_STATIC, RTS_RIP ];
|
||||||
# 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";
|
|
||||||
# };
|
# };
|
||||||
# authentication none;
|
# interface "eth*" {
|
||||||
# import filter { print "importing"; accept; };
|
# update time 10; # Default period is 30
|
||||||
# export filter { print "exporting"; accept; };
|
# timeout time 60; # Default timeout is 180
|
||||||
#}
|
# authentication cryptographic; # No authentication by default
|
||||||
|
# password "hello" { algorithm hmac sha256; }; # Default is MD5
|
||||||
|
# };
|
||||||
|
# }
|
||||||
|
|
||||||
#protocol ospf MyOSPF {
|
# OSPF example, both OSPFv2 and OSPFv3 are supported
|
||||||
# tick 2;
|
# protocol ospf v3 {
|
||||||
# rfc1583compat yes;
|
# ipv6 {
|
||||||
# area 0.0.0.0 {
|
# import all;
|
||||||
# stub no;
|
# export where source = RTS_STATIC;
|
||||||
|
# };
|
||||||
|
# area 0 {
|
||||||
# interface "eth*" {
|
# interface "eth*" {
|
||||||
# hello 9;
|
# type broadcast; # Detected by default
|
||||||
# retransmit 6;
|
# cost 10; # Interface metric
|
||||||
# cost 10;
|
# hello 5; # Default hello perid 10 is too long
|
||||||
# transmit delay 5;
|
|
||||||
# dead count 5;
|
|
||||||
# wait 50;
|
|
||||||
# type broadcast;
|
|
||||||
# authentication simple;
|
|
||||||
# password "pass";
|
|
||||||
# };
|
# };
|
||||||
# interface "arc0" {
|
# interface "tun*" {
|
||||||
# rx buffer large;
|
# type ptp; # PtP mode, avoids DR selection
|
||||||
# type nonbroadcast;
|
# cost 100; # Interface metric
|
||||||
# poll 14;
|
# hello 5; # Default hello perid 10 is too long
|
||||||
# dead 75;
|
|
||||||
# neighbors {
|
|
||||||
# 10.1.1.2 eligible;
|
|
||||||
# 10.1.1.4;
|
|
||||||
# };
|
|
||||||
# strict nonbroadcast yes;
|
|
||||||
# };
|
# };
|
||||||
# interface "xxx0" {
|
# interface "dummy0" {
|
||||||
# passwords {
|
# stub; # Stub interface, just propagate it
|
||||||
# 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;
|
|
||||||
# };
|
# };
|
||||||
# };
|
# };
|
||||||
# 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 {
|
# BGP example, explicit name 'uplink1' is used instead of default 'bgp1'
|
||||||
# disabled;
|
# protocol bgp uplink1 {
|
||||||
# description "My BGP uplink";
|
# description "My BGP uplink";
|
||||||
# local as 65000;
|
# local 198.51.100.1 as 65000;
|
||||||
# neighbor 198.51.100.130 as 64496;
|
# neighbor 198.51.100.10 as 64496;
|
||||||
# multihop;
|
# hold time 90; # Default is 240
|
||||||
# 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
|
|
||||||
# password "secret"; # Password used for MD5 authentication
|
# 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
|
# ipv4 { # regular IPv4 unicast (1/1)
|
||||||
# export where source=RTS_STATIC;
|
# import filter rt_import;
|
||||||
# export filter {
|
# export where source ~ [ RTS_STATIC, RTS_BGP ];
|
||||||
# if source = RTS_STATIC then {
|
# };
|
||||||
# bgp_community = -empty-; bgp_community = add(bgp_community,(65000,5678));
|
#
|
||||||
# bgp_origin = 0;
|
# ipv6 { # regular IPv6 unicast (2/1)
|
||||||
# bgp_community = -empty-; bgp_community.add((65000,5678));
|
# import filter rt_import;
|
||||||
# if (65000,64501) ~ bgp_community then
|
# export filter { # The same as 'where' expression above
|
||||||
# bgp_community.add((0, 1));
|
# if source ~ [ RTS_STATIC, RTS_BGP ]
|
||||||
# if bgp_path ~ [= 65000 =] then
|
# then accept;
|
||||||
# bgp_path.prepend(65000);
|
# else reject;
|
||||||
# accept;
|
# };
|
||||||
# }
|
# };
|
||||||
# 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 example. Using templates to define IBGP route reflector clients.
|
||||||
#template bgp rr_client {
|
# template bgp rr_clients {
|
||||||
# disabled;
|
# local 10.0.0.1 as 65000;
|
||||||
# local as 65000;
|
# neighbor as 65000;
|
||||||
# multihop;
|
|
||||||
# rr client;
|
# rr client;
|
||||||
# rr cluster id 1.0.0.1;
|
# rr cluster id 1.0.0.1;
|
||||||
#}
|
|
||||||
#
|
#
|
||||||
#protocol bgp rr_abcd from rr_client {
|
# ipv4 {
|
||||||
# neighbor 10.1.4.7 as 65000;
|
# 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;
|
||||||
|
# }
|
||||||
|
@ -293,6 +293,20 @@ routes are:
|
|||||||
<item>Route next hops (see below)
|
<item>Route next hops (see below)
|
||||||
</itemize>
|
</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
|
<sect1>VPN IPv4 and IPv6 routes
|
||||||
<label id="vpn-routes">
|
<label id="vpn-routes">
|
||||||
|
|
||||||
@ -646,9 +660,9 @@ agreement").
|
|||||||
restricted to interfaces assigned to the VRF and will use sockets bound
|
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
|
to the VRF. Appropriate VRF interface must exist on OS level. For kernel
|
||||||
protocol, an appropriate table still must be explicitly selected by
|
protocol, an appropriate table still must be explicitly selected by
|
||||||
<cf/table/ option. Note that the VRF support in BIRD and Linux kernel
|
<cf/table/ option. Note that for proper VRF support it is necessary to
|
||||||
(4.11) is still in development and is currently problematic outside of
|
use Linux kernel version at least 4.14, older versions have limited VRF
|
||||||
multihop BGP.
|
implementation.
|
||||||
|
|
||||||
<tag><label id="proto-channel"><m/channel name/ [{<m/channel config/}]</tag>
|
<tag><label id="proto-channel"><m/channel name/ [{<m/channel config/}]</tag>
|
||||||
Every channel must be explicitly stated. See the protocol-specific
|
Every channel must be explicitly stated. See the protocol-specific
|
||||||
@ -1234,12 +1248,17 @@ foot).
|
|||||||
operator <cf/.type/. The type may be:
|
operator <cf/.type/. The type may be:
|
||||||
|
|
||||||
<cf/NET_IP4/ and <cf/NET_IP6/ prefixes hold an IP prefix. The literals
|
<cf/NET_IP4/ and <cf/NET_IP6/ prefixes hold an IP prefix. The literals
|
||||||
are written as <cf><m/ipaddress//<m/pxlen/</cf>,
|
are written as <cf><m/ipaddress//<m/pxlen/</cf>. There are two special
|
||||||
or <cf><m>ipaddress</m>/<m>netmask</m></cf>. There are two special
|
|
||||||
operators on these: <cf/.ip/ which extracts the IP address from the
|
operators on these: <cf/.ip/ which extracts the IP address from the
|
||||||
pair, and <cf/.len/, which separates prefix length from the pair.
|
pair, and <cf/.len/, which separates prefix length from the pair.
|
||||||
So <cf>1.2.0.0/16.len = 16</cf> is true.
|
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
|
<cf/NET_VPN4/ and <cf/NET_VPN6/ prefixes hold an IP prefix with VPN
|
||||||
Route Distinguisher (<rfc id="4364">). They support the same special
|
Route Distinguisher (<rfc id="4364">). They support the same special
|
||||||
operators as IP prefixes, and also <cf/.rd/ which extracts the Route
|
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.
|
lclists, with LCs instead of pairs as arguments.
|
||||||
</descrip>
|
</descrip>
|
||||||
|
|
||||||
|
|
||||||
<sect>Operators
|
<sect>Operators
|
||||||
<label id="operators">
|
<label id="operators">
|
||||||
|
|
||||||
@ -1652,19 +1672,25 @@ cases desirable.
|
|||||||
routes over the same IPv6 transport. For sending and receiving Babel packets,
|
routes over the same IPv6 transport. For sending and receiving Babel packets,
|
||||||
only a link-local IPv6 address is needed.
|
only a link-local IPv6 address is needed.
|
||||||
|
|
||||||
<p>BIRD does not implement any Babel extensions, but will coexist with
|
<p>BIRD implements an extension for IPv6 source-specific routing (SSR or SADR),
|
||||||
implementations using extensions (and will just ignore extension messages).
|
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
|
<sect1>Configuration
|
||||||
<label id="babel-config">
|
<label id="babel-config">
|
||||||
|
|
||||||
<p>Babel supports no global configuration options apart from those common to all
|
<p>The Babel protocol support both IPv4 and IPv6 channels; both can be
|
||||||
other protocols, but supports the following per-interface configuration options:
|
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>
|
<code>
|
||||||
protocol babel [<name>] {
|
protocol babel [<name>] {
|
||||||
ipv4 { <channel config> };
|
ipv4 { <channel config> };
|
||||||
ipv6 { <channel config> };
|
ipv6 [sadr] { <channel config> };
|
||||||
interface <interface pattern> {
|
interface <interface pattern> {
|
||||||
type <wired|wireless>;
|
type <wired|wireless>;
|
||||||
rxcost <number>;
|
rxcost <number>;
|
||||||
@ -1684,8 +1710,8 @@ protocol babel [<name>] {
|
|||||||
</code>
|
</code>
|
||||||
|
|
||||||
<descrip>
|
<descrip>
|
||||||
<tag><label id="babel-channel">ipv4|ipv6 <m/channel config/</tag>
|
<tag><label id="babel-channel">ipv4 | ipv6 [sadr] <m/channel config/</tag>
|
||||||
The supported channels are IPv4 and IPv6.
|
The supported channels are IPv4, IPv6, and IPv6 SADR.
|
||||||
|
|
||||||
<tag><label id="babel-type">type wired|wireless </tag>
|
<tag><label id="babel-type">type wired|wireless </tag>
|
||||||
This option specifies the interface type: Wired or wireless. On wired
|
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:
|
using the following configuration parameters:
|
||||||
|
|
||||||
<descrip>
|
<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,
|
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,
|
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
|
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
|
to get consistent behavior.) Optional <cf/ip/ argument specifies a source
|
||||||
address, equivalent to the <cf/source address/ option (see below). This
|
address, equivalent to the <cf/source address/ option (see below).
|
||||||
parameter is mandatory.
|
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>
|
<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
|
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
|
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
|
automatically switch to iBGP. Optionally, the remote port may also be
|
||||||
specified. The parameter may be used multiple times with different
|
specified. Like <cf/local/ parameter, this parameter may also be used
|
||||||
sub-options (e.g., both <cf/neighbor 10.0.0.1 as 65000;/ and
|
multiple times with different sub-options. This parameter is mandatory.
|
||||||
<cf/neighbor 10.0.0.1; neighbor as 65000;/ are valid). This parameter is
|
|
||||||
mandatory.
|
|
||||||
|
|
||||||
<tag><label id="bgp-iface">interface <m/string/</tag>
|
<tag><label id="bgp-iface">interface <m/string/</tag>
|
||||||
Define interface we should use for link-local BGP IPv6 sessions.
|
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
|
<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
|
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
|
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
|
<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
|
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
|
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
|
the kernel protocol. OSPF protocol creates device routes for its interfaces
|
||||||
itself and BGP protocol is usually used for exporting aggregate routes. Although
|
itself and BGP protocol is usually used for exporting aggregate routes. But the
|
||||||
there are some use cases that use the direct protocol (like abusing eBGP as an
|
Direct protocol is necessary for distance-vector protocols like RIP or Babel to
|
||||||
IGP routing protocol), in most cases it is not needed to have these device
|
announce local networks.
|
||||||
routes in BIRD routing table and to use the direct protocol.
|
|
||||||
|
|
||||||
<p>There is one notable case when you definitely want to use the direct protocol
|
<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
|
-- 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
|
(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.
|
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
|
<p>The Kernel protocol supports both IPv4 and IPv6 channels; only one channel
|
||||||
can be configured in each protocol instance.
|
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
|
<sect1>Configuration
|
||||||
<label id="krt-config">
|
<label id="krt-config">
|
||||||
@ -4280,6 +4311,7 @@ protocol rip {
|
|||||||
|
|
||||||
|
|
||||||
<sect>RPKI
|
<sect>RPKI
|
||||||
|
<label id="rpki">
|
||||||
|
|
||||||
<sect1>Introduction
|
<sect1>Introduction
|
||||||
|
|
||||||
|
266
filter/config.Y
266
filter/config.Y
@ -12,8 +12,6 @@ CF_HDR
|
|||||||
|
|
||||||
CF_DEFINES
|
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(u32 a, u32 b) { return (a << 16) | b; }
|
||||||
static inline u32 pair_a(u32 p) { return p >> 16; }
|
static inline u32 pair_a(u32 p) { return p >> 16; }
|
||||||
static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
|
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 *
|
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();
|
struct f_inst *e = f_new_inst(FI_EMPTY);
|
||||||
e->code = 'E';
|
|
||||||
|
|
||||||
switch (dyn->aux & EAF_TYPE_MASK) {
|
switch (dyn.type & EAF_TYPE_MASK) {
|
||||||
case EAF_TYPE_AS_PATH:
|
case EAF_TYPE_AS_PATH:
|
||||||
e->aux = T_PATH;
|
e->aux = T_PATH;
|
||||||
break;
|
break;
|
||||||
@ -179,9 +176,9 @@ f_generate_empty(struct f_inst *dyn)
|
|||||||
cf_error("Can't empty that attribute");
|
cf_error("Can't empty that attribute");
|
||||||
}
|
}
|
||||||
|
|
||||||
dyn->code = P('e','S');
|
struct f_inst *s = f_new_inst_da(FI_EA_SET, dyn);
|
||||||
dyn->a1.p = e;
|
s->a1.p = e;
|
||||||
return dyn;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -190,21 +187,19 @@ f_generate_dpair(struct f_inst *t1, struct f_inst *t2)
|
|||||||
{
|
{
|
||||||
struct f_inst *rv;
|
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))
|
if ((t1->aux != T_INT) || (t2->aux != T_INT))
|
||||||
cf_error( "Can't operate with value of non-integer type in pair constructor");
|
cf_error( "Can't operate with value of non-integer type in pair constructor");
|
||||||
|
|
||||||
check_u16(t1->a2.i);
|
check_u16(t1->a2.i);
|
||||||
check_u16(t2->a2.i);
|
check_u16(t2->a2.i);
|
||||||
|
|
||||||
rv = f_new_inst();
|
rv = f_new_inst(FI_CONSTANT);
|
||||||
rv->code = 'c';
|
|
||||||
rv->aux = T_PAIR;
|
rv->aux = T_PAIR;
|
||||||
rv->a2.i = pair(t1->a2.i, t2->a2.i);
|
rv->a2.i = pair(t1->a2.i, t2->a2.i);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rv = f_new_inst();
|
rv = f_new_inst(FI_PAIR_CONSTRUCT);
|
||||||
rv->code = P('m', 'p');
|
|
||||||
rv->a1.p = t1;
|
rv->a1.p = t1;
|
||||||
rv->a2.p = t2;
|
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;
|
int c1 = 0, c2 = 0, ipv4_used = 0;
|
||||||
u32 key = 0, val2 = 0;
|
u32 key = 0, val2 = 0;
|
||||||
|
|
||||||
if (tk->code == 'c') {
|
if (tk->fi_code == FI_CONSTANT) {
|
||||||
c1 = 1;
|
c1 = 1;
|
||||||
|
|
||||||
if (tk->aux == T_INT) {
|
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 */
|
/* IP->Quad implicit conversion */
|
||||||
else if (tk->code == 'C') {
|
else if (tk->fi_code == FI_CONSTANT_INDIRECT) {
|
||||||
c1 = 1;
|
c1 = 1;
|
||||||
struct f_val *val = tk->a1.p;
|
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");
|
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)
|
if (tv->aux != T_INT)
|
||||||
cf_error("Can't operate with value of non-integer type in EC constructor");
|
cf_error("Can't operate with value of non-integer type in EC constructor");
|
||||||
c2 = 1;
|
c2 = 1;
|
||||||
@ -276,15 +271,13 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
NEW_F_VAL;
|
NEW_F_VAL;
|
||||||
rv = f_new_inst();
|
rv = f_new_inst(FI_CONSTANT_INDIRECT);
|
||||||
rv->code = 'C';
|
|
||||||
rv->a1.p = val;
|
rv->a1.p = val;
|
||||||
val->type = T_EC;
|
val->type = T_EC;
|
||||||
val->val.ec = ec;
|
val->val.ec = ec;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rv = f_new_inst();
|
rv = f_new_inst(FI_EC_CONSTRUCT);
|
||||||
rv->code = P('m','c');
|
|
||||||
rv->aux = kind;
|
rv->aux = kind;
|
||||||
rv->a1.p = tk;
|
rv->a1.p = tk;
|
||||||
rv->a2.p = tv;
|
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;
|
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))
|
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");
|
cf_error( "LC - Can't operate with value of non-integer type in tuple constructor");
|
||||||
|
|
||||||
rv = f_new_inst();
|
rv = f_new_inst(FI_CONSTANT_INDIRECT);
|
||||||
rv->code = 'C';
|
|
||||||
|
|
||||||
NEW_F_VAL;
|
NEW_F_VAL;
|
||||||
rv->a1.p = 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 = cfg_allocz(sizeof(struct f_inst3));
|
||||||
rv->lineno = ifs->lino;
|
rv->lineno = ifs->lino;
|
||||||
rv->code = P('m','l');
|
rv->fi_code = FI_LC_CONSTRUCT;
|
||||||
rv->a1.p = t1;
|
rv->a1.p = t1;
|
||||||
rv->a2.p = t2;
|
rv->a2.p = t2;
|
||||||
INST3(rv).p = t3;
|
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;
|
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
|
* Remove all new lines and doubled whitespaces
|
||||||
* and convert all tabulators to spaces
|
* 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)
|
assert_done(struct f_inst *expr, const char *start, const char *end)
|
||||||
{
|
{
|
||||||
struct f_inst *i;
|
struct f_inst *i;
|
||||||
i = f_new_inst();
|
i = f_new_inst(FI_ASSERT);
|
||||||
i->code = P('a','s');
|
|
||||||
i->a1.p = expr;
|
i->a1.p = expr;
|
||||||
|
|
||||||
if (end >= start)
|
if (end >= start)
|
||||||
@ -412,7 +424,9 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
|||||||
%nonassoc THEN
|
%nonassoc THEN
|
||||||
%nonassoc ELSE
|
%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 <f> filter filter_body where_filter
|
||||||
%type <i> type break_command ec_kind
|
%type <i> type break_command ec_kind
|
||||||
%type <i32> cnum
|
%type <i32> cnum
|
||||||
@ -543,16 +557,13 @@ where_filter:
|
|||||||
/* Construct 'IF term THEN ACCEPT; REJECT;' */
|
/* Construct 'IF term THEN ACCEPT; REJECT;' */
|
||||||
struct filter *f = cfg_alloc(sizeof(struct filter));
|
struct filter *f = cfg_alloc(sizeof(struct filter));
|
||||||
struct f_inst *i, *acc, *rej;
|
struct f_inst *i, *acc, *rej;
|
||||||
acc = f_new_inst(); /* ACCEPT */
|
acc = f_new_inst(FI_PRINT_AND_DIE); /* ACCEPT */
|
||||||
acc->code = P('p',',');
|
|
||||||
acc->a1.p = NULL;
|
acc->a1.p = NULL;
|
||||||
acc->a2.i = F_ACCEPT;
|
acc->a2.i = F_ACCEPT;
|
||||||
rej = f_new_inst(); /* REJECT */
|
rej = f_new_inst(FI_PRINT_AND_DIE); /* REJECT */
|
||||||
rej->code = P('p',',');
|
|
||||||
rej->a1.p = NULL;
|
rej->a1.p = NULL;
|
||||||
rej->a2.i = F_REJECT;
|
rej->a2.i = F_REJECT;
|
||||||
i = f_new_inst(); /* IF */
|
i = f_new_inst(FI_CONDITION); /* IF */
|
||||||
i->code = '?';
|
|
||||||
i->a1.p = $2;
|
i->a1.p = $2;
|
||||||
i->a2.p = acc;
|
i->a2.p = acc;
|
||||||
i->next = rej;
|
i->next = rej;
|
||||||
@ -571,8 +582,7 @@ function_body:
|
|||||||
decls '{' cmds '}' {
|
decls '{' cmds '}' {
|
||||||
if ($1) {
|
if ($1) {
|
||||||
/* Prepend instruction to clear local variables */
|
/* Prepend instruction to clear local variables */
|
||||||
$$ = f_new_inst();
|
$$ = f_new_inst(FI_CLEAR_LOCAL_VARS);
|
||||||
$$->code = P('c','v');
|
|
||||||
$$->a1.p = $1;
|
$$->a1.p = $1;
|
||||||
$$->next = $3;
|
$$->next = $3;
|
||||||
} else
|
} 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:
|
bgp_path_expr:
|
||||||
symbol { $$ = $1; }
|
symbol { $$ = $1; }
|
||||||
@ -776,23 +786,23 @@ bgp_path_tail:
|
|||||||
;
|
;
|
||||||
|
|
||||||
constant:
|
constant:
|
||||||
NUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $1; }
|
NUM { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_INT; $$->a2.i = $1; }
|
||||||
| TRUE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 1; }
|
| TRUE { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_BOOL; $$->a2.i = 1; }
|
||||||
| FALSE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 0; }
|
| FALSE { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_BOOL; $$->a2.i = 0; }
|
||||||
| TEXT { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_STRING; $$->a2.p = $1; }
|
| TEXT { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_STRING; $$->a2.p = $1; }
|
||||||
| fipa { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
|
| fipa { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); $$->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; }
|
| 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(); $$->code = 'C'; val->type = T_NET; val->val.net = $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(); $$->code = 'c'; $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); }
|
| '[' 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(); $$->code = 'c'; $$->aux = T_PREFIX_SET; $$->a2.p = $2; }
|
| '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_PREFIX_SET; $$->a2.p = $2; }
|
||||||
| ENUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; }
|
| ENUM { $$ = f_new_inst(FI_CONSTANT); $$->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; }
|
|
||||||
;
|
;
|
||||||
|
|
||||||
constructor:
|
constructor:
|
||||||
'(' term ',' term ')' { $$ = f_generate_dpair($2, $4); }
|
'(' term ',' term ')' { $$ = f_generate_dpair($2, $4); }
|
||||||
| '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); }
|
| '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); }
|
||||||
| '(' term ',' term ',' term ')' { $$ = f_generate_lc($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
|
* For such cases, we force the dynamic_attr list to contain
|
||||||
* at least an invalid token, so it is syntantically correct.
|
* 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 */
|
rtadot: /* EMPTY, we are not permitted RTA. prefix */
|
||||||
;
|
;
|
||||||
@ -813,8 +823,7 @@ function_call:
|
|||||||
if ($1->class != SYM_FUNCTION)
|
if ($1->class != SYM_FUNCTION)
|
||||||
cf_error("You can't call something which is not a function. Really.");
|
cf_error("You can't call something which is not a function. Really.");
|
||||||
DBG("You are calling function %s\n", $1->name);
|
DBG("You are calling function %s\n", $1->name);
|
||||||
$$ = f_new_inst();
|
$$ = f_new_inst(FI_CALL);
|
||||||
$$->code = P('c','a');
|
|
||||||
$$->a1.p = inst;
|
$$->a1.p = inst;
|
||||||
$$->a2.p = $1->def;
|
$$->a2.p = $1->def;
|
||||||
sym = $1->aux2;
|
sym = $1->aux2;
|
||||||
@ -831,11 +840,9 @@ function_call:
|
|||||||
|
|
||||||
symbol:
|
symbol:
|
||||||
SYM {
|
SYM {
|
||||||
$$ = f_new_inst();
|
|
||||||
|
|
||||||
switch ($1->class & 0xff00) {
|
switch ($1->class & 0xff00) {
|
||||||
case SYM_CONSTANT: $$->code = 'C'; break;
|
case SYM_CONSTANT: $$ = f_new_inst(FI_CONSTANT_INDIRECT); break;
|
||||||
case SYM_VARIABLE: $$->code = 'V'; break;
|
case SYM_VARIABLE: $$ = f_new_inst(FI_VARIABLE); break;
|
||||||
default: cf_error("%s: variable expected.", $1->name);
|
default: cf_error("%s: variable expected.", $1->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -844,57 +851,57 @@ symbol:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static_attr:
|
static_attr:
|
||||||
FROM { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = SA_FROM; $$->a1.i = 1; }
|
FROM { $$ = f_new_static_attr(T_IP, SA_FROM, 1); }
|
||||||
| GW { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = SA_GW; $$->a1.i = 1; }
|
| GW { $$ = f_new_static_attr(T_IP, SA_GW, 1); }
|
||||||
| NET { $$ = f_new_inst(); $$->aux = T_NET; $$->a2.i = SA_NET; }
|
| NET { $$ = f_new_static_attr(T_NET, SA_NET, 0); }
|
||||||
| PROTO { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_PROTO; }
|
| PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 0); }
|
||||||
| SOURCE { $$ = f_new_inst(); $$->aux = T_ENUM_RTS; $$->a2.i = SA_SOURCE; }
|
| SOURCE { $$ = f_new_static_attr(T_ENUM_RTS, SA_SOURCE, 0); }
|
||||||
| SCOPE { $$ = f_new_inst(); $$->aux = T_ENUM_SCOPE; $$->a2.i = SA_SCOPE; $$->a1.i = 1; }
|
| SCOPE { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE, 1); }
|
||||||
| DEST { $$ = f_new_inst(); $$->aux = T_ENUM_RTD; $$->a2.i = SA_DEST; $$->a1.i = 1; }
|
| DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 1); }
|
||||||
| IFNAME { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_IFNAME; }
|
| IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
|
||||||
| IFINDEX { $$ = f_new_inst(); $$->aux = T_INT; $$->a2.i = SA_IFINDEX; }
|
| IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 0); }
|
||||||
;
|
;
|
||||||
|
|
||||||
term:
|
term:
|
||||||
'(' term ')' { $$ = $2; }
|
'(' term ')' { $$ = $2; }
|
||||||
| term '+' term { $$ = f_new_inst(); $$->code = '+'; $$->a1.p = $1; $$->a2.p = $3; }
|
| term '+' term { $$ = f_new_inst(FI_ADD); $$->a1.p = $1; $$->a2.p = $3; }
|
||||||
| term '-' term { $$ = f_new_inst(); $$->code = '-'; $$->a1.p = $1; $$->a2.p = $3; }
|
| term '-' term { $$ = f_new_inst(FI_SUBTRACT); $$->a1.p = $1; $$->a2.p = $3; }
|
||||||
| term '*' term { $$ = f_new_inst(); $$->code = '*'; $$->a1.p = $1; $$->a2.p = $3; }
|
| term '*' term { $$ = f_new_inst(FI_MULTIPLY); $$->a1.p = $1; $$->a2.p = $3; }
|
||||||
| term '/' term { $$ = f_new_inst(); $$->code = '/'; $$->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(); $$->code = '&'; $$->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(); $$->code = '|'; $$->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(); $$->code = P('=','='); $$->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(); $$->code = P('!','='); $$->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(); $$->code = '<'; $$->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(); $$->code = P('<','='); $$->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(); $$->code = '<'; $$->a1.p = $3; $$->a2.p = $1; }
|
| term '>' term { $$ = f_new_inst(FI_LT); $$->a1.p = $3; $$->a2.p = $1; }
|
||||||
| term GEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->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(); $$->code = '~'; $$->a1.p = $1; $$->a2.p = $3; }
|
| term '~' term { $$ = f_new_inst(FI_MATCH); $$->a1.p = $1; $$->a2.p = $3; }
|
||||||
| term NMA term { $$ = f_new_inst(); $$->code = P('!','~'); $$->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(); $$->code = '!'; $$->a1.p = $2; }
|
| '!' term { $$ = f_new_inst(FI_NOT); $$->a1.p = $2; }
|
||||||
| DEFINED '(' term ')' { $$ = f_new_inst(); $$->code = P('d','e'); $$->a1.p = $3; }
|
| DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED); $$->a1.p = $3; }
|
||||||
|
|
||||||
| symbol { $$ = $1; }
|
| symbol { $$ = $1; }
|
||||||
| constant { $$ = $1; }
|
| constant { $$ = $1; }
|
||||||
| constructor { $$ = $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 '.' IS_V4 { $$ = f_new_inst(FI_IS_V4); $$->a1.p = $1; }
|
||||||
| term '.' TYPE { $$ = f_new_inst(); $$->code = 'T'; $$->a1.p = $1; }
|
| term '.' TYPE { $$ = f_new_inst(FI_TYPE); $$->a1.p = $1; }
|
||||||
| term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p = $1; $$->aux = T_IP; }
|
| term '.' IP { $$ = f_new_inst(FI_IP); $$->a1.p = $1; $$->aux = T_IP; }
|
||||||
| term '.' RD { $$ = f_new_inst(); $$->code = P('R','D'); $$->a1.p = $1; $$->aux = T_RD; }
|
| term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER); $$->a1.p = $1; $$->aux = T_RD; }
|
||||||
| term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; }
|
| term '.' LEN { $$ = f_new_inst(FI_LENGTH); $$->a1.p = $1; }
|
||||||
| term '.' MAXLEN { $$ = f_new_inst(); $$->code = P('R','m'); $$->a1.p = $1; }
|
| term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN); $$->a1.p = $1; }
|
||||||
| term '.' ASN { $$ = f_new_inst(); $$->code = P('R','a'); $$->a1.p = $1; }
|
| term '.' ASN { $$ = f_new_inst(FI_ROA_ASN); $$->a1.p = $1; }
|
||||||
| term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; }
|
| term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK); $$->a1.p = $1; $$->a2.p = $5; }
|
||||||
| term '.' FIRST { $$ = f_new_inst(); $$->code = P('a','f'); $$->a1.p = $1; }
|
| term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST); $$->a1.p = $1; }
|
||||||
| term '.' LAST { $$ = f_new_inst(); $$->code = P('a','l'); $$->a1.p = $1; }
|
| term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST); $$->a1.p = $1; }
|
||||||
| term '.' LAST_NONAGGREGATED { $$ = f_new_inst(); $$->code = P('a','L'); $$->a1.p = $1; }
|
| term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG); $$->a1.p = $1; }
|
||||||
|
|
||||||
/* Communities */
|
/* Communities */
|
||||||
/* This causes one shift/reduce conflict
|
/* This causes one shift/reduce conflict
|
||||||
@ -904,19 +911,19 @@ term:
|
|||||||
| rtadot dynamic_attr '.' RESET{ }
|
| rtadot dynamic_attr '.' RESET{ }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
| '+' EMPTY '+' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_PATH; }
|
| '+' EMPTY '+' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_PATH; }
|
||||||
| '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; }
|
| '-' EMPTY '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_CLIST; }
|
||||||
| '-' '-' EMPTY '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_ECLIST; }
|
| '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_ECLIST; }
|
||||||
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_LCLIST; }
|
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_LCLIST; }
|
||||||
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('A','p'); $$->a1.p = $3; $$->a2.p = $5; }
|
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND); $$->a1.p = $3; $$->a2.p = $5; }
|
||||||
| ADD '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; }
|
| ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->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'; }
|
| DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->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'; }
|
| 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 ')' { $$ = f_generate_roa_check($3, NULL, NULL); }
|
||||||
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); }
|
| 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'); } */
|
/* | term '.' LEN { $$->code = P('P','l'); } */
|
||||||
|
|
||||||
@ -927,8 +934,7 @@ term:
|
|||||||
if ($1->class != SYM_FUNCTION)
|
if ($1->class != SYM_FUNCTION)
|
||||||
cf_error("You can't call something which is not a function. Really.");
|
cf_error("You can't call something which is not a function. Really.");
|
||||||
DBG("You are calling function %s\n", $1->name);
|
DBG("You are calling function %s\n", $1->name);
|
||||||
$$ = f_new_inst();
|
$$ = f_new_inst(FI_CALL);
|
||||||
$$->code = P('c','a');
|
|
||||||
$$->a1.p = inst;
|
$$->a1.p = inst;
|
||||||
$$->a2.p = $1->def;
|
$$->a2.p = $1->def;
|
||||||
sym = $1->aux2;
|
sym = $1->aux2;
|
||||||
@ -953,7 +959,7 @@ break_command:
|
|||||||
;
|
;
|
||||||
|
|
||||||
print_one:
|
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; }
|
print_list: /* EMPTY */ { $$ = NULL; }
|
||||||
@ -967,15 +973,13 @@ print_list: /* EMPTY */ { $$ = NULL; }
|
|||||||
;
|
;
|
||||||
|
|
||||||
var_listn: term {
|
var_listn: term {
|
||||||
$$ = f_new_inst();
|
$$ = f_new_inst(FI_SET);
|
||||||
$$->code = 's';
|
|
||||||
$$->a1.p = NULL;
|
$$->a1.p = NULL;
|
||||||
$$->a2.p = $1;
|
$$->a2.p = $1;
|
||||||
$$->next = NULL;
|
$$->next = NULL;
|
||||||
}
|
}
|
||||||
| term ',' var_listn {
|
| term ',' var_listn {
|
||||||
$$ = f_new_inst();
|
$$ = f_new_inst(FI_SET);
|
||||||
$$->code = 's';
|
|
||||||
$$->a1.p = NULL;
|
$$->a1.p = NULL;
|
||||||
$$->a2.p = $1;
|
$$->a2.p = $1;
|
||||||
$$->next = $3;
|
$$->next = $3;
|
||||||
@ -988,73 +992,63 @@ var_list: /* EMPTY */ { $$ = NULL; }
|
|||||||
|
|
||||||
cmd:
|
cmd:
|
||||||
IF term THEN block {
|
IF term THEN block {
|
||||||
$$ = f_new_inst();
|
$$ = f_new_inst(FI_CONDITION);
|
||||||
$$->code = '?';
|
|
||||||
$$->a1.p = $2;
|
$$->a1.p = $2;
|
||||||
$$->a2.p = $4;
|
$$->a2.p = $4;
|
||||||
}
|
}
|
||||||
| IF term THEN block ELSE block {
|
| IF term THEN block ELSE block {
|
||||||
struct f_inst *i = f_new_inst();
|
struct f_inst *i = f_new_inst(FI_CONDITION);
|
||||||
i->code = '?';
|
|
||||||
i->a1.p = $2;
|
i->a1.p = $2;
|
||||||
i->a2.p = $4;
|
i->a2.p = $4;
|
||||||
$$ = f_new_inst();
|
$$ = f_new_inst(FI_CONDITION);
|
||||||
$$->code = '?';
|
|
||||||
$$->a1.p = i;
|
$$->a1.p = i;
|
||||||
$$->a2.p = $6;
|
$$->a2.p = $6;
|
||||||
}
|
}
|
||||||
| SYM '=' term ';' {
|
| SYM '=' term ';' {
|
||||||
$$ = f_new_inst();
|
|
||||||
DBG( "Ook, we'll set value\n" );
|
DBG( "Ook, we'll set value\n" );
|
||||||
if (($1->class & ~T_MASK) != SYM_VARIABLE)
|
if (($1->class & ~T_MASK) != SYM_VARIABLE)
|
||||||
cf_error( "You may set only variables." );
|
cf_error( "You may set only variables." );
|
||||||
$$->code = 's';
|
$$ = f_new_inst(FI_SET);
|
||||||
$$->a1.p = $1;
|
$$->a1.p = $1;
|
||||||
$$->a2.p = $3;
|
$$->a2.p = $3;
|
||||||
}
|
}
|
||||||
| RETURN term ';' {
|
| RETURN term ';' {
|
||||||
$$ = f_new_inst();
|
|
||||||
DBG( "Ook, we'll return the value\n" );
|
DBG( "Ook, we'll return the value\n" );
|
||||||
$$->code = 'r';
|
$$ = f_new_inst(FI_RETURN);
|
||||||
$$->a1.p = $2;
|
$$->a1.p = $2;
|
||||||
}
|
}
|
||||||
| rtadot dynamic_attr '=' term ';' {
|
| rtadot dynamic_attr '=' term ';' {
|
||||||
$$ = $2;
|
$$ = f_new_inst_da(FI_EA_SET, $2);
|
||||||
$$->code = P('e','S');
|
|
||||||
$$->a1.p = $4;
|
$$->a1.p = $4;
|
||||||
}
|
}
|
||||||
| rtadot static_attr '=' term ';' {
|
| rtadot static_attr '=' term ';' {
|
||||||
$$ = $2;
|
$$ = f_new_inst_sa(FI_RTA_SET, $2);
|
||||||
if (!$$->a1.i)
|
if (!$$->a1.i)
|
||||||
cf_error( "This static attribute is read-only.");
|
cf_error( "This static attribute is read-only.");
|
||||||
$$->code = P('a','S');
|
|
||||||
$$->a1.p = $4;
|
$$->a1.p = $4;
|
||||||
}
|
}
|
||||||
| PREFERENCE '=' term ';' {
|
| PREFERENCE '=' term ';' {
|
||||||
$$ = f_new_inst();
|
$$ = f_new_inst(FI_PREF_SET);
|
||||||
$$->code = P('P','S');
|
|
||||||
$$->a1.p = $3;
|
$$->a1.p = $3;
|
||||||
}
|
}
|
||||||
| UNSET '(' rtadot dynamic_attr ')' ';' {
|
| UNSET '(' rtadot dynamic_attr ')' ';' {
|
||||||
$$ = $4;
|
$$ = f_new_inst_da(FI_EA_SET, $4);
|
||||||
$$->aux = EAF_TYPE_UNDEF | EAF_TEMP;
|
$$->aux = EAF_TYPE_UNDEF | EAF_TEMP;
|
||||||
$$->code = P('e','S');
|
|
||||||
$$->a1.p = NULL;
|
$$->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; }
|
| function_call ';' { $$ = $1; }
|
||||||
| CASE term '{' switch_body '}' {
|
| CASE term '{' switch_body '}' {
|
||||||
$$ = f_new_inst();
|
$$ = f_new_inst(FI_SWITCH);
|
||||||
$$->code = P('S','W');
|
|
||||||
$$->a1.p = $2;
|
$$->a1.p = $2;
|
||||||
$$->a2.p = build_tree( $4 );
|
$$->a2.p = build_tree( $4 );
|
||||||
}
|
}
|
||||||
|
|
||||||
| rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); }
|
| 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 '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, 'x', $2, $6 ); }
|
||||||
| rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'a', $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( P('C','a'), 'd', $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( P('C','a'), 'f', $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); }
|
| BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
* Filters: utility functions
|
* Filters: utility functions
|
||||||
*
|
*
|
||||||
* Copyright 1998 Pavel Machek <pavel@ucw.cz>
|
* 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.
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
*/
|
*/
|
||||||
@ -13,43 +14,48 @@
|
|||||||
#define P(a,b) ((a<<8) | b)
|
#define P(a,b) ((a<<8) | b)
|
||||||
|
|
||||||
struct f_inst *
|
struct f_inst *
|
||||||
f_new_inst(void)
|
f_new_inst(enum f_instruction_code fi_code)
|
||||||
{
|
{
|
||||||
struct f_inst * ret;
|
struct f_inst * ret;
|
||||||
ret = cfg_alloc(sizeof(struct f_inst));
|
ret = cfg_allocz(sizeof(struct f_inst));
|
||||||
ret->code = ret->aux = 0;
|
ret->fi_code = fi_code;
|
||||||
ret->arg1 = ret->arg2 = ret->next = NULL;
|
|
||||||
ret->lineno = ifs->lino;
|
ret->lineno = ifs->lino;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct f_inst *
|
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 *ret = f_new_inst(fi_code);
|
||||||
struct f_inst *f = f_new_inst();
|
ret->aux = (da.f_type << 8) | da.type;
|
||||||
f->aux = (f_type << 8) | type;
|
ret->a2.i = da.ea_code;
|
||||||
f->a2.i = code;
|
return ret;
|
||||||
return f;
|
}
|
||||||
|
|
||||||
|
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 ) )
|
* Generate set_dynamic( operation( get_dynamic(), argument ) )
|
||||||
*/
|
*/
|
||||||
struct f_inst *
|
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(),
|
struct f_inst *set_dyn = f_new_inst_da(FI_EA_SET, da),
|
||||||
*oper = f_new_inst(),
|
*oper = f_new_inst(operation),
|
||||||
*get_dyn = dyn;
|
*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->aux = operation_aux;
|
||||||
oper->a1.p = get_dyn;
|
oper->a1.p = get_dyn;
|
||||||
oper->a2.p = argument;
|
oper->a2.p = argument;
|
||||||
set_dyn->code = P('e','S');
|
|
||||||
set_dyn->a1.p = oper;
|
set_dyn->a1.p = oper;
|
||||||
return set_dyn;
|
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)
|
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));
|
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.lineno = ifs->lino;
|
||||||
ret->i.arg1 = prefix;
|
ret->i.arg1 = prefix;
|
||||||
ret->i.arg2 = asn;
|
ret->i.arg2 = asn;
|
||||||
|
265
filter/filter.c
265
filter/filter.c
@ -48,8 +48,6 @@
|
|||||||
#include "conf/conf.h"
|
#include "conf/conf.h"
|
||||||
#include "filter/filter.h"
|
#include "filter/filter.h"
|
||||||
|
|
||||||
#define P(a,b) ((a<<8) | b)
|
|
||||||
|
|
||||||
#define CMP_ERROR 999
|
#define CMP_ERROR 999
|
||||||
|
|
||||||
void (*bt_assert_hook)(int result, struct f_inst *assert);
|
void (*bt_assert_hook)(int result, struct f_inst *assert);
|
||||||
@ -99,8 +97,7 @@ pm_format(struct f_path_mask *p, buffer *buf)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PM_ASN_EXPR:
|
case PM_ASN_EXPR:
|
||||||
buffer_print(buf, "%u ", f_eval_asn((struct f_inst *) p->val));
|
ASSERT(0);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p = p->next;
|
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))
|
if (((v1.type == T_PAIR) || (v1.type == T_QUAD)) && (v2.type == T_CLIST))
|
||||||
return int_set_contains(v2.val.ad, v1.val.i);
|
return int_set_contains(v2.val.ad, v1.val.i);
|
||||||
|
|
||||||
/* IP->Quad implicit conversion */
|
/* IP->Quad implicit conversion */
|
||||||
if (val_is_ip4(v1) && (v2.type == T_CLIST))
|
if (val_is_ip4(v1) && (v2.type == T_CLIST))
|
||||||
return int_set_contains(v2.val.ad, ipa_to_u32(v1.val.ip));
|
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)
|
interpret(struct f_inst *what)
|
||||||
{
|
{
|
||||||
struct symbol *sym;
|
struct symbol *sym;
|
||||||
struct f_val v1, v2, res, *vp;
|
struct f_val v1, v2, res = { .type = T_VOID }, *vp;
|
||||||
unsigned u1, u2;
|
unsigned u1, u2;
|
||||||
int i;
|
int i;
|
||||||
u32 as;
|
u32 as;
|
||||||
|
|
||||||
|
for ( ; what; what = what->next) {
|
||||||
res.type = T_VOID;
|
res.type = T_VOID;
|
||||||
if (!what)
|
switch(what->fi_code) {
|
||||||
return res;
|
|
||||||
|
|
||||||
switch(what->code) {
|
|
||||||
case ',':
|
|
||||||
TWOARGS;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Binary operators */
|
/* Binary operators */
|
||||||
case '+':
|
case FI_ADD:
|
||||||
TWOARGS_C;
|
TWOARGS_C;
|
||||||
switch (res.type = v1.type) {
|
switch (res.type = v1.type) {
|
||||||
case T_VOID: runtime( "Can't operate with values of type void" );
|
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" );
|
default: runtime( "Usage of unknown type" );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '-':
|
case FI_SUBTRACT:
|
||||||
TWOARGS_C;
|
TWOARGS_C;
|
||||||
switch (res.type = v1.type) {
|
switch (res.type = v1.type) {
|
||||||
case T_VOID: runtime( "Can't operate with values of type void" );
|
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" );
|
default: runtime( "Usage of unknown type" );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '*':
|
case FI_MULTIPLY:
|
||||||
TWOARGS_C;
|
TWOARGS_C;
|
||||||
switch (res.type = v1.type) {
|
switch (res.type = v1.type) {
|
||||||
case T_VOID: runtime( "Can't operate with values of type void" );
|
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" );
|
default: runtime( "Usage of unknown type" );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '/':
|
case FI_DIVIDE:
|
||||||
TWOARGS_C;
|
TWOARGS_C;
|
||||||
switch (res.type = v1.type) {
|
switch (res.type = v1.type) {
|
||||||
case T_VOID: runtime( "Can't operate with values of type void" );
|
case T_VOID: runtime( "Can't operate with values of type void" );
|
||||||
@ -681,12 +671,12 @@ interpret(struct f_inst *what)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '&':
|
case FI_AND:
|
||||||
case '|':
|
case FI_OR:
|
||||||
ARG(v1, a1.p);
|
ARG(v1, a1.p);
|
||||||
if (v1.type != T_BOOL)
|
if (v1.type != T_BOOL)
|
||||||
runtime( "Can't do boolean operation on non-booleans" );
|
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.type = T_BOOL;
|
||||||
res.val.i = v1.val.i;
|
res.val.i = v1.val.i;
|
||||||
break;
|
break;
|
||||||
@ -699,7 +689,7 @@ interpret(struct f_inst *what)
|
|||||||
res.val.i = v2.val.i;
|
res.val.i = v2.val.i;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case P('m','p'):
|
case FI_PAIR_CONSTRUCT:
|
||||||
TWOARGS;
|
TWOARGS;
|
||||||
if ((v1.type != T_INT) || (v2.type != T_INT))
|
if ((v1.type != T_INT) || (v2.type != T_INT))
|
||||||
runtime( "Can't operate with value of non-integer type in pair constructor" );
|
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;
|
res.type = T_PAIR;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case P('m','c'):
|
case FI_EC_CONSTRUCT:
|
||||||
{
|
{
|
||||||
TWOARGS;
|
TWOARGS;
|
||||||
|
|
||||||
@ -757,7 +747,7 @@ interpret(struct f_inst *what)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case P('m','l'):
|
case FI_LC_CONSTRUCT:
|
||||||
{
|
{
|
||||||
TWOARGS;
|
TWOARGS;
|
||||||
|
|
||||||
@ -775,6 +765,32 @@ interpret(struct f_inst *what)
|
|||||||
break;
|
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 */
|
/* Relational operators */
|
||||||
|
|
||||||
#define COMPARE(x) \
|
#define COMPARE(x) \
|
||||||
@ -793,12 +809,12 @@ interpret(struct f_inst *what)
|
|||||||
res.val.i = (x); \
|
res.val.i = (x); \
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case P('!','='): SAME(!i);
|
case FI_NEQ: SAME(!i);
|
||||||
case P('=','='): SAME(i);
|
case FI_EQ: SAME(i);
|
||||||
case '<': COMPARE(i==-1);
|
case FI_LT: COMPARE(i==-1);
|
||||||
case P('<','='): COMPARE(i!=1);
|
case FI_LTE: COMPARE(i!=1);
|
||||||
|
|
||||||
case '!':
|
case FI_NOT:
|
||||||
ONEARG;
|
ONEARG;
|
||||||
if (v1.type != T_BOOL)
|
if (v1.type != T_BOOL)
|
||||||
runtime( "Not applied to non-boolean" );
|
runtime( "Not applied to non-boolean" );
|
||||||
@ -806,7 +822,7 @@ interpret(struct f_inst *what)
|
|||||||
res.val.i = !res.val.i;
|
res.val.i = !res.val.i;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '~':
|
case FI_MATCH:
|
||||||
TWOARGS;
|
TWOARGS;
|
||||||
res.type = T_BOOL;
|
res.type = T_BOOL;
|
||||||
res.val.i = val_in_range(v1, v2);
|
res.val.i = val_in_range(v1, v2);
|
||||||
@ -815,7 +831,7 @@ interpret(struct f_inst *what)
|
|||||||
res.val.i = !!res.val.i;
|
res.val.i = !!res.val.i;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case P('!','~'):
|
case FI_NOT_MATCH:
|
||||||
TWOARGS;
|
TWOARGS;
|
||||||
res.type = T_BOOL;
|
res.type = T_BOOL;
|
||||||
res.val.i = val_in_range(v1, v2);
|
res.val.i = val_in_range(v1, v2);
|
||||||
@ -824,12 +840,12 @@ interpret(struct f_inst *what)
|
|||||||
res.val.i = !res.val.i;
|
res.val.i = !res.val.i;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case P('d','e'):
|
case FI_DEFINED:
|
||||||
ONEARG;
|
ONEARG;
|
||||||
res.type = T_BOOL;
|
res.type = T_BOOL;
|
||||||
res.val.i = (v1.type != T_VOID) && !undef_value(v1);
|
res.val.i = (v1.type != T_VOID) && !undef_value(v1);
|
||||||
break;
|
break;
|
||||||
case 'T':
|
case FI_TYPE:
|
||||||
ONEARG;
|
ONEARG;
|
||||||
switch (v1.type)
|
switch (v1.type)
|
||||||
{
|
{
|
||||||
@ -841,7 +857,7 @@ interpret(struct f_inst *what)
|
|||||||
runtime( "Can't determine type of this item" );
|
runtime( "Can't determine type of this item" );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case P('I','i'):
|
case FI_IS_V4:
|
||||||
ONEARG;
|
ONEARG;
|
||||||
if (v1.type != T_IP)
|
if (v1.type != T_IP)
|
||||||
runtime( "IP version check needs an IP address" );
|
runtime( "IP version check needs an IP address" );
|
||||||
@ -850,7 +866,7 @@ interpret(struct f_inst *what)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
/* Set to indirect value, a1 = variable, a2 = value */
|
/* Set to indirect value, a1 = variable, a2 = value */
|
||||||
case 's':
|
case FI_SET:
|
||||||
ARG(v2, a2.p);
|
ARG(v2, a2.p);
|
||||||
sym = what->a1.p;
|
sym = what->a1.p;
|
||||||
vp = sym->def;
|
vp = sym->def;
|
||||||
@ -869,7 +885,7 @@ interpret(struct f_inst *what)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
/* some constants have value in a2, some in *a1.p, strange. */
|
/* 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;
|
res.type = what->aux;
|
||||||
|
|
||||||
if (res.type == T_PREFIX_SET)
|
if (res.type == T_PREFIX_SET)
|
||||||
@ -881,15 +897,15 @@ interpret(struct f_inst *what)
|
|||||||
else
|
else
|
||||||
res.val.i = what->a2.i;
|
res.val.i = what->a2.i;
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case FI_VARIABLE:
|
||||||
case 'C':
|
case FI_CONSTANT_INDIRECT:
|
||||||
res = * ((struct f_val *) what->a1.p);
|
res = * ((struct f_val *) what->a1.p);
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case FI_PRINT:
|
||||||
ONEARG;
|
ONEARG;
|
||||||
val_format(v1, &f_buf);
|
val_format(v1, &f_buf);
|
||||||
break;
|
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;
|
ONEARG;
|
||||||
if (v1.type != T_BOOL)
|
if (v1.type != T_BOOL)
|
||||||
runtime( "If requires boolean expression" );
|
runtime( "If requires boolean expression" );
|
||||||
@ -899,10 +915,10 @@ interpret(struct f_inst *what)
|
|||||||
} else res.val.i = 1;
|
} else res.val.i = 1;
|
||||||
res.type = T_BOOL;
|
res.type = T_BOOL;
|
||||||
break;
|
break;
|
||||||
case '0':
|
case FI_NOP:
|
||||||
debug( "No operation\n" );
|
debug( "No operation\n" );
|
||||||
break;
|
break;
|
||||||
case P('p',','):
|
case FI_PRINT_AND_DIE:
|
||||||
ONEARG;
|
ONEARG;
|
||||||
if ((what->a2.i == F_NOP || (what->a2.i != F_NONL && what->a1.p)) &&
|
if ((what->a2.i == F_NOP || (what->a2.i != F_NONL && what->a1.p)) &&
|
||||||
!(f_flags & FF_SILENT))
|
!(f_flags & FF_SILENT))
|
||||||
@ -925,7 +941,7 @@ interpret(struct f_inst *what)
|
|||||||
bug( "unknown return type: Can't happen");
|
bug( "unknown return type: Can't happen");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'a': /* rta access */
|
case FI_RTA_GET: /* rta access */
|
||||||
{
|
{
|
||||||
ACCESS_RTE;
|
ACCESS_RTE;
|
||||||
struct rta *rta = (*f_rte)->attrs;
|
struct rta *rta = (*f_rte)->attrs;
|
||||||
@ -948,7 +964,7 @@ interpret(struct f_inst *what)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case P('a','S'):
|
case FI_RTA_SET:
|
||||||
ACCESS_RTE;
|
ACCESS_RTE;
|
||||||
ONEARG;
|
ONEARG;
|
||||||
if (what->aux != v1.type)
|
if (what->aux != v1.type)
|
||||||
@ -1000,7 +1016,7 @@ interpret(struct f_inst *what)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case P('e','a'): /* Access to extended attributes */
|
case FI_EA_GET: /* Access to extended attributes */
|
||||||
ACCESS_RTE;
|
ACCESS_RTE;
|
||||||
{
|
{
|
||||||
eattr *e = NULL;
|
eattr *e = NULL;
|
||||||
@ -1094,7 +1110,7 @@ interpret(struct f_inst *what)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case P('e','S'):
|
case FI_EA_SET:
|
||||||
ACCESS_RTE;
|
ACCESS_RTE;
|
||||||
ONEARG;
|
ONEARG;
|
||||||
{
|
{
|
||||||
@ -1198,12 +1214,12 @@ interpret(struct f_inst *what)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case FI_PREF_GET:
|
||||||
ACCESS_RTE;
|
ACCESS_RTE;
|
||||||
res.type = T_INT;
|
res.type = T_INT;
|
||||||
res.val.i = (*f_rte)->pref;
|
res.val.i = (*f_rte)->pref;
|
||||||
break;
|
break;
|
||||||
case P('P','S'):
|
case FI_PREF_SET:
|
||||||
ACCESS_RTE;
|
ACCESS_RTE;
|
||||||
ONEARG;
|
ONEARG;
|
||||||
if (v1.type != T_INT)
|
if (v1.type != T_INT)
|
||||||
@ -1213,7 +1229,7 @@ interpret(struct f_inst *what)
|
|||||||
f_rte_cow();
|
f_rte_cow();
|
||||||
(*f_rte)->pref = v1.val.i;
|
(*f_rte)->pref = v1.val.i;
|
||||||
break;
|
break;
|
||||||
case 'L': /* Get length of */
|
case FI_LENGTH: /* Get length of */
|
||||||
ONEARG;
|
ONEARG;
|
||||||
res.type = T_INT;
|
res.type = T_INT;
|
||||||
switch(v1.type) {
|
switch(v1.type) {
|
||||||
@ -1225,7 +1241,7 @@ interpret(struct f_inst *what)
|
|||||||
default: runtime( "Prefix, path, clist or eclist expected" );
|
default: runtime( "Prefix, path, clist or eclist expected" );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case P('R','m'): /* Get ROA max prefix length */
|
case FI_ROA_MAXLEN: /* Get ROA max prefix length */
|
||||||
ONEARG;
|
ONEARG;
|
||||||
if (v1.type != T_NET || !net_is_roa(v1.val.net))
|
if (v1.type != T_NET || !net_is_roa(v1.val.net))
|
||||||
runtime( "ROA expected" );
|
runtime( "ROA expected" );
|
||||||
@ -1235,7 +1251,7 @@ interpret(struct f_inst *what)
|
|||||||
((net_addr_roa4 *) v1.val.net)->max_pxlen :
|
((net_addr_roa4 *) v1.val.net)->max_pxlen :
|
||||||
((net_addr_roa6 *) v1.val.net)->max_pxlen;
|
((net_addr_roa6 *) v1.val.net)->max_pxlen;
|
||||||
break;
|
break;
|
||||||
case P('R','a'): /* Get ROA ASN */
|
case FI_ROA_ASN: /* Get ROA ASN */
|
||||||
ONEARG;
|
ONEARG;
|
||||||
if (v1.type != T_NET || !net_is_roa(v1.val.net))
|
if (v1.type != T_NET || !net_is_roa(v1.val.net))
|
||||||
runtime( "ROA expected" );
|
runtime( "ROA expected" );
|
||||||
@ -1245,14 +1261,14 @@ interpret(struct f_inst *what)
|
|||||||
((net_addr_roa4 *) v1.val.net)->asn :
|
((net_addr_roa4 *) v1.val.net)->asn :
|
||||||
((net_addr_roa6 *) v1.val.net)->asn;
|
((net_addr_roa6 *) v1.val.net)->asn;
|
||||||
break;
|
break;
|
||||||
case P('c','p'): /* Convert prefix to ... */
|
case FI_IP: /* Convert prefix to ... */
|
||||||
ONEARG;
|
ONEARG;
|
||||||
if (v1.type != T_NET)
|
if (v1.type != T_NET)
|
||||||
runtime( "Prefix expected" );
|
runtime( "Prefix expected" );
|
||||||
res.type = T_IP;
|
res.type = T_IP;
|
||||||
res.val.ip = net_prefix(v1.val.net);
|
res.val.ip = net_prefix(v1.val.net);
|
||||||
break;
|
break;
|
||||||
case P('R','D'):
|
case FI_ROUTE_DISTINGUISHER:
|
||||||
ONEARG;
|
ONEARG;
|
||||||
if (v1.type != T_NET)
|
if (v1.type != T_NET)
|
||||||
runtime( "Prefix expected" );
|
runtime( "Prefix expected" );
|
||||||
@ -1261,7 +1277,7 @@ interpret(struct f_inst *what)
|
|||||||
res.type = T_RD;
|
res.type = T_RD;
|
||||||
res.val.ec = net_rd(v1.val.net);
|
res.val.ec = net_rd(v1.val.net);
|
||||||
break;
|
break;
|
||||||
case P('a','f'): /* Get first ASN from AS PATH */
|
case FI_AS_PATH_FIRST: /* Get first ASN from AS PATH */
|
||||||
ONEARG;
|
ONEARG;
|
||||||
if (v1.type != T_PATH)
|
if (v1.type != T_PATH)
|
||||||
runtime( "AS path expected" );
|
runtime( "AS path expected" );
|
||||||
@ -1271,7 +1287,7 @@ interpret(struct f_inst *what)
|
|||||||
res.type = T_INT;
|
res.type = T_INT;
|
||||||
res.val.i = as;
|
res.val.i = as;
|
||||||
break;
|
break;
|
||||||
case P('a','l'): /* Get last ASN from AS PATH */
|
case FI_AS_PATH_LAST: /* Get last ASN from AS PATH */
|
||||||
ONEARG;
|
ONEARG;
|
||||||
if (v1.type != T_PATH)
|
if (v1.type != T_PATH)
|
||||||
runtime( "AS path expected" );
|
runtime( "AS path expected" );
|
||||||
@ -1281,7 +1297,7 @@ interpret(struct f_inst *what)
|
|||||||
res.type = T_INT;
|
res.type = T_INT;
|
||||||
res.val.i = as;
|
res.val.i = as;
|
||||||
break;
|
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;
|
ONEARG;
|
||||||
if (v1.type != T_PATH)
|
if (v1.type != T_PATH)
|
||||||
runtime( "AS path expected" );
|
runtime( "AS path expected" );
|
||||||
@ -1289,23 +1305,23 @@ interpret(struct f_inst *what)
|
|||||||
res.type = T_INT;
|
res.type = T_INT;
|
||||||
res.val.i = as_path_get_last_nonaggregated(v1.val.ad);
|
res.val.i = as_path_get_last_nonaggregated(v1.val.ad);
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case FI_RETURN:
|
||||||
ONEARG;
|
ONEARG;
|
||||||
res = v1;
|
res = v1;
|
||||||
res.type |= T_RETURN;
|
res.type |= T_RETURN;
|
||||||
return res;
|
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;
|
ONEARG;
|
||||||
res = interpret(what->a2.p);
|
res = interpret(what->a2.p);
|
||||||
if (res.type == T_RETURN)
|
if (res.type == T_RETURN)
|
||||||
return res;
|
return res;
|
||||||
res.type &= ~T_RETURN;
|
res.type &= ~T_RETURN;
|
||||||
break;
|
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)
|
for (sym = what->a1.p; sym != NULL; sym = sym->aux2)
|
||||||
((struct f_val *) sym->def)->type = T_VOID;
|
((struct f_val *) sym->def)->type = T_VOID;
|
||||||
break;
|
break;
|
||||||
case P('S','W'):
|
case FI_SWITCH:
|
||||||
ONEARG;
|
ONEARG;
|
||||||
{
|
{
|
||||||
struct f_tree *t = find_tree(what->a2.p, v1);
|
struct f_tree *t = find_tree(what->a2.p, v1);
|
||||||
@ -1324,7 +1340,7 @@ interpret(struct f_inst *what)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case P('i','M'): /* IP.MASK(val) */
|
case FI_IP_MASK: /* IP.MASK(val) */
|
||||||
TWOARGS;
|
TWOARGS;
|
||||||
if (v2.type != T_INT)
|
if (v2.type != T_INT)
|
||||||
runtime( "Integer expected");
|
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)));
|
ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'E': /* Create empty attribute */
|
case FI_EMPTY: /* Create empty attribute */
|
||||||
res.type = what->aux;
|
res.type = what->aux;
|
||||||
res.val.ad = adata_empty(f_pool, 0);
|
res.val.ad = adata_empty(f_pool, 0);
|
||||||
break;
|
break;
|
||||||
case P('A','p'): /* Path prepend */
|
case FI_PATH_PREPEND: /* Path prepend */
|
||||||
TWOARGS;
|
TWOARGS;
|
||||||
if (v1.type != T_PATH)
|
if (v1.type != T_PATH)
|
||||||
runtime("Can't prepend to non-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);
|
res.val.ad = as_path_prepend(f_pool, v1.val.ad, v2.val.i);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case P('C','a'): /* (Extended) Community list add or delete */
|
case FI_CLIST_ADD_DEL: /* (Extended) Community list add or delete */
|
||||||
TWOARGS;
|
TWOARGS;
|
||||||
if (v1.type == T_PATH)
|
if (v1.type == T_PATH)
|
||||||
{
|
{
|
||||||
@ -1518,8 +1534,7 @@ interpret(struct f_inst *what)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FI_ROA_CHECK: /* ROA Check */
|
||||||
case P('R','C'): /* ROA Check */
|
|
||||||
if (what->arg1)
|
if (what->arg1)
|
||||||
{
|
{
|
||||||
TWOARGS;
|
TWOARGS;
|
||||||
@ -1559,14 +1574,14 @@ interpret(struct f_inst *what)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case P('f','m'): /* Format */
|
case FI_FORMAT: /* Format */
|
||||||
ONEARG;
|
ONEARG;
|
||||||
|
|
||||||
res.type = T_STRING;
|
res.type = T_STRING;
|
||||||
res.val.s = val_format_str(v1);
|
res.val.s = val_format_str(v1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case P('a','s'): /* Birdtest Assert */
|
case FI_ASSERT: /* Birdtest Assert */
|
||||||
ONEARG;
|
ONEARG;
|
||||||
|
|
||||||
if (v1.type != T_BOOL)
|
if (v1.type != T_BOOL)
|
||||||
@ -1579,10 +1594,8 @@ interpret(struct f_inst *what)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
bug( "Unknown instruction %d (%c)", what->code, what->code & 0xff);
|
bug( "Unknown instruction %d (%c)", what->fi_code, what->fi_code & 0xff);
|
||||||
}
|
}}
|
||||||
if (what->next)
|
|
||||||
return interpret(what->next);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1609,40 +1622,40 @@ i_same(struct f_inst *f1, struct f_inst *f2)
|
|||||||
return 1;
|
return 1;
|
||||||
if (f1->aux != f2->aux)
|
if (f1->aux != f2->aux)
|
||||||
return 0;
|
return 0;
|
||||||
if (f1->code != f2->code)
|
if (f1->fi_code != f2->fi_code)
|
||||||
return 0;
|
return 0;
|
||||||
if (f1 == f2) /* It looks strange, but it is possible with call rewriting trickery */
|
if (f1 == f2) /* It looks strange, but it is possible with call rewriting trickery */
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
switch(f1->code) {
|
switch(f1->fi_code) {
|
||||||
case ',': /* fall through */
|
case FI_ADD: /* fall through */
|
||||||
case '+':
|
case FI_SUBTRACT:
|
||||||
case '-':
|
case FI_MULTIPLY:
|
||||||
case '*':
|
case FI_DIVIDE:
|
||||||
case '/':
|
case FI_OR:
|
||||||
case '|':
|
case FI_AND:
|
||||||
case '&':
|
case FI_PAIR_CONSTRUCT:
|
||||||
case P('m','p'):
|
case FI_EC_CONSTRUCT:
|
||||||
case P('m','c'):
|
case FI_NEQ:
|
||||||
case P('!','='):
|
case FI_EQ:
|
||||||
case P('=','='):
|
case FI_LT:
|
||||||
case '<':
|
case FI_LTE: TWOARGS; break;
|
||||||
case P('<','='): TWOARGS; break;
|
|
||||||
|
|
||||||
case '!': ONEARG; break;
|
case FI_PATHMASK_CONSTRUCT: if (!pm_same(f1->a1.p, f2->a1.p)) return 0; break;
|
||||||
case P('!', '~'):
|
|
||||||
case '~': TWOARGS; break;
|
|
||||||
case P('d','e'): ONEARG; break;
|
|
||||||
case 'T': ONEARG; break;
|
|
||||||
case P('n','T'): 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;
|
TWOARGS;
|
||||||
if (!i_same(INST3(f1).p, INST3(f2).p))
|
if (!i_same(INST3(f1).p, INST3(f2).p))
|
||||||
return 0;
|
return 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 's':
|
case FI_SET:
|
||||||
ARG(v2, a2.p);
|
ARG(v2, a2.p);
|
||||||
{
|
{
|
||||||
struct symbol *s1, *s2;
|
struct symbol *s1, *s2;
|
||||||
@ -1655,7 +1668,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'c':
|
case FI_CONSTANT:
|
||||||
switch (f1->aux) {
|
switch (f1->aux) {
|
||||||
|
|
||||||
case T_PREFIX_SET:
|
case T_PREFIX_SET:
|
||||||
@ -1678,44 +1691,44 @@ i_same(struct f_inst *f1, struct f_inst *f2)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'C':
|
case FI_CONSTANT_INDIRECT:
|
||||||
if (!val_same(* (struct f_val *) f1->a1.p, * (struct f_val *) f2->a1.p))
|
if (!val_same(* (struct f_val *) f1->a1.p, * (struct f_val *) f2->a1.p))
|
||||||
return 0;
|
return 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'V':
|
case FI_VARIABLE:
|
||||||
if (strcmp((char *) f1->a2.p, (char *) f2->a2.p))
|
if (strcmp((char *) f1->a2.p, (char *) f2->a2.p))
|
||||||
return 0;
|
return 0;
|
||||||
break;
|
break;
|
||||||
case 'p': case 'L': ONEARG; break;
|
case FI_PRINT: case FI_LENGTH: ONEARG; break;
|
||||||
case '?': TWOARGS; break;
|
case FI_CONDITION: TWOARGS; break;
|
||||||
case '0': case 'E': break;
|
case FI_NOP: case FI_EMPTY: break;
|
||||||
case P('p',','): ONEARG; A2_SAME; break;
|
case FI_PRINT_AND_DIE: ONEARG; A2_SAME; break;
|
||||||
case 'P':
|
case FI_PREF_GET:
|
||||||
case 'a': A2_SAME; break;
|
case FI_RTA_GET: A2_SAME; break;
|
||||||
case P('e','a'): A2_SAME; break;
|
case FI_EA_GET: A2_SAME; break;
|
||||||
case P('P','S'):
|
case FI_PREF_SET:
|
||||||
case P('a','S'):
|
case FI_RTA_SET:
|
||||||
case P('e','S'): ONEARG; A2_SAME; break;
|
case FI_EA_SET: ONEARG; A2_SAME; break;
|
||||||
|
|
||||||
case 'r': ONEARG; break;
|
case FI_RETURN: ONEARG; break;
|
||||||
case P('c','p'): ONEARG; break;
|
case FI_IP: ONEARG; break;
|
||||||
case P('R','D'): ONEARG; break;
|
case FI_ROUTE_DISTINGUISHER: ONEARG; break;
|
||||||
case P('c','a'): /* Call rewriting trickery to avoid exponential behaviour */
|
case FI_CALL: /* Call rewriting trickery to avoid exponential behaviour */
|
||||||
ONEARG;
|
ONEARG;
|
||||||
if (!i_same(f1->a2.p, f2->a2.p))
|
if (!i_same(f1->a2.p, f2->a2.p))
|
||||||
return 0;
|
return 0;
|
||||||
f2->a2.p = f1->a2.p;
|
f2->a2.p = f1->a2.p;
|
||||||
break;
|
break;
|
||||||
case P('c','v'): break; /* internal instruction */
|
case FI_CLEAR_LOCAL_VARS: break; /* internal instruction */
|
||||||
case P('S','W'): ONEARG; if (!same_tree(f1->a2.p, f2->a2.p)) return 0; break;
|
case FI_SWITCH: ONEARG; if (!same_tree(f1->a2.p, f2->a2.p)) return 0; break;
|
||||||
case P('i','M'): TWOARGS; break;
|
case FI_IP_MASK: TWOARGS; break;
|
||||||
case P('A','p'): TWOARGS; break;
|
case FI_PATH_PREPEND: TWOARGS; break;
|
||||||
case P('C','a'): TWOARGS; break;
|
case FI_CLIST_ADD_DEL: TWOARGS; break;
|
||||||
case P('a','f'):
|
case FI_AS_PATH_FIRST:
|
||||||
case P('a','l'):
|
case FI_AS_PATH_LAST:
|
||||||
case P('a','L'): ONEARG; break;
|
case FI_AS_PATH_LAST_NAG: ONEARG; break;
|
||||||
case P('R','C'):
|
case FI_ROA_CHECK:
|
||||||
TWOARGS;
|
TWOARGS;
|
||||||
/* Does not really make sense - ROA check results may change anyway */
|
/* Does not really make sense - ROA check results may change anyway */
|
||||||
if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name,
|
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;
|
return 0;
|
||||||
break;
|
break;
|
||||||
default:
|
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);
|
return i_same(f1->next, f2->next);
|
||||||
}
|
}
|
||||||
@ -1853,14 +1866,6 @@ f_eval_int(struct f_inst *expr)
|
|||||||
return res.val.i;
|
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
|
* filter_same - compare two filters
|
||||||
* @new: first filter to be compared
|
* @new: first filter to be compared
|
||||||
|
@ -14,9 +14,74 @@
|
|||||||
#include "nest/route.h"
|
#include "nest/route.h"
|
||||||
#include "nest/attrs.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 { /* Instruction */
|
||||||
struct f_inst *next; /* Structure is 16 bytes, anyway */
|
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_* */
|
u16 aux; /* Extension to instruction code, T_*, EA_*, EAF_* */
|
||||||
union {
|
union {
|
||||||
uint i;
|
uint i;
|
||||||
@ -70,15 +135,32 @@ struct f_val {
|
|||||||
} 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 {
|
struct filter {
|
||||||
char *name;
|
char *name;
|
||||||
struct f_inst *root;
|
struct f_inst *root;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct f_inst *f_new_inst(void);
|
struct f_inst *f_new_inst(enum f_instruction_code fi_code);
|
||||||
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_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_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);
|
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_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);
|
struct f_val f_eval(struct f_inst *expr, struct linpool *tmp_pool);
|
||||||
uint f_eval_int(struct f_inst *expr);
|
uint f_eval_int(struct f_inst *expr);
|
||||||
u32 f_eval_asn(struct f_inst *expr);
|
|
||||||
|
|
||||||
char *filter_name(struct filter *filter);
|
char *filter_name(struct filter *filter);
|
||||||
int filter_same(struct filter *new, struct filter *old);
|
int filter_same(struct filter *new, struct filter *old);
|
||||||
|
@ -55,29 +55,28 @@ protocol static {
|
|||||||
rip_metric = rip_metric + 5;
|
rip_metric = rip_metric + 5;
|
||||||
print rip_metric;
|
print rip_metric;
|
||||||
|
|
||||||
#
|
bgp_community = -empty-;
|
||||||
# TODO: uncomment this part after finishing BGP integration version
|
print "hi";
|
||||||
#
|
bgp_community = add(bgp_community, (1,2));
|
||||||
# bgp_community = -empty-;
|
print "hello";
|
||||||
# print "hi";
|
bgp_community = add(bgp_community, (2,3));
|
||||||
# bgp_community = add(bgp_community, (1,2));
|
bgp_community.add((4,5));
|
||||||
# print "hello";
|
print "community = ", bgp_community;
|
||||||
# bgp_community = add(bgp_community, (2,3));
|
bgp_community.delete((2,3));
|
||||||
# bgp_community.add((4,5));
|
print "community = ", bgp_community;
|
||||||
# print "community = ", bgp_community;
|
bgp_community.empty;
|
||||||
# bgp_community.delete((2,3));
|
print "community = ", bgp_community;
|
||||||
# print "community = ", bgp_community;
|
print "done";
|
||||||
# bgp_community.empty;
|
|
||||||
# print "community = ", bgp_community;
|
accept;
|
||||||
# print "done";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
route 0.0.0.0/0 via 195.113.31.113;
|
route 0.0.0.0/0 via 195.113.31.113;
|
||||||
route 62.168.0.0/25 reject;
|
route 62.168.0.0/25 reject;
|
||||||
route 1.2.3.4/32 via 195.113.31.124;
|
route 1.2.3.4/32 via 195.113.31.124;
|
||||||
route 10.0.0.0/8 reject;
|
route 10.0.0.0/8 reject;
|
||||||
route 10.1.1.0:255.255.255.0 via 62.168.0.3;
|
route 10.1.1.0/24 via 62.168.0.3;
|
||||||
route 10.1.2.0:255.255.255.0 via 62.168.0.3;
|
route 10.1.2.0/24 via 62.168.0.3;
|
||||||
route 10.1.3.0:255.255.255.0 via 62.168.0.4;
|
route 10.1.3.0/24 via 62.168.0.4;
|
||||||
route 10.2.0.0/24 via "arc0";
|
route 10.2.0.0/24 via "arc0";
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
Summary: BIRD Internet Routing Daemon
|
Summary: BIRD Internet Routing Daemon
|
||||||
Name: bird
|
Name: bird
|
||||||
Version: 2.0.1
|
Version: 2.0.2
|
||||||
Release: 1
|
Release: 1
|
||||||
Copyright: GPL
|
Copyright: GPL
|
||||||
Group: Networking/Daemons
|
Group: Networking/Daemons
|
||||||
|
@ -805,8 +805,7 @@ as_path_match(const struct adata *path, struct f_path_mask *mask)
|
|||||||
val2 = val = mask->val;
|
val2 = val = mask->val;
|
||||||
goto step;
|
goto step;
|
||||||
case PM_ASN_EXPR:
|
case PM_ASN_EXPR:
|
||||||
val2 = val = f_eval_asn((struct f_inst *) mask->val);
|
ASSERT(0);
|
||||||
goto step;
|
|
||||||
case PM_ASN_RANGE:
|
case PM_ASN_RANGE:
|
||||||
val = mask->val;
|
val = mask->val;
|
||||||
val2 = mask->val2;
|
val2 = mask->val2;
|
||||||
|
@ -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_proto *p = (void *) P;
|
||||||
struct rt_dev_config *cf = (void *) P->cf;
|
struct rt_dev_config *cf = (void *) P->cf;
|
||||||
struct channel *c;
|
struct channel *c;
|
||||||
|
net_addr *net = &ad->prefix;
|
||||||
|
|
||||||
if (!EMPTY_LIST(cf->iface_list) &&
|
if (!EMPTY_LIST(cf->iface_list) &&
|
||||||
!iface_patt_find(&cf->iface_list, ad->iface, ad))
|
!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)
|
if (!c)
|
||||||
return;
|
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)
|
if (flags & IF_CHANGE_DOWN)
|
||||||
{
|
{
|
||||||
DBG("dev_if_notify: %s:%I going down\n", ad->iface->name, ad->ip);
|
DBG("dev_if_notify: %s:%I going down\n", ad->iface->name, ad->ip);
|
||||||
|
|
||||||
/* Use iface ID as local source ID */
|
/* Use iface ID as local source ID */
|
||||||
struct rte_src *src = rt_get_source(P, ad->iface->index);
|
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)
|
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);
|
a = rta_lookup(&a0);
|
||||||
e = rte_get_temp(a);
|
e = rte_get_temp(a);
|
||||||
e->pflags = 0;
|
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 *
|
static struct proto *
|
||||||
dev_init(struct proto_config *CF)
|
dev_init(struct proto_config *CF)
|
||||||
{
|
{
|
||||||
struct proto *P = proto_new(CF);
|
struct proto *P = proto_new(CF);
|
||||||
struct rt_dev_proto *p = (void *) P;
|
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->ip4_channel, cf->ip4_channel);
|
||||||
proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6));
|
proto_configure_channel(P, &p->ip6_channel, cf->ip6_channel);
|
||||||
|
|
||||||
P->if_notify = dev_if_notify;
|
P->if_notify = dev_if_notify;
|
||||||
P->ifa_notify = dev_ifa_notify;
|
P->ifa_notify = dev_ifa_notify;
|
||||||
@ -136,8 +160,8 @@ dev_reconfigure(struct proto *P, struct proto_config *CF)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return
|
return
|
||||||
proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)) &&
|
proto_configure_channel(P, &p->ip4_channel, n->ip4_channel) &&
|
||||||
proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6));
|
proto_configure_channel(P, &p->ip6_channel, n->ip6_channel);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -162,9 +186,10 @@ struct protocol proto_device = {
|
|||||||
.name = "Direct",
|
.name = "Direct",
|
||||||
.template = "direct%d",
|
.template = "direct%d",
|
||||||
.preference = DEF_PREF_DIRECT,
|
.preference = DEF_PREF_DIRECT,
|
||||||
.channel_mask = NB_IP,
|
.channel_mask = NB_IP | NB_IP6_SADR,
|
||||||
.proto_size = sizeof(struct rt_dev_proto),
|
.proto_size = sizeof(struct rt_dev_proto),
|
||||||
.config_size = sizeof(struct rt_dev_config),
|
.config_size = sizeof(struct rt_dev_config),
|
||||||
|
.postconfig = dev_postconfig,
|
||||||
.init = dev_init,
|
.init = dev_init,
|
||||||
.reconfigure = dev_reconfigure,
|
.reconfigure = dev_reconfigure,
|
||||||
.copy_config = dev_copy_config
|
.copy_config = dev_copy_config
|
||||||
|
@ -13,6 +13,9 @@ struct rt_dev_config {
|
|||||||
struct proto_config c;
|
struct proto_config c;
|
||||||
list iface_list; /* list of struct iface_patt */
|
list iface_list; /* list of struct iface_patt */
|
||||||
int check_link;
|
int check_link;
|
||||||
|
|
||||||
|
struct channel_config *ip4_channel;
|
||||||
|
struct channel_config *ip6_channel;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct rt_dev_proto {
|
struct rt_dev_proto {
|
||||||
|
@ -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
|
* rt_commit - commit new routing table configuration
|
||||||
* @new: new configuration
|
* @new: new configuration
|
||||||
@ -2101,11 +2108,10 @@ rt_commit(struct config *new, struct config *old)
|
|||||||
rtable *ot = o->table;
|
rtable *ot = o->table;
|
||||||
if (!ot->deleted)
|
if (!ot->deleted)
|
||||||
{
|
{
|
||||||
struct symbol *sym = cf_find_symbol(new, o->name);
|
r = rt_find_table_config(new, o->name);
|
||||||
if (sym && sym->class == SYM_TABLE && !new->shutdown)
|
if (r && (r->addr_type == o->addr_type) && !new->shutdown)
|
||||||
{
|
{
|
||||||
DBG("\t%s: same\n", o->name);
|
DBG("\t%s: same\n", o->name);
|
||||||
r = sym->def;
|
|
||||||
r->table = ot;
|
r->table = ot;
|
||||||
ot->name = r->name;
|
ot->name = r->name;
|
||||||
ot->config = r;
|
ot->config = r;
|
||||||
|
@ -22,9 +22,10 @@ CF_DEFINES
|
|||||||
|
|
||||||
CF_DECLS
|
CF_DECLS
|
||||||
|
|
||||||
CF_KEYWORDS(BABEL, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT, WIRED,
|
CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
|
||||||
WIRELESS, RX, TX, BUFFER, LENGTH, CHECK, LINK, BABEL_METRIC, NEXT, HOP,
|
TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK,
|
||||||
IPV4, IPV6)
|
NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
|
||||||
|
ENTRIES)
|
||||||
|
|
||||||
CF_GRAMMAR
|
CF_GRAMMAR
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
#ifdef GIT_LABEL
|
#ifdef GIT_LABEL
|
||||||
#define BIRD_VERSION XSTR1(GIT_LABEL)
|
#define BIRD_VERSION XSTR1(GIT_LABEL)
|
||||||
#else
|
#else
|
||||||
#define BIRD_VERSION "2.0.1"
|
#define BIRD_VERSION "2.0.2"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Include parameters determined by configure script */
|
/* Include parameters determined by configure script */
|
||||||
|
Loading…
Reference in New Issue
Block a user