diff options
-rw-r--r-- | NEWS | 12 | ||||
-rw-r--r-- | conf/confbase.Y | 6 | ||||
-rw-r--r-- | doc/bird.conf.example | 354 | ||||
-rw-r--r-- | doc/bird.sgml | 84 | ||||
-rw-r--r-- | filter/config.Y | 266 | ||||
-rw-r--r-- | filter/f-util.c | 44 | ||||
-rw-r--r-- | filter/filter.c | 271 | ||||
-rw-r--r-- | filter/filter.h | 91 | ||||
-rw-r--r-- | filter/test.conf2 | 35 | ||||
-rw-r--r-- | misc/bird.spec | 2 | ||||
-rw-r--r-- | nest/a-path.c | 3 | ||||
-rw-r--r-- | nest/rt-dev.c | 41 | ||||
-rw-r--r-- | nest/rt-dev.h | 3 | ||||
-rw-r--r-- | nest/rt-table.c | 12 | ||||
-rw-r--r-- | proto/babel/config.Y | 7 | ||||
-rw-r--r-- | sysdep/config.h | 2 |
16 files changed, 688 insertions, 545 deletions
@@ -1,3 +1,15 @@ +Version 2.0.2 (2018-03-xx) + o Source-specific routing support for Linux kernel and Babel + o BGP: New option 'disable after cease' + o Filter: Allow silent filter execution + o Filter: Fixed stack overflow in BGP mask expressions. + o Several bugfixes + + Notes: + + Syntax prefix:netmask for IPv4 prefixes was dropped. Just use prefix/pxlen. + + Version 2.0.1 (2018-01-16) o Linux MPLS kernel support o Better handling of channels inherited from templates diff --git a/conf/confbase.Y b/conf/confbase.Y index c2d647eb..72f56f1e 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -49,6 +49,8 @@ CF_DECLS struct rtable_config *r; struct channel_config *cc; struct f_inst *x; + struct f_dynamic_attr fda; + struct f_static_attr fsa; struct filter *f; struct f_tree *e; struct f_trie *trie; @@ -177,10 +179,6 @@ pxlen4: if ($2 > IP4_MAX_PREFIX_LENGTH) cf_error("Invalid prefix length %u", $2); $$ = $2; } - | ':' IP4 { - $$ = ip4_masklen($2); - if ($$ == 255) cf_error("Invalid netmask %I4", $2); - } ; net_ip4_: IP4 pxlen4 diff --git a/doc/bird.conf.example b/doc/bird.conf.example index 62c65ce9..384270e3 100644 --- a/doc/bird.conf.example +++ b/doc/bird.conf.example @@ -1,222 +1,204 @@ -/* - * This is an example configuration file - * (for version 1.x.x, obsolete) - */ - -# Yes, even shell-like comments work... +# This is a basic configuration file, which contains boilerplate options and +# some basic examples. It allows the BIRD daemon to start but will not cause +# anything else to happen. +# +# Please refer to the BIRD User's Guide documentation, which is also available +# online at http://bird.network.cz/ in HTML format, for more information on +# configuring BIRD and adding routing protocols. # Configure logging -#log syslog { debug, trace, info, remote, warning, error, auth, fatal, bug }; -#log stderr all; -#log "tmp" all; +log syslog all; +# log "/var/log/bird.log" { debug, trace, info, remote, warning, error, auth, fatal, bug }; -# Override router ID -#router id 198.51.100.1; +# Set router ID. It is a unique identification of your router, usually one of +# IPv4 addresses of the router. It is recommended to configure it explicitly. +# router id 198.51.100.1; -# You can define your own symbols... -#define xyzzy = (120+10); -#define '1a-a1' = (30+40); +# Turn on global debugging of all protocols (all messages or just selected classes) +# debug protocols all; +# debug protocols { events, states }; -# Define a route filter... -#filter test_filter { -# if net ~ 10.0.0.0/16 then accept; -# else reject; -#} +# Turn on internal watchdog +# watchdog warning 5 s; +# watchdog timeout 30 s; -#filter sink { reject; } -#filter okay { accept; } +# You can define your own constants +# define my_asn = 65000; +# define my_addr = 198.51.100.1; -#include "filters.conf"; +# Tables master4 and master6 are defined by default +# ipv4 table master4; +# ipv6 table master6; -# Define another routing table -#table testable; +# Define more tables, e.g. for policy routing or as MRIB +# ipv4 table mrib4; +# ipv6 table mrib6; -# Turn on global debugging of all protocols -#debug protocols all; +# 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 { +} -# Turn on internal watchdog -#watchdog warning 5 s; -#watchdog timeout 30 s; - -# The direct protocol automatically generates device routes to -# all network interfaces. Can exist in as many instances as you wish -# if you want to populate multiple routing tables with device routes. -#protocol direct { -# interface "-eth*", "*"; # Restrict network interfaces it works with -#} +# The direct protocol is not a real routing protocol. It automatically generates +# direct routes to all network interfaces. Can exist in as many instances as you +# wish if you want to populate multiple routing tables with direct routes. +protocol direct { + disabled; # Disable by default + ipv4; # Connect to default IPv4 table + ipv6; # ... and to default IPv6 table +} -# This pseudo-protocol performs synchronization between BIRD's routing -# tables and the kernel. If your kernel supports multiple routing tables -# (as Linux 2.2.x does), you can run multiple instances of the kernel -# protocol and synchronize different kernel tables with different BIRD tables. +# 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 { -# 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) + 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) } -# This pseudo-protocol watches all interface up/down events. -protocol device { - scan time 10; # Scan interfaces every 10 seconds +# Another instance for IPv6, skipping default options +protocol kernel { + ipv6 { export all; }; } -# Static routes (again, there can be multiple instances, so that you -# can disable/enable various groups of static routes on the fly). +# Static routes (Again, there can be multiple instances, for different address +# families and to disable/enable various groups of static routes on the fly). protocol static { -# disabled; # Disable by default -# table testable; # Connect to a non-default table -# preference 1000; # Default preference of routes -# debug { states, routes, filters, interfaces, events, packets }; -# debug all; -# route 0.0.0.0/0 via 198.51.100.13; -# route 198.51.100.0/25 unreachable; + ipv4; # Again, IPv4 channel with default options + +# route 0.0.0.0/0 via 198.51.100.10; +# route 192.0.2.0/24 blackhole; # route 10.0.0.0/8 unreachable; -# route 10.1.1.0:255.255.255.0 via 198.51.100.3; -# route 10.1.2.0:255.255.255.0 via 198.51.100.3; -# route 10.1.3.0:255.255.255.0 via 198.51.100.4; -# route 10.2.0.0/24 via "arc0"; +# route 10.2.0.0/24 via "eth0"; +# # Static routes can be defined with optional attributes +# route 10.1.1.0/24 via 198.51.100.3 { rip_metric = 3; }; +# route 10.1.2.0/24 via 198.51.100.3 { ospf_metric1 = 100; }; +# route 10.1.3.0/24 via 198.51.100.4 { ospf_metric2 = 100; }; } -# Pipe protocol connects two routing tables... Beware of loops. -#protocol pipe { -# peer table testable; -# Define what routes do we export to this protocol / import from it. -# import all; # default is all -# export all; # default is none -# import none; # If you wish to disable imports -# import filter test_filter; # Use named filter -# import where source = RTS_DEVICE; # Use explicit filter -#} - -# RIP aka Rest In Pieces... -#protocol rip MyRIP { # You can also use an explicit name -# preference xyzzy; -# debug all; -# port 1520; -# period 7; -# infinity 16; -# garbage time 60; -# interface "*" { mode broadcast; }; -# honor neighbor; # To whom do we agree to send the routing table -# honor always; -# honor never; -# passwords { -# password "nazdar"; +# Pipe protocol connects two routing tables. Beware of loops. +# protocol pipe { +# table master4; # No ipv4/ipv6 channel definition like in other protocols +# peer table mrib4; +# import all; # Direction peer table -> table +# export all; # Direction table -> peer table +# } + +# RIP example, both RIP and RIPng are supported +# protocol rip { +# ipv4 { +# # Export direct, static routes and ones from RIP itself +# import all; +# export where source ~ [ RTS_DEVICE, RTS_STATIC, RTS_RIP ]; # }; -# authentication none; -# import filter { print "importing"; accept; }; -# export filter { print "exporting"; accept; }; -#} +# interface "eth*" { +# update time 10; # Default period is 30 +# timeout time 60; # Default timeout is 180 +# authentication cryptographic; # No authentication by default +# password "hello" { algorithm hmac sha256; }; # Default is MD5 +# }; +# } -#protocol ospf MyOSPF { -# tick 2; -# rfc1583compat yes; -# area 0.0.0.0 { -# stub no; +# OSPF example, both OSPFv2 and OSPFv3 are supported +# protocol ospf v3 { +# ipv6 { +# import all; +# export where source = RTS_STATIC; +# }; +# area 0 { # interface "eth*" { -# hello 9; -# retransmit 6; -# cost 10; -# transmit delay 5; -# dead count 5; -# wait 50; -# type broadcast; -# authentication simple; -# password "pass"; +# type broadcast; # Detected by default +# cost 10; # Interface metric +# hello 5; # Default hello perid 10 is too long # }; -# interface "arc0" { -# rx buffer large; -# type nonbroadcast; -# poll 14; -# dead 75; -# neighbors { -# 10.1.1.2 eligible; -# 10.1.1.4; -# }; -# strict nonbroadcast yes; +# interface "tun*" { +# type ptp; # PtP mode, avoids DR selection +# cost 100; # Interface metric +# hello 5; # Default hello perid 10 is too long # }; -# interface "xxx0" { -# passwords { -# password "abc" { -# id 1; -# generate to "22-04-2003 11:00:06"; -# accept to "17-01-2004 12:01:05"; -# }; -# password "def" { -# id 2; -# generate from "22-04-2003 11:00:07"; -# accept from "17-01-2003 12:01:05"; -# }; -# }; -# authentication cryptographic; -# }; -# }; -# area 20 { -# stub 1; -# interface "ppp1" { -# hello 8; -# authentication none; +# interface "dummy0" { +# stub; # Stub interface, just propagate it # }; -# interface "fr*"; -# virtual link 192.168.0.1 { -# password "sdsdffsdfg"; -# authentication cryptographic; -# }; # }; #} - -#protocol bgp { -# disabled; +# 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; +# } + +# BGP example, explicit name 'uplink1' is used instead of default 'bgp1' +# protocol bgp uplink1 { # description "My BGP uplink"; -# local as 65000; -# neighbor 198.51.100.130 as 64496; -# multihop; -# hold time 240; -# startup hold time 240; -# connect retry time 120; -# keepalive time 80; # defaults to hold time / 3 -# start delay time 5; # How long do we wait before initial connect -# error wait time 60, 300;# Minimum and maximum time we wait after an error (when consecutive -# # errors occur, we increase the delay exponentially ... -# error forget time 300; # ... until this timeout expires) -# disable after error; # Disable the protocol automatically when an error occurs -# next hop self; # Disable next hop processing and always advertise our local address as nexthop -# path metric 1; # Prefer routes with shorter paths (like Cisco does) -# default bgp_med 0; # MED value we use for comparison when none is defined -# default bgp_local_pref 0; # The same for local preference -# source address 198.51.100.14; # What local address we use for the TCP connection +# local 198.51.100.1 as 65000; +# neighbor 198.51.100.10 as 64496; +# hold time 90; # Default is 240 # password "secret"; # Password used for MD5 authentication -# rr client; # I am a route reflector and the neighor is my client -# rr cluster id 1.0.0.1; # Use this value for cluster id instead of my router id -# export where source=RTS_STATIC; -# export filter { -# if source = RTS_STATIC then { -# bgp_community = -empty-; bgp_community = add(bgp_community,(65000,5678)); -# bgp_origin = 0; -# bgp_community = -empty-; bgp_community.add((65000,5678)); -# if (65000,64501) ~ bgp_community then -# bgp_community.add((0, 1)); -# if bgp_path ~ [= 65000 =] then -# bgp_path.prepend(65000); -# accept; -# } -# reject; +# +# ipv4 { # regular IPv4 unicast (1/1) +# import filter rt_import; +# export where source ~ [ RTS_STATIC, RTS_BGP ]; +# }; +# +# ipv6 { # regular IPv6 unicast (2/1) +# import filter rt_import; +# export filter { # The same as 'where' expression above +# if source ~ [ RTS_STATIC, RTS_BGP ] +# then accept; +# else reject; +# }; +# }; +# +# ipv4 multicast { # IPv4 multicast topology (1/2) +# table mrib4; # explicit IPv4 table +# import filter rt_import; +# export all; # }; -#} # -# Template usage example -#template bgp rr_client { -# disabled; -# local as 65000; -# multihop; +# ipv6 multicast { # IPv6 multicast topology (2/2) +# table mrib6; # explicit IPv6 table +# import filter rt_import; +# export all; +# }; +#} + +# Template example. Using templates to define IBGP route reflector clients. +# template bgp rr_clients { +# local 10.0.0.1 as 65000; +# neighbor as 65000; # rr client; # rr cluster id 1.0.0.1; -#} # -#protocol bgp rr_abcd from rr_client { -# neighbor 10.1.4.7 as 65000; -#} +# ipv4 { +# import all; +# export where source = RTS_BGP; +# }; +# +# ipv6 { +# import all; +# export where source = RTS_BGP; +# }; +# } +# +# protocol bgp client1 from rr_clients { +# neighbor 10.0.1.1; +# } +# +# protocol bgp client2 from rr_clients { +# neighbor 10.0.2.1; +# } +# +# protocol bgp client3 from rr_clients { +# neighbor 10.0.3.1; +# } diff --git a/doc/bird.sgml b/doc/bird.sgml index 04f9258b..903456f0 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -293,6 +293,20 @@ routes are: <item>Route next hops (see below) </itemize> +<sect1>IPv6 source-specific routes +<label id="ip-sadr-routes"> + +<p>The IPv6 routes containing both destination and source prefix. They are used +for source-specific routing (SSR), also called source-address dependent routing +(SADR), see <rfc id="8043">. Currently limited mostly to the Babel protocol. +Configuration keyword is <cf/ipv6 sadr/. + +<itemize> + <item>(PK) Route destination (IP prefix together with its length) + <item>(PK) Route source (IP prefix together with its length) + <item>Route next hops (see below) +</itemize> + <sect1>VPN IPv4 and IPv6 routes <label id="vpn-routes"> @@ -646,9 +660,9 @@ agreement"). restricted to interfaces assigned to the VRF and will use sockets bound to the VRF. Appropriate VRF interface must exist on OS level. For kernel protocol, an appropriate table still must be explicitly selected by - <cf/table/ option. Note that the VRF support in BIRD and Linux kernel - (4.11) is still in development and is currently problematic outside of - multihop BGP. + <cf/table/ option. Note that for proper VRF support it is necessary to + use Linux kernel version at least 4.14, older versions have limited VRF + implementation. <tag><label id="proto-channel"><m/channel name/ [{<m/channel config/}]</tag> Every channel must be explicitly stated. See the protocol-specific @@ -1234,12 +1248,17 @@ foot). operator <cf/.type/. The type may be: <cf/NET_IP4/ and <cf/NET_IP6/ prefixes hold an IP prefix. The literals - are written as <cf><m/ipaddress//<m/pxlen/</cf>, - or <cf><m>ipaddress</m>/<m>netmask</m></cf>. There are two special + are written as <cf><m/ipaddress//<m/pxlen/</cf>. There are two special operators on these: <cf/.ip/ which extracts the IP address from the pair, and <cf/.len/, which separates prefix length from the pair. So <cf>1.2.0.0/16.len = 16</cf> is true. + <cf/NET_IP6_SADR/ nettype holds both destination and source IPv6 + prefix. The literals are written as <cf><m/ipaddress//<m/pxlen/ from + <m/ipaddress//<m/pxlen/</cf>, where the first part is the destination + prefix and the second art is the source prefix. They support the same + operators as IP prefixes, but just for the destination part. + <cf/NET_VPN4/ and <cf/NET_VPN6/ prefixes hold an IP prefix with VPN Route Distinguisher (<rfc id="4364">). They support the same special operators as IP prefixes, and also <cf/.rd/ which extracts the Route @@ -1460,6 +1479,7 @@ foot). lclists, with LCs instead of pairs as arguments. </descrip> + <sect>Operators <label id="operators"> @@ -1652,19 +1672,25 @@ cases desirable. routes over the same IPv6 transport. For sending and receiving Babel packets, only a link-local IPv6 address is needed. -<p>BIRD does not implement any Babel extensions, but will coexist with -implementations using extensions (and will just ignore extension messages). +<p>BIRD implements an extension for IPv6 source-specific routing (SSR or SADR), +but must be configured accordingly to use it. SADR-enabled Babel router can +interoperate with non-SADR Babel router, but the later would ignore routes +with specific (non-zero) source prefix. <sect1>Configuration <label id="babel-config"> -<p>Babel supports no global configuration options apart from those common to all -other protocols, but supports the following per-interface configuration options: +<p>The Babel protocol support both IPv4 and IPv6 channels; both can be +configured simultaneously. It can also be configured with <ref +id="ip-sadr-routes" name="IPv6 SADR"> channel instead of regular IPv6 +channel, in such case SADR support is enabled. Babel supports no global +configuration options apart from those common to all other protocols, but +supports the following per-interface configuration options: <code> protocol babel [<name>] { ipv4 { <channel config> }; - ipv6 { <channel config> }; + ipv6 [sadr] { <channel config> }; interface <interface pattern> { type <wired|wireless>; rxcost <number>; @@ -1684,8 +1710,8 @@ protocol babel [<name>] { </code> <descrip> - <tag><label id="babel-channel">ipv4|ipv6 <m/channel config/</tag> - The supported channels are IPv4 and IPv6. + <tag><label id="babel-channel">ipv4 | ipv6 [sadr] <m/channel config/</tag> + The supported channels are IPv4, IPv6, and IPv6 SADR. <tag><label id="babel-type">type wired|wireless </tag> This option specifies the interface type: Wired or wireless. On wired @@ -2119,22 +2145,24 @@ to set routing policy and all the other parameters differently for each neighbor using the following configuration parameters: <descrip> - <tag><label id="bgp-local">local [<m/ip/] as <m/number/</tag> + <tag><label id="bgp-local">local [<m/ip/] [port <m/number/] [as <m/number/]</tag> Define which AS we are part of. (Note that contrary to other IP routers, BIRD is able to act as a router located in multiple AS'es simultaneously, but in such cases you need to tweak the BGP paths manually in the filters to get consistent behavior.) Optional <cf/ip/ argument specifies a source - address, equivalent to the <cf/source address/ option (see below). This - parameter is mandatory. + address, equivalent to the <cf/source address/ option (see below). + Optional <cf/port/ argument specifies the local BGP port instead of + standard port 179. The parameter may be used multiple times with + different sub-options (e.g., both <cf/local 10.0.0.1 as 65000;/ and + <cf/local 10.0.0.1; local as 65000;/ are valid). This parameter is + mandatory. <tag><label id="bgp-neighbor">neighbor [<m/ip/] [port <m/number/] [as <m/number/]</tag> Define neighboring router this instance will be talking to and what AS it is located in. In case the neighbor is in the same AS as we are, we automatically switch to iBGP. Optionally, the remote port may also be - specified. The parameter may be used multiple times with different - sub-options (e.g., both <cf/neighbor 10.0.0.1 as 65000;/ and - <cf/neighbor 10.0.0.1; neighbor as 65000;/ are valid). This parameter is - mandatory. + specified. Like <cf/local/ parameter, this parameter may also be used + multiple times with different sub-options. This parameter is mandatory. <tag><label id="bgp-iface">interface <m/string/</tag> Define interface we should use for link-local BGP IPv6 sessions. @@ -2744,16 +2772,17 @@ protocol device { <p>The Direct protocol is a simple generator of device routes for all the directly connected networks according to the list of interfaces provided by the kernel via the Device protocol. The Direct protocol supports both IPv4 and IPv6 -channels. +channels; both can be configured simultaneously. It can also be configured with +<ref id="ip-sadr-routes" name="IPv6 SADR"> channel instead of regular IPv6 +channel in order to be used together with SADR-enabled Babel protocol. <p>The question is whether it is a good idea to have such device routes in BIRD routing table. OS kernel usually handles device routes for directly connected networks by itself so we don't need (and don't want) to export these routes to the kernel protocol. OSPF protocol creates device routes for its interfaces -itself and BGP protocol is usually used for exporting aggregate routes. Although -there are some use cases that use the direct protocol (like abusing eBGP as an -IGP routing protocol), in most cases it is not needed to have these device -routes in BIRD routing table and to use the direct protocol. +itself and BGP protocol is usually used for exporting aggregate routes. But the +Direct protocol is necessary for distance-vector protocols like RIP or Babel to +announce local networks. <p>There is one notable case when you definitely want to use the direct protocol -- running BIRD on BSD systems. Having high priority device routes for directly @@ -2828,8 +2857,10 @@ kernel protocols to the same routing table and changing route destination (gateway) in an export filter of a kernel protocol does not work. Both limitations can be overcome using another routing table and the pipe protocol. -<p>The Kernel protocol supports both IPv4 and IPv6 channels; only one of them -can be configured in each protocol instance. +<p>The Kernel protocol supports both IPv4 and IPv6 channels; only one channel +can be configured in each protocol instance. On Linux, it also supports <ref +id="ip-sadr-routes" name="IPv6 SADR"> and <ref id="mpls-routes" name="MPLS"> +channels. <sect1>Configuration <label id="krt-config"> @@ -4280,6 +4311,7 @@ protocol rip { <sect>RPKI +<label id="rpki"> <sect1>Introduction diff --git a/filter/config.Y b/filter/config.Y index cd5a5b33..f8170a83 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -12,8 +12,6 @@ CF_HDR CF_DEFINES -#define P(a,b) ((a << 8) | b) - static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; } static inline u32 pair_a(u32 p) { return p >> 16; } static inline u32 pair_b(u32 p) { return p & 0xFFFF; } @@ -157,12 +155,11 @@ f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3) } static inline struct f_inst * -f_generate_empty(struct f_inst *dyn) +f_generate_empty(struct f_dynamic_attr dyn) { - struct f_inst *e = f_new_inst(); - e->code = 'E'; + struct f_inst *e = f_new_inst(FI_EMPTY); - switch (dyn->aux & EAF_TYPE_MASK) { + switch (dyn.type & EAF_TYPE_MASK) { case EAF_TYPE_AS_PATH: e->aux = T_PATH; break; @@ -179,9 +176,9 @@ f_generate_empty(struct f_inst *dyn) cf_error("Can't empty that attribute"); } - dyn->code = P('e','S'); - dyn->a1.p = e; - return dyn; + struct f_inst *s = f_new_inst_da(FI_EA_SET, dyn); + s->a1.p = e; + return s; } @@ -190,21 +187,19 @@ f_generate_dpair(struct f_inst *t1, struct f_inst *t2) { struct f_inst *rv; - if ((t1->code == 'c') && (t2->code == 'c')) { + if ((t1->fi_code == FI_CONSTANT) && (t2->fi_code == FI_CONSTANT)) { if ((t1->aux != T_INT) || (t2->aux != T_INT)) cf_error( "Can't operate with value of non-integer type in pair constructor"); check_u16(t1->a2.i); check_u16(t2->a2.i); - rv = f_new_inst(); - rv->code = 'c'; + rv = f_new_inst(FI_CONSTANT); rv->aux = T_PAIR; rv->a2.i = pair(t1->a2.i, t2->a2.i); } else { - rv = f_new_inst(); - rv->code = P('m', 'p'); + rv = f_new_inst(FI_PAIR_CONSTRUCT); rv->a1.p = t1; rv->a2.p = t2; } @@ -219,7 +214,7 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv) int c1 = 0, c2 = 0, ipv4_used = 0; u32 key = 0, val2 = 0; - if (tk->code == 'c') { + if (tk->fi_code == FI_CONSTANT) { c1 = 1; if (tk->aux == T_INT) { @@ -233,7 +228,7 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv) } /* IP->Quad implicit conversion */ - else if (tk->code == 'C') { + else if (tk->fi_code == FI_CONSTANT_INDIRECT) { c1 = 1; struct f_val *val = tk->a1.p; @@ -250,7 +245,7 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv) cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor"); } - if (tv->code == 'c') { + if (tv->fi_code == FI_CONSTANT) { if (tv->aux != T_INT) cf_error("Can't operate with value of non-integer type in EC constructor"); c2 = 1; @@ -276,15 +271,13 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv) } NEW_F_VAL; - rv = f_new_inst(); - rv->code = 'C'; + rv = f_new_inst(FI_CONSTANT_INDIRECT); rv->a1.p = val; val->type = T_EC; val->val.ec = ec; } else { - rv = f_new_inst(); - rv->code = P('m','c'); + rv = f_new_inst(FI_EC_CONSTRUCT); rv->aux = kind; rv->a1.p = tk; rv->a2.p = tv; @@ -298,12 +291,11 @@ f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3) { struct f_inst *rv; - if ((t1->code == 'c') && (t2->code == 'c') && (t3->code == 'c')) { + if ((t1->fi_code == FI_CONSTANT) && (t2->fi_code == FI_CONSTANT) && (t3->fi_code == FI_CONSTANT)) { if ((t1->aux != T_INT) || (t2->aux != T_INT) || (t3->aux != T_INT)) cf_error( "LC - Can't operate with value of non-integer type in tuple constructor"); - rv = f_new_inst(); - rv->code = 'C'; + rv = f_new_inst(FI_CONSTANT_INDIRECT); NEW_F_VAL; rv->a1.p = val; @@ -314,7 +306,7 @@ f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3) { rv = cfg_allocz(sizeof(struct f_inst3)); rv->lineno = ifs->lino; - rv->code = P('m','l'); + rv->fi_code = FI_LC_CONSTRUCT; rv->a1.p = t1; rv->a2.p = t2; INST3(rv).p = t3; @@ -323,6 +315,27 @@ f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3) return rv; } +static inline struct f_inst * +f_generate_path_mask(struct f_path_mask *t) +{ + for (struct f_path_mask *tt = t; tt; tt = tt->next) { + if (tt->kind == PM_ASN_EXPR) { + struct f_inst *mrv = f_new_inst(FI_PATHMASK_CONSTRUCT); + mrv->a1.p = t; + return mrv; + } + } + + NEW_F_VAL; + val->type = T_PATH_MASK; + val->val.path_mask = t; + + struct f_inst *rv = f_new_inst(FI_CONSTANT_INDIRECT); + rv->a1.p = val; + + return rv; +} + /* * Remove all new lines and doubled whitespaces * and convert all tabulators to spaces @@ -372,8 +385,7 @@ static struct f_inst * assert_done(struct f_inst *expr, const char *start, const char *end) { struct f_inst *i; - i = f_new_inst(); - i->code = P('a','s'); + i = f_new_inst(FI_ASSERT); i->a1.p = expr; if (end >= start) @@ -412,7 +424,9 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, %nonassoc THEN %nonassoc ELSE -%type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn dynamic_attr static_attr function_call symbol bgp_path_expr +%type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn function_call symbol bgp_path_expr +%type <fda> dynamic_attr +%type <fsa> static_attr %type <f> filter filter_body where_filter %type <i> type break_command ec_kind %type <i32> cnum @@ -543,16 +557,13 @@ where_filter: /* Construct 'IF term THEN ACCEPT; REJECT;' */ struct filter *f = cfg_alloc(sizeof(struct filter)); struct f_inst *i, *acc, *rej; - acc = f_new_inst(); /* ACCEPT */ - acc->code = P('p',','); + acc = f_new_inst(FI_PRINT_AND_DIE); /* ACCEPT */ acc->a1.p = NULL; acc->a2.i = F_ACCEPT; - rej = f_new_inst(); /* REJECT */ - rej->code = P('p',','); + rej = f_new_inst(FI_PRINT_AND_DIE); /* REJECT */ rej->a1.p = NULL; rej->a2.i = F_REJECT; - i = f_new_inst(); /* IF */ - i->code = '?'; + i = f_new_inst(FI_CONDITION); /* IF */ i->a1.p = $2; i->a2.p = acc; i->next = rej; @@ -571,8 +582,7 @@ function_body: decls '{' cmds '}' { if ($1) { /* Prepend instruction to clear local variables */ - $$ = f_new_inst(); - $$->code = P('c','v'); + $$ = f_new_inst(FI_CLEAR_LOCAL_VARS); $$->a1.p = $1; $$->next = $3; } else @@ -755,7 +765,7 @@ switch_body: /* EMPTY */ { $$ = NULL; } } ; -/* CONST '(' expr ')' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $3; } */ +/* CONST '(' expr ')' { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_INT; $$->a2.i = $3; } */ bgp_path_expr: symbol { $$ = $1; } @@ -776,23 +786,23 @@ bgp_path_tail: ; constant: - NUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $1; } - | TRUE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 1; } - | FALSE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 0; } - | TEXT { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_STRING; $$->a2.p = $1; } - | fipa { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; } - | VPN_RD { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_RD; val->val.ec = $1; $$->a1.p = val; } - | net_ { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_NET; val->val.net = $1; $$->a1.p = val; } - | '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); } - | '[' fprefix_set ']' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PREFIX_SET; $$->a2.p = $2; } - | ENUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; } - | bgp_path { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_PATH_MASK; val->val.path_mask = $1; $$->a1.p = val; } + NUM { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_INT; $$->a2.i = $1; } + | TRUE { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_BOOL; $$->a2.i = 1; } + | FALSE { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_BOOL; $$->a2.i = 0; } + | TEXT { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_STRING; $$->a2.p = $1; } + | fipa { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); $$->a1.p = val; *val = $1; } + | VPN_RD { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); val->type = T_RD; val->val.ec = $1; $$->a1.p = val; } + | net_ { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); val->type = T_NET; val->val.net = $1; $$->a1.p = val; } + | '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(FI_CONSTANT); $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); } + | '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_PREFIX_SET; $$->a2.p = $2; } + | ENUM { $$ = f_new_inst(FI_CONSTANT); $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; } ; constructor: '(' term ',' term ')' { $$ = f_generate_dpair($2, $4); } | '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); } | '(' term ',' term ',' term ')' { $$ = f_generate_lc($2, $4, $6); } + | bgp_path { $$ = f_generate_path_mask($1); } ; @@ -801,7 +811,7 @@ constructor: * For such cases, we force the dynamic_attr list to contain * at least an invalid token, so it is syntantically correct. */ -CF_ADDTO(dynamic_attr, INVALID_TOKEN { $$ = NULL; }) +CF_ADDTO(dynamic_attr, INVALID_TOKEN { $$ = (struct f_dynamic_attr) {}; }) rtadot: /* EMPTY, we are not permitted RTA. prefix */ ; @@ -813,8 +823,7 @@ function_call: if ($1->class != SYM_FUNCTION) cf_error("You can't call something which is not a function. Really."); DBG("You are calling function %s\n", $1->name); - $$ = f_new_inst(); - $$->code = P('c','a'); + $$ = f_new_inst(FI_CALL); $$->a1.p = inst; $$->a2.p = $1->def; sym = $1->aux2; @@ -831,11 +840,9 @@ function_call: symbol: SYM { - $$ = f_new_inst(); - switch ($1->class & 0xff00) { - case SYM_CONSTANT: $$->code = 'C'; break; - case SYM_VARIABLE: $$->code = 'V'; break; + case SYM_CONSTANT: $$ = f_new_inst(FI_CONSTANT_INDIRECT); break; + case SYM_VARIABLE: $$ = f_new_inst(FI_VARIABLE); break; default: cf_error("%s: variable expected.", $1->name); } @@ -844,57 +851,57 @@ symbol: } static_attr: - FROM { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = SA_FROM; $$->a1.i = 1; } - | GW { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = SA_GW; $$->a1.i = 1; } - | NET { $$ = f_new_inst(); $$->aux = T_NET; $$->a2.i = SA_NET; } - | PROTO { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_PROTO; } - | SOURCE { $$ = f_new_inst(); $$->aux = T_ENUM_RTS; $$->a2.i = SA_SOURCE; } - | SCOPE { $$ = f_new_inst(); $$->aux = T_ENUM_SCOPE; $$->a2.i = SA_SCOPE; $$->a1.i = 1; } - | DEST { $$ = f_new_inst(); $$->aux = T_ENUM_RTD; $$->a2.i = SA_DEST; $$->a1.i = 1; } - | IFNAME { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_IFNAME; } - | IFINDEX { $$ = f_new_inst(); $$->aux = T_INT; $$->a2.i = SA_IFINDEX; } + FROM { $$ = f_new_static_attr(T_IP, SA_FROM, 1); } + | GW { $$ = f_new_static_attr(T_IP, SA_GW, 1); } + | NET { $$ = f_new_static_attr(T_NET, SA_NET, 0); } + | PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 0); } + | SOURCE { $$ = f_new_static_attr(T_ENUM_RTS, SA_SOURCE, 0); } + | SCOPE { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE, 1); } + | DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 1); } + | IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); } + | IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 0); } ; term: - '(' term ')' { $$ = $2; } - | term '+' term { $$ = f_new_inst(); $$->code = '+'; $$->a1.p = $1; $$->a2.p = $3; } - | term '-' term { $$ = f_new_inst(); $$->code = '-'; $$->a1.p = $1; $$->a2.p = $3; } - | term '*' term { $$ = f_new_inst(); $$->code = '*'; $$->a1.p = $1; $$->a2.p = $3; } - | term '/' term { $$ = f_new_inst(); $$->code = '/'; $$->a1.p = $1; $$->a2.p = $3; } - | term AND term { $$ = f_new_inst(); $$->code = '&'; $$->a1.p = $1; $$->a2.p = $3; } - | term OR term { $$ = f_new_inst(); $$->code = '|'; $$->a1.p = $1; $$->a2.p = $3; } - | term '=' term { $$ = f_new_inst(); $$->code = P('=','='); $$->a1.p = $1; $$->a2.p = $3; } - | term NEQ term { $$ = f_new_inst(); $$->code = P('!','='); $$->a1.p = $1; $$->a2.p = $3; } - | term '<' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $1; $$->a2.p = $3; } - | term LEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $1; $$->a2.p = $3; } - | term '>' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $3; $$->a2.p = $1; } - | term GEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $3; $$->a2.p = $1; } - | term '~' term { $$ = f_new_inst(); $$->code = '~'; $$->a1.p = $1; $$->a2.p = $3; } - | term NMA term { $$ = f_new_inst(); $$->code = P('!','~'); $$->a1.p = $1; $$->a2.p = $3; } - | '!' term { $$ = f_new_inst(); $$->code = '!'; $$->a1.p = $2; } - | DEFINED '(' term ')' { $$ = f_new_inst(); $$->code = P('d','e'); $$->a1.p = $3; } + '(' term ')' { $$ = $2; } + | term '+' term { $$ = f_new_inst(FI_ADD); $$->a1.p = $1; $$->a2.p = $3; } + | term '-' term { $$ = f_new_inst(FI_SUBTRACT); $$->a1.p = $1; $$->a2.p = $3; } + | term '*' term { $$ = f_new_inst(FI_MULTIPLY); $$->a1.p = $1; $$->a2.p = $3; } + | term '/' term { $$ = f_new_inst(FI_DIVIDE); $$->a1.p = $1; $$->a2.p = $3; } + | term AND term { $$ = f_new_inst(FI_AND); $$->a1.p = $1; $$->a2.p = $3; } + | term OR term { $$ = f_new_inst(FI_OR); $$->a1.p = $1; $$->a2.p = $3; } + | term '=' term { $$ = f_new_inst(FI_EQ); $$->a1.p = $1; $$->a2.p = $3; } + | term NEQ term { $$ = f_new_inst(FI_NEQ); $$->a1.p = $1; $$->a2.p = $3; } + | term '<' term { $$ = f_new_inst(FI_LT); $$->a1.p = $1; $$->a2.p = $3; } + | term LEQ term { $$ = f_new_inst(FI_LTE); $$->a1.p = $1; $$->a2.p = $3; } + | term '>' term { $$ = f_new_inst(FI_LT); $$->a1.p = $3; $$->a2.p = $1; } + | term GEQ term { $$ = f_new_inst(FI_LTE); $$->a1.p = $3; $$->a2.p = $1; } + | term '~' term { $$ = f_new_inst(FI_MATCH); $$->a1.p = $1; $$->a2.p = $3; } + | term NMA term { $$ = f_new_inst(FI_NOT_MATCH);$$->a1.p = $1; $$->a2.p = $3; } + | '!' term { $$ = f_new_inst(FI_NOT); $$->a1.p = $2; } + | DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED); $$->a1.p = $3; } | symbol { $$ = $1; } | constant { $$ = $1; } | constructor { $$ = $1; } - | PREFERENCE { $$ = f_new_inst(); $$->code = 'P'; } + | PREFERENCE { $$ = f_new_inst(FI_PREF_GET); } - | rtadot static_attr { $$ = $2; $$->code = 'a'; } + | rtadot static_attr { $$ = f_new_inst_sa(FI_RTA_GET, $2); } - | rtadot dynamic_attr { $$ = $2; $$->code = P('e','a'); } + | rtadot dynamic_attr { $$ = f_new_inst_da(FI_EA_GET, $2); } - | term '.' IS_V4 { $$ = f_new_inst(); $$->code = P('I','i'); $$->a1.p = $1; } - | term '.' TYPE { $$ = f_new_inst(); $$->code = 'T'; $$->a1.p = $1; } - | term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p = $1; $$->aux = T_IP; } - | term '.' RD { $$ = f_new_inst(); $$->code = P('R','D'); $$->a1.p = $1; $$->aux = T_RD; } - | term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; } - | term '.' MAXLEN { $$ = f_new_inst(); $$->code = P('R','m'); $$->a1.p = $1; } - | term '.' ASN { $$ = f_new_inst(); $$->code = P('R','a'); $$->a1.p = $1; } - | term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; } - | term '.' FIRST { $$ = f_new_inst(); $$->code = P('a','f'); $$->a1.p = $1; } - | term '.' LAST { $$ = f_new_inst(); $$->code = P('a','l'); $$->a1.p = $1; } - | term '.' LAST_NONAGGREGATED { $$ = f_new_inst(); $$->code = P('a','L'); $$->a1.p = $1; } + | term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4); $$->a1.p = $1; } + | term '.' TYPE { $$ = f_new_inst(FI_TYPE); $$->a1.p = $1; } + | term '.' IP { $$ = f_new_inst(FI_IP); $$->a1.p = $1; $$->aux = T_IP; } + | term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER); $$->a1.p = $1; $$->aux = T_RD; } + | term '.' LEN { $$ = f_new_inst(FI_LENGTH); $$->a1.p = $1; } + | term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN); $$->a1.p = $1; } + | term '.' ASN { $$ = f_new_inst(FI_ROA_ASN); $$->a1.p = $1; } + | term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK); $$->a1.p = $1; $$->a2.p = $5; } + | term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST); $$->a1.p = $1; } + | term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST); $$->a1.p = $1; } + | term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG); $$->a1.p = $1; } /* Communities */ /* This causes one shift/reduce conflict @@ -904,19 +911,19 @@ term: | rtadot dynamic_attr '.' RESET{ } */ - | '+' EMPTY '+' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_PATH; } - | '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; } - | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_ECLIST; } - | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_LCLIST; } - | PREPEND '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('A','p'); $$->a1.p = $3; $$->a2.p = $5; } - | ADD '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; } - | DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; } - | FILTER '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; } + | '+' EMPTY '+' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_PATH; } + | '-' EMPTY '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_CLIST; } + | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_ECLIST; } + | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_LCLIST; } + | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND); $$->a1.p = $3; $$->a2.p = $5; } + | ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; } + | DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; } + | FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; } | ROA_CHECK '(' rtable ')' { $$ = f_generate_roa_check($3, NULL, NULL); } | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); } - | FORMAT '(' term ')' { $$ = f_new_inst(); $$->code = P('f','m'); $$->a1.p = $3; } + | FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT); $$->a1.p = $3; } /* | term '.' LEN { $$->code = P('P','l'); } */ @@ -927,8 +934,7 @@ term: if ($1->class != SYM_FUNCTION) cf_error("You can't call something which is not a function. Really."); DBG("You are calling function %s\n", $1->name); - $$ = f_new_inst(); - $$->code = P('c','a'); + $$ = f_new_inst(FI_CALL); $$->a1.p = inst; $$->a2.p = $1->def; sym = $1->aux2; @@ -953,7 +959,7 @@ break_command: ; print_one: - term { $$ = f_new_inst(); $$->code = 'p'; $$->a1.p = $1; $$->a2.p = NULL; } + term { $$ = f_new_inst(FI_PRINT); $$->a1.p = $1; $$->a2.p = NULL; } ; print_list: /* EMPTY */ { $$ = NULL; } @@ -967,15 +973,13 @@ print_list: /* EMPTY */ { $$ = NULL; } ; var_listn: term { - $$ = f_new_inst(); - $$->code = 's'; + $$ = f_new_inst(FI_SET); $$->a1.p = NULL; $$->a2.p = $1; $$->next = NULL; } | term ',' var_listn { - $$ = f_new_inst(); - $$->code = 's'; + $$ = f_new_inst(FI_SET); $$->a1.p = NULL; $$->a2.p = $1; $$->next = $3; @@ -988,73 +992,63 @@ var_list: /* EMPTY */ { $$ = NULL; } cmd: IF term THEN block { - $$ = f_new_inst(); - $$->code = '?'; + $$ = f_new_inst(FI_CONDITION); $$->a1.p = $2; $$->a2.p = $4; } | IF term THEN block ELSE block { - struct f_inst *i = f_new_inst(); - i->code = '?'; + struct f_inst *i = f_new_inst(FI_CONDITION); i->a1.p = $2; i->a2.p = $4; - $$ = f_new_inst(); - $$->code = '?'; + $$ = f_new_inst(FI_CONDITION); $$->a1.p = i; $$->a2.p = $6; } | SYM '=' term ';' { - $$ = f_new_inst(); DBG( "Ook, we'll set value\n" ); if (($1->class & ~T_MASK) != SYM_VARIABLE) cf_error( "You may set only variables." ); - $$->code = 's'; + $$ = f_new_inst(FI_SET); $$->a1.p = $1; $$->a2.p = $3; } | RETURN term ';' { - $$ = f_new_inst(); DBG( "Ook, we'll return the value\n" ); - $$->code = 'r'; + $$ = f_new_inst(FI_RETURN); $$->a1.p = $2; } | rtadot dynamic_attr '=' term ';' { - $$ = $2; - $$->code = P('e','S'); + $$ = f_new_inst_da(FI_EA_SET, $2); $$->a1.p = $4; } | rtadot static_attr '=' term ';' { - $$ = $2; + $$ = f_new_inst_sa(FI_RTA_SET, $2); if (!$$->a1.i) cf_error( "This static attribute is read-only."); - $$->code = P('a','S'); $$->a1.p = $4; } | PREFERENCE '=' term ';' { - $$ = f_new_inst(); - $$->code = P('P','S'); + $$ = f_new_inst(FI_PREF_SET); $$->a1.p = $3; } | UNSET '(' rtadot dynamic_attr ')' ';' { - $$ = $4; + $$ = f_new_inst_da(FI_EA_SET, $4); $$->aux = EAF_TYPE_UNDEF | EAF_TEMP; - $$->code = P('e','S'); $$->a1.p = NULL; } - | break_command print_list ';' { $$ = f_new_inst(); $$->code = P('p',','); $$->a1.p = $2; $$->a2.i = $1; } + | break_command print_list ';' { $$ = f_new_inst(FI_PRINT_AND_DIE); $$->a1.p = $2; $$->a2.i = $1; } | function_call ';' { $$ = $1; } | CASE term '{' switch_body '}' { - $$ = f_new_inst(); - $$->code = P('S','W'); + $$ = f_new_inst(FI_SWITCH); $$->a1.p = $2; $$->a2.p = build_tree( $4 ); } | rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); } - | rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( P('A','p'), 'x', $2, $6 ); } - | rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'a', $2, $6 ); } - | rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'd', $2, $6 ); } - | rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'f', $2, $6 ); } + | rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, 'x', $2, $6 ); } + | rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'a', $2, $6 ); } + | rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'd', $2, $6 ); } + | rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'f', $2, $6 ); } | BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); } ; diff --git a/filter/f-util.c b/filter/f-util.c index 52c13223..68aecd73 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -2,6 +2,7 @@ * Filters: utility functions * * Copyright 1998 Pavel Machek <pavel@ucw.cz> + * 2017 Jan Maria Matejka <mq@ucw.cz> * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -13,43 +14,48 @@ #define P(a,b) ((a<<8) | b) struct f_inst * -f_new_inst(void) +f_new_inst(enum f_instruction_code fi_code) { struct f_inst * ret; - ret = cfg_alloc(sizeof(struct f_inst)); - ret->code = ret->aux = 0; - ret->arg1 = ret->arg2 = ret->next = NULL; + ret = cfg_allocz(sizeof(struct f_inst)); + ret->fi_code = fi_code; ret->lineno = ifs->lino; return ret; } struct f_inst * -f_new_dynamic_attr(int type, int f_type, int code) +f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da) { - /* FIXME: Remove the f_type parameter? */ - struct f_inst *f = f_new_inst(); - f->aux = (f_type << 8) | type; - f->a2.i = code; - return f; + struct f_inst *ret = f_new_inst(fi_code); + ret->aux = (da.f_type << 8) | da.type; + ret->a2.i = da.ea_code; + return ret; +} + +struct f_inst * +f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa) +{ + struct f_inst *ret = f_new_inst(fi_code); + ret->aux = sa.f_type; + ret->a2.i = sa.sa_code; + ret->a1.i = sa.readonly; + return ret; } /* * Generate set_dynamic( operation( get_dynamic(), argument ) ) */ struct f_inst * -f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct f_inst *argument) +f_generate_complex(int operation, int operation_aux, struct f_dynamic_attr da, struct f_inst *argument) { - struct f_inst *set_dyn = f_new_inst(), - *oper = f_new_inst(), - *get_dyn = dyn; + struct f_inst *set_dyn = f_new_inst_da(FI_EA_SET, da), + *oper = f_new_inst(operation), + *get_dyn = f_new_inst_da(FI_EA_GET, da); - *set_dyn = *get_dyn; - get_dyn->code = P('e','a'); - oper->code = operation; oper->aux = operation_aux; oper->a1.p = get_dyn; oper->a2.p = argument; - set_dyn->code = P('e','S'); + set_dyn->a1.p = oper; return set_dyn; } @@ -58,7 +64,7 @@ struct f_inst * f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn) { struct f_inst_roa_check *ret = cfg_allocz(sizeof(struct f_inst_roa_check)); - ret->i.code = P('R','C'); + ret->i.fi_code = FI_ROA_CHECK; ret->i.lineno = ifs->lino; ret->i.arg1 = prefix; ret->i.arg2 = asn; diff --git a/filter/filter.c b/filter/filter.c index 8cf90b53..bb3146e7 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -48,8 +48,6 @@ #include "conf/conf.h" #include "filter/filter.h" -#define P(a,b) ((a<<8) | b) - #define CMP_ERROR 999 void (*bt_assert_hook)(int result, struct f_inst *assert); @@ -99,8 +97,7 @@ pm_format(struct f_path_mask *p, buffer *buf) break; case PM_ASN_EXPR: - buffer_print(buf, "%u ", f_eval_asn((struct f_inst *) p->val)); - break; + ASSERT(0); } p = p->next; @@ -462,7 +459,6 @@ val_in_range(struct f_val v1, struct f_val v2) if (((v1.type == T_PAIR) || (v1.type == T_QUAD)) && (v2.type == T_CLIST)) return int_set_contains(v2.val.ad, v1.val.i); - /* IP->Quad implicit conversion */ if (val_is_ip4(v1) && (v2.type == T_CLIST)) return int_set_contains(v2.val.ad, ipa_to_u32(v1.val.ip)); @@ -632,22 +628,16 @@ static struct f_val interpret(struct f_inst *what) { struct symbol *sym; - struct f_val v1, v2, res, *vp; + struct f_val v1, v2, res = { .type = T_VOID }, *vp; unsigned u1, u2; int i; u32 as; + for ( ; what; what = what->next) { res.type = T_VOID; - if (!what) - return res; - - switch(what->code) { - case ',': - TWOARGS; - break; - + switch(what->fi_code) { /* Binary operators */ - case '+': + case FI_ADD: TWOARGS_C; switch (res.type = v1.type) { case T_VOID: runtime( "Can't operate with values of type void" ); @@ -655,7 +645,7 @@ interpret(struct f_inst *what) default: runtime( "Usage of unknown type" ); } break; - case '-': + case FI_SUBTRACT: TWOARGS_C; switch (res.type = v1.type) { case T_VOID: runtime( "Can't operate with values of type void" ); @@ -663,7 +653,7 @@ interpret(struct f_inst *what) default: runtime( "Usage of unknown type" ); } break; - case '*': + case FI_MULTIPLY: TWOARGS_C; switch (res.type = v1.type) { case T_VOID: runtime( "Can't operate with values of type void" ); @@ -671,7 +661,7 @@ interpret(struct f_inst *what) default: runtime( "Usage of unknown type" ); } break; - case '/': + case FI_DIVIDE: TWOARGS_C; switch (res.type = v1.type) { case T_VOID: runtime( "Can't operate with values of type void" ); @@ -681,12 +671,12 @@ interpret(struct f_inst *what) } break; - case '&': - case '|': + case FI_AND: + case FI_OR: ARG(v1, a1.p); if (v1.type != T_BOOL) runtime( "Can't do boolean operation on non-booleans" ); - if (v1.val.i == (what->code == '|')) { + if (v1.val.i == (what->fi_code == FI_OR)) { res.type = T_BOOL; res.val.i = v1.val.i; break; @@ -699,7 +689,7 @@ interpret(struct f_inst *what) res.val.i = v2.val.i; break; - case P('m','p'): + case FI_PAIR_CONSTRUCT: TWOARGS; if ((v1.type != T_INT) || (v2.type != T_INT)) runtime( "Can't operate with value of non-integer type in pair constructor" ); @@ -711,7 +701,7 @@ interpret(struct f_inst *what) res.type = T_PAIR; break; - case P('m','c'): + case FI_EC_CONSTRUCT: { TWOARGS; @@ -757,7 +747,7 @@ interpret(struct f_inst *what) break; } - case P('m','l'): + case FI_LC_CONSTRUCT: { TWOARGS; @@ -775,6 +765,32 @@ interpret(struct f_inst *what) break; } + case FI_PATHMASK_CONSTRUCT: + { + struct f_path_mask *tt = what->a1.p, *vbegin, **vv = &vbegin; + + while (tt) { + *vv = lp_alloc(f_pool, sizeof(struct f_path_mask)); + if (tt->kind == PM_ASN_EXPR) { + struct f_val res = interpret((struct f_inst *) tt->val); + (*vv)->kind = PM_ASN; + if (res.type != T_INT) { + runtime( "Error resolving path mask template: value not an integer" ); + return (struct f_val) { .type = T_VOID }; + } + + (*vv)->val = res.val.i; + } else { + **vv = *tt; + } + tt = tt->next; + vv = &((*vv)->next); + } + + res = (struct f_val) { .type = T_PATH_MASK, .val.path_mask = vbegin }; + break; + } + /* Relational operators */ #define COMPARE(x) \ @@ -793,12 +809,12 @@ interpret(struct f_inst *what) res.val.i = (x); \ break; - case P('!','='): SAME(!i); - case P('=','='): SAME(i); - case '<': COMPARE(i==-1); - case P('<','='): COMPARE(i!=1); + case FI_NEQ: SAME(!i); + case FI_EQ: SAME(i); + case FI_LT: COMPARE(i==-1); + case FI_LTE: COMPARE(i!=1); - case '!': + case FI_NOT: ONEARG; if (v1.type != T_BOOL) runtime( "Not applied to non-boolean" ); @@ -806,7 +822,7 @@ interpret(struct f_inst *what) res.val.i = !res.val.i; break; - case '~': + case FI_MATCH: TWOARGS; res.type = T_BOOL; res.val.i = val_in_range(v1, v2); @@ -815,7 +831,7 @@ interpret(struct f_inst *what) res.val.i = !!res.val.i; break; - case P('!','~'): + case FI_NOT_MATCH: TWOARGS; res.type = T_BOOL; res.val.i = val_in_range(v1, v2); @@ -824,12 +840,12 @@ interpret(struct f_inst *what) res.val.i = !res.val.i; break; - case P('d','e'): + case FI_DEFINED: ONEARG; res.type = T_BOOL; res.val.i = (v1.type != T_VOID) && !undef_value(v1); break; - case 'T': + case FI_TYPE: ONEARG; switch (v1.type) { @@ -841,7 +857,7 @@ interpret(struct f_inst *what) runtime( "Can't determine type of this item" ); } break; - case P('I','i'): + case FI_IS_V4: ONEARG; if (v1.type != T_IP) runtime( "IP version check needs an IP address" ); @@ -850,7 +866,7 @@ interpret(struct f_inst *what) break; /* Set to indirect value, a1 = variable, a2 = value */ - case 's': + case FI_SET: ARG(v2, a2.p); sym = what->a1.p; vp = sym->def; @@ -869,7 +885,7 @@ interpret(struct f_inst *what) break; /* some constants have value in a2, some in *a1.p, strange. */ - case 'c': /* integer (or simple type) constant, string, set, or prefix_set */ + case FI_CONSTANT: /* integer (or simple type) constant, string, set, or prefix_set */ res.type = what->aux; if (res.type == T_PREFIX_SET) @@ -881,15 +897,15 @@ interpret(struct f_inst *what) else res.val.i = what->a2.i; break; - case 'V': - case 'C': + case FI_VARIABLE: + case FI_CONSTANT_INDIRECT: res = * ((struct f_val *) what->a1.p); break; - case 'p': + case FI_PRINT: ONEARG; val_format(v1, &f_buf); break; - case '?': /* ? has really strange error value, so we can implement if ... else nicely :-) */ + case FI_CONDITION: /* ? has really strange error value, so we can implement if ... else nicely :-) */ ONEARG; if (v1.type != T_BOOL) runtime( "If requires boolean expression" ); @@ -899,10 +915,10 @@ interpret(struct f_inst *what) } else res.val.i = 1; res.type = T_BOOL; break; - case '0': + case FI_NOP: debug( "No operation\n" ); break; - case P('p',','): + case FI_PRINT_AND_DIE: ONEARG; if ((what->a2.i == F_NOP || (what->a2.i != F_NONL && what->a1.p)) && !(f_flags & FF_SILENT)) @@ -925,7 +941,7 @@ interpret(struct f_inst *what) bug( "unknown return type: Can't happen"); } break; - case 'a': /* rta access */ + case FI_RTA_GET: /* rta access */ { ACCESS_RTE; struct rta *rta = (*f_rte)->attrs; @@ -948,7 +964,7 @@ interpret(struct f_inst *what) } } break; - case P('a','S'): + case FI_RTA_SET: ACCESS_RTE; ONEARG; if (what->aux != v1.type) @@ -1000,7 +1016,7 @@ interpret(struct f_inst *what) } } break; - case P('e','a'): /* Access to extended attributes */ + case FI_EA_GET: /* Access to extended attributes */ ACCESS_RTE; { eattr *e = NULL; @@ -1094,7 +1110,7 @@ interpret(struct f_inst *what) } } break; - case P('e','S'): + case FI_EA_SET: ACCESS_RTE; ONEARG; { @@ -1198,12 +1214,12 @@ interpret(struct f_inst *what) } } break; - case 'P': + case FI_PREF_GET: ACCESS_RTE; res.type = T_INT; res.val.i = (*f_rte)->pref; break; - case P('P','S'): + case FI_PREF_SET: ACCESS_RTE; ONEARG; if (v1.type != T_INT) @@ -1213,7 +1229,7 @@ interpret(struct f_inst *what) f_rte_cow(); (*f_rte)->pref = v1.val.i; break; - case 'L': /* Get length of */ + case FI_LENGTH: /* Get length of */ ONEARG; res.type = T_INT; switch(v1.type) { @@ -1225,7 +1241,7 @@ interpret(struct f_inst *what) default: runtime( "Prefix, path, clist or eclist expected" ); } break; - case P('R','m'): /* Get ROA max prefix length */ + case FI_ROA_MAXLEN: /* Get ROA max prefix length */ ONEARG; if (v1.type != T_NET || !net_is_roa(v1.val.net)) runtime( "ROA expected" ); @@ -1235,7 +1251,7 @@ interpret(struct f_inst *what) ((net_addr_roa4 *) v1.val.net)->max_pxlen : ((net_addr_roa6 *) v1.val.net)->max_pxlen; break; - case P('R','a'): /* Get ROA ASN */ + case FI_ROA_ASN: /* Get ROA ASN */ ONEARG; if (v1.type != T_NET || !net_is_roa(v1.val.net)) runtime( "ROA expected" ); @@ -1245,14 +1261,14 @@ interpret(struct f_inst *what) ((net_addr_roa4 *) v1.val.net)->asn : ((net_addr_roa6 *) v1.val.net)->asn; break; - case P('c','p'): /* Convert prefix to ... */ + case FI_IP: /* Convert prefix to ... */ ONEARG; if (v1.type != T_NET) runtime( "Prefix expected" ); res.type = T_IP; res.val.ip = net_prefix(v1.val.net); break; - case P('R','D'): + case FI_ROUTE_DISTINGUISHER: ONEARG; if (v1.type != T_NET) runtime( "Prefix expected" ); @@ -1261,7 +1277,7 @@ interpret(struct f_inst *what) res.type = T_RD; res.val.ec = net_rd(v1.val.net); break; - case P('a','f'): /* Get first ASN from AS PATH */ + case FI_AS_PATH_FIRST: /* Get first ASN from AS PATH */ ONEARG; if (v1.type != T_PATH) runtime( "AS path expected" ); @@ -1271,7 +1287,7 @@ interpret(struct f_inst *what) res.type = T_INT; res.val.i = as; break; - case P('a','l'): /* Get last ASN from AS PATH */ + case FI_AS_PATH_LAST: /* Get last ASN from AS PATH */ ONEARG; if (v1.type != T_PATH) runtime( "AS path expected" ); @@ -1281,7 +1297,7 @@ interpret(struct f_inst *what) res.type = T_INT; res.val.i = as; break; - case P('a','L'): /* Get last ASN from non-aggregated part of AS PATH */ + case FI_AS_PATH_LAST_NAG: /* Get last ASN from non-aggregated part of AS PATH */ ONEARG; if (v1.type != T_PATH) runtime( "AS path expected" ); @@ -1289,23 +1305,23 @@ interpret(struct f_inst *what) res.type = T_INT; res.val.i = as_path_get_last_nonaggregated(v1.val.ad); break; - case 'r': + case FI_RETURN: ONEARG; res = v1; res.type |= T_RETURN; return res; - case P('c','a'): /* CALL: this is special: if T_RETURN and returning some value, mask it out */ + case FI_CALL: /* CALL: this is special: if T_RETURN and returning some value, mask it out */ ONEARG; res = interpret(what->a2.p); if (res.type == T_RETURN) return res; res.type &= ~T_RETURN; break; - case P('c','v'): /* Clear local variables */ + case FI_CLEAR_LOCAL_VARS: /* Clear local variables */ for (sym = what->a1.p; sym != NULL; sym = sym->aux2) ((struct f_val *) sym->def)->type = T_VOID; break; - case P('S','W'): + case FI_SWITCH: ONEARG; { struct f_tree *t = find_tree(what->a2.p, v1); @@ -1324,7 +1340,7 @@ interpret(struct f_inst *what) return res; } break; - case P('i','M'): /* IP.MASK(val) */ + case FI_IP_MASK: /* IP.MASK(val) */ TWOARGS; if (v2.type != T_INT) runtime( "Integer expected"); @@ -1337,11 +1353,11 @@ interpret(struct f_inst *what) ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i))); break; - case 'E': /* Create empty attribute */ + case FI_EMPTY: /* Create empty attribute */ res.type = what->aux; res.val.ad = adata_empty(f_pool, 0); break; - case P('A','p'): /* Path prepend */ + case FI_PATH_PREPEND: /* Path prepend */ TWOARGS; if (v1.type != T_PATH) runtime("Can't prepend to non-path"); @@ -1352,7 +1368,7 @@ interpret(struct f_inst *what) res.val.ad = as_path_prepend(f_pool, v1.val.ad, v2.val.i); break; - case P('C','a'): /* (Extended) Community list add or delete */ + case FI_CLIST_ADD_DEL: /* (Extended) Community list add or delete */ TWOARGS; if (v1.type == T_PATH) { @@ -1518,8 +1534,7 @@ interpret(struct f_inst *what) break; - - case P('R','C'): /* ROA Check */ + case FI_ROA_CHECK: /* ROA Check */ if (what->arg1) { TWOARGS; @@ -1559,14 +1574,14 @@ interpret(struct f_inst *what) break; - case P('f','m'): /* Format */ + case FI_FORMAT: /* Format */ ONEARG; res.type = T_STRING; res.val.s = val_format_str(v1); break; - case P('a','s'): /* Birdtest Assert */ + case FI_ASSERT: /* Birdtest Assert */ ONEARG; if (v1.type != T_BOOL) @@ -1579,10 +1594,8 @@ interpret(struct f_inst *what) break; default: - bug( "Unknown instruction %d (%c)", what->code, what->code & 0xff); - } - if (what->next) - return interpret(what->next); + bug( "Unknown instruction %d (%c)", what->fi_code, what->fi_code & 0xff); + }} return res; } @@ -1609,40 +1622,40 @@ i_same(struct f_inst *f1, struct f_inst *f2) return 1; if (f1->aux != f2->aux) return 0; - if (f1->code != f2->code) + if (f1->fi_code != f2->fi_code) return 0; if (f1 == f2) /* It looks strange, but it is possible with call rewriting trickery */ return 1; - switch(f1->code) { - case ',': /* fall through */ - case '+': - case '-': - case '*': - case '/': - case '|': - case '&': - case P('m','p'): - case P('m','c'): - case P('!','='): - case P('=','='): - case '<': - case P('<','='): TWOARGS; break; - - case '!': ONEARG; 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'): + switch(f1->fi_code) { + case FI_ADD: /* fall through */ + case FI_SUBTRACT: + case FI_MULTIPLY: + case FI_DIVIDE: + case FI_OR: + case FI_AND: + case FI_PAIR_CONSTRUCT: + case FI_EC_CONSTRUCT: + case FI_NEQ: + case FI_EQ: + case FI_LT: + case FI_LTE: TWOARGS; break; + + case FI_PATHMASK_CONSTRUCT: if (!pm_same(f1->a1.p, f2->a1.p)) return 0; break; + + case FI_NOT: ONEARG; break; + case FI_NOT_MATCH: + case FI_MATCH: TWOARGS; break; + case FI_DEFINED: ONEARG; break; + case FI_TYPE: ONEARG; break; + + case FI_LC_CONSTRUCT: TWOARGS; if (!i_same(INST3(f1).p, INST3(f2).p)) return 0; break; - case 's': + case FI_SET: ARG(v2, a2.p); { struct symbol *s1, *s2; @@ -1655,7 +1668,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) } break; - case 'c': + case FI_CONSTANT: switch (f1->aux) { case T_PREFIX_SET: @@ -1678,44 +1691,44 @@ i_same(struct f_inst *f1, struct f_inst *f2) } break; - case 'C': + case FI_CONSTANT_INDIRECT: if (!val_same(* (struct f_val *) f1->a1.p, * (struct f_val *) f2->a1.p)) return 0; break; - case 'V': + case FI_VARIABLE: if (strcmp((char *) f1->a2.p, (char *) f2->a2.p)) return 0; break; - case 'p': case 'L': ONEARG; break; - case '?': TWOARGS; break; - case '0': case 'E': break; - case P('p',','): ONEARG; A2_SAME; break; - case 'P': - case 'a': A2_SAME; break; - case P('e','a'): A2_SAME; break; - case P('P','S'): - case P('a','S'): - case P('e','S'): ONEARG; A2_SAME; break; - - case 'r': ONEARG; break; - case P('c','p'): ONEARG; break; - case P('R','D'): ONEARG; break; - case P('c','a'): /* Call rewriting trickery to avoid exponential behaviour */ + case FI_PRINT: case FI_LENGTH: ONEARG; break; + case FI_CONDITION: TWOARGS; break; + case FI_NOP: case FI_EMPTY: break; + case FI_PRINT_AND_DIE: ONEARG; A2_SAME; break; + case FI_PREF_GET: + case FI_RTA_GET: A2_SAME; break; + case FI_EA_GET: A2_SAME; break; + case FI_PREF_SET: + case FI_RTA_SET: + case FI_EA_SET: ONEARG; A2_SAME; break; + + case FI_RETURN: ONEARG; break; + case FI_IP: ONEARG; break; + case FI_ROUTE_DISTINGUISHER: ONEARG; break; + case FI_CALL: /* Call rewriting trickery to avoid exponential behaviour */ ONEARG; if (!i_same(f1->a2.p, f2->a2.p)) return 0; f2->a2.p = f1->a2.p; break; - case P('c','v'): break; /* internal instruction */ - case P('S','W'): ONEARG; if (!same_tree(f1->a2.p, f2->a2.p)) return 0; break; - case P('i','M'): TWOARGS; break; - case P('A','p'): TWOARGS; break; - case P('C','a'): TWOARGS; break; - case P('a','f'): - case P('a','l'): - case P('a','L'): ONEARG; break; - case P('R','C'): + case FI_CLEAR_LOCAL_VARS: break; /* internal instruction */ + case FI_SWITCH: ONEARG; if (!same_tree(f1->a2.p, f2->a2.p)) return 0; break; + case FI_IP_MASK: TWOARGS; break; + case FI_PATH_PREPEND: TWOARGS; break; + case FI_CLIST_ADD_DEL: TWOARGS; break; + case FI_AS_PATH_FIRST: + case FI_AS_PATH_LAST: + case FI_AS_PATH_LAST_NAG: ONEARG; break; + case FI_ROA_CHECK: TWOARGS; /* Does not really make sense - ROA check results may change anyway */ if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name, @@ -1723,7 +1736,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) return 0; break; default: - bug( "Unknown instruction %d in same (%c)", f1->code, f1->code & 0xff); + bug( "Unknown instruction %d in same (%c)", f1->fi_code, f1->fi_code & 0xff); } return i_same(f1->next, f2->next); } @@ -1853,14 +1866,6 @@ f_eval_int(struct f_inst *expr) return res.val.i; } -u32 -f_eval_asn(struct f_inst *expr) -{ - /* Called as a part of another interpret call, therefore no log_reset() */ - struct f_val res = interpret(expr); - return (res.type == T_INT) ? res.val.i : 0; -} - /** * filter_same - compare two filters * @new: first filter to be compared diff --git a/filter/filter.h b/filter/filter.h index 49004c33..d347924a 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -14,9 +14,74 @@ #include "nest/route.h" #include "nest/attrs.h" +/* Filter instruction types */ + +#define FI__TWOCHAR(a,b) ((a<<8) | b) +#define FI__LIST \ + F(FI_ADD, 0, '+') \ + F(FI_SUBTRACT, 0, '-') \ + F(FI_MULTIPLY, 0, '*') \ + F(FI_DIVIDE, 0, '/') \ + F(FI_AND, 0, '&') \ + F(FI_OR, 0, '|') \ + F(FI_PAIR_CONSTRUCT, 'm', 'p') \ + F(FI_EC_CONSTRUCT, 'm', 'c') \ + F(FI_LC_CONSTRUCT, 'm', 'l') \ + F(FI_PATHMASK_CONSTRUCT, 'm', 'P') \ + F(FI_NEQ, '!', '=') \ + F(FI_EQ, '=', '=') \ + F(FI_LT, 0, '<') \ + F(FI_LTE, '<', '=') \ + F(FI_NOT, 0, '!') \ + F(FI_MATCH, 0, '~') \ + F(FI_NOT_MATCH, '!', '~') \ + F(FI_DEFINED, 'd', 'e') \ + F(FI_TYPE, 0, 'T') \ + F(FI_IS_V4, 'I', 'i') \ + F(FI_SET, 0, 's') \ + F(FI_CONSTANT, 0, 'c') \ + F(FI_VARIABLE, 0, 'V') \ + F(FI_CONSTANT_INDIRECT, 0, 'C') \ + F(FI_PRINT, 0, 'p') \ + F(FI_CONDITION, 0, '?') \ + F(FI_NOP, 0, '0') \ + F(FI_PRINT_AND_DIE, 'p', ',') \ + F(FI_RTA_GET, 0, 'a') \ + F(FI_RTA_SET, 'a', 'S') \ + F(FI_EA_GET, 'e', 'a') \ + F(FI_EA_SET, 'e', 'S') \ + F(FI_PREF_GET, 0, 'P') \ + F(FI_PREF_SET, 'P', 'S') \ + F(FI_LENGTH, 0, 'L') \ + F(FI_ROA_MAXLEN, 'R', 'M') \ + F(FI_ROA_ASN, 'R', 'A') \ + F(FI_IP, 'c', 'p') \ + F(FI_ROUTE_DISTINGUISHER, 'R', 'D') \ + F(FI_AS_PATH_FIRST, 'a', 'f') \ + F(FI_AS_PATH_LAST, 'a', 'l') \ + F(FI_AS_PATH_LAST_NAG, 'a', 'L') \ + F(FI_RETURN, 0, 'r') \ + F(FI_CALL, 'c', 'a') \ + F(FI_CLEAR_LOCAL_VARS, 'c', 'V') \ + F(FI_SWITCH, 'S', 'W') \ + F(FI_IP_MASK, 'i', 'M') \ + F(FI_EMPTY, 0, 'E') \ + F(FI_PATH_PREPEND, 'A', 'p') \ + F(FI_CLIST_ADD_DEL, 'C', 'a') \ + F(FI_ROA_CHECK, 'R', 'C') \ + F(FI_FORMAT, 0, 'F') \ + F(FI_ASSERT, 'a', 's') + +enum f_instruction_code { +#define F(c,a,b) \ + c = FI__TWOCHAR(a,b), +FI__LIST +#undef F +} PACKED; + struct f_inst { /* Instruction */ struct f_inst *next; /* Structure is 16 bytes, anyway */ - u16 code; /* Instruction code, see the interpret() function and P() macro */ + enum f_instruction_code fi_code; u16 aux; /* Extension to instruction code, T_*, EA_*, EAF_* */ union { uint i; @@ -70,15 +135,32 @@ struct f_val { } val; }; +struct f_dynamic_attr { + int type; + int f_type; + int ea_code; +}; + +struct f_static_attr { + int f_type; + int sa_code; + int readonly; +}; + struct filter { char *name; struct f_inst *root; }; -struct f_inst *f_new_inst(void); -struct f_inst *f_new_dynamic_attr(int type, int f_type, int code); /* Type as core knows it, type as filters know it, and code of dynamic attribute */ +struct f_inst *f_new_inst(enum f_instruction_code fi_code); +struct f_inst *f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da); +struct f_inst *f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa); +static inline struct f_dynamic_attr f_new_dynamic_attr(int type, int f_type, int code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */ +{ return (struct f_dynamic_attr) { .type = type, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */ +static inline struct f_static_attr f_new_static_attr(int f_type, int code, int readonly) +{ return (struct f_static_attr) { .f_type = f_type, .sa_code = code, .readonly = readonly }; } struct f_tree *f_new_tree(void); -struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct f_inst *argument); +struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_dynamic_attr da, struct f_inst *argument); struct f_inst *f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn); @@ -100,7 +182,6 @@ int f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, s struct f_val f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool); struct f_val f_eval(struct f_inst *expr, struct linpool *tmp_pool); uint f_eval_int(struct f_inst *expr); -u32 f_eval_asn(struct f_inst *expr); char *filter_name(struct filter *filter); int filter_same(struct filter *new, struct filter *old); diff --git a/filter/test.conf2 b/filter/test.conf2 index 2a5d896b..48515020 100644 --- a/filter/test.conf2 +++ b/filter/test.conf2 @@ -55,29 +55,28 @@ protocol static { rip_metric = rip_metric + 5; print rip_metric; -# -# TODO: uncomment this part after finishing BGP integration version -# -# bgp_community = -empty-; -# print "hi"; -# bgp_community = add(bgp_community, (1,2)); -# print "hello"; -# bgp_community = add(bgp_community, (2,3)); -# bgp_community.add((4,5)); -# print "community = ", bgp_community; -# bgp_community.delete((2,3)); -# print "community = ", bgp_community; -# bgp_community.empty; -# print "community = ", bgp_community; -# print "done"; + bgp_community = -empty-; + print "hi"; + bgp_community = add(bgp_community, (1,2)); + print "hello"; + bgp_community = add(bgp_community, (2,3)); + bgp_community.add((4,5)); + print "community = ", bgp_community; + bgp_community.delete((2,3)); + print "community = ", bgp_community; + bgp_community.empty; + print "community = ", bgp_community; + print "done"; + + accept; }; }; route 0.0.0.0/0 via 195.113.31.113; route 62.168.0.0/25 reject; route 1.2.3.4/32 via 195.113.31.124; route 10.0.0.0/8 reject; - route 10.1.1.0:255.255.255.0 via 62.168.0.3; - route 10.1.2.0:255.255.255.0 via 62.168.0.3; - route 10.1.3.0:255.255.255.0 via 62.168.0.4; + route 10.1.1.0/24 via 62.168.0.3; + route 10.1.2.0/24 via 62.168.0.3; + route 10.1.3.0/24 via 62.168.0.4; route 10.2.0.0/24 via "arc0"; } diff --git a/misc/bird.spec b/misc/bird.spec index 7b645655..bf5c14bb 100644 --- a/misc/bird.spec +++ b/misc/bird.spec @@ -1,6 +1,6 @@ Summary: BIRD Internet Routing Daemon Name: bird -Version: 2.0.1 +Version: 2.0.2 Release: 1 Copyright: GPL Group: Networking/Daemons diff --git a/nest/a-path.c b/nest/a-path.c index c0d16c30..6bad9747 100644 --- a/nest/a-path.c +++ b/nest/a-path.c @@ -805,8 +805,7 @@ as_path_match(const struct adata *path, struct f_path_mask *mask) val2 = val = mask->val; goto step; case PM_ASN_EXPR: - val2 = val = f_eval_asn((struct f_inst *) mask->val); - goto step; + ASSERT(0); case PM_ASN_RANGE: val = mask->val; val2 = mask->val2; diff --git a/nest/rt-dev.c b/nest/rt-dev.c index 718c4578..66f458e7 100644 --- a/nest/rt-dev.c +++ b/nest/rt-dev.c @@ -31,6 +31,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad) struct rt_dev_proto *p = (void *) P; struct rt_dev_config *cf = (void *) P->cf; struct channel *c; + net_addr *net = &ad->prefix; if (!EMPTY_LIST(cf->iface_list) && !iface_patt_find(&cf->iface_list, ad->iface, ad)) @@ -53,13 +54,20 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad) if (!c) return; + /* For IPv6 SADR, replace regular prefix with SADR prefix */ + if (c->net_type == NET_IP6_SADR) + { + net = alloca(sizeof(net_addr_ip6_sadr)); + net_fill_ip6_sadr(net, net6_prefix(&ad->prefix), net6_pxlen(&ad->prefix), IP6_NONE, 0); + } + if (flags & IF_CHANGE_DOWN) { DBG("dev_if_notify: %s:%I going down\n", ad->iface->name, ad->ip); /* Use iface ID as local source ID */ struct rte_src *src = rt_get_source(P, ad->iface->index); - rte_update2(c, &ad->prefix, NULL, src); + rte_update2(c, net, NULL, src); } else if (flags & IF_CHANGE_UP) { @@ -85,7 +93,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad) a = rta_lookup(&a0); e = rte_get_temp(a); e->pflags = 0; - rte_update2(c, &ad->prefix, e, src); + rte_update2(c, net, e, src); } } @@ -107,16 +115,32 @@ dev_if_notify(struct proto *p, uint c, struct iface *iface) } } +static void +dev_postconfig(struct proto_config *CF) +{ + struct rt_dev_config *cf = (void *) CF; + struct channel_config *ip4, *ip6, *ip6_sadr; + + ip4 = proto_cf_find_channel(CF, NET_IP4); + ip6 = proto_cf_find_channel(CF, NET_IP6); + ip6_sadr = proto_cf_find_channel(CF, NET_IP6_SADR); + + if (ip6 && ip6_sadr) + cf_error("Both ipv6 and ipv6-sadr channels"); + + cf->ip4_channel = ip4; + cf->ip6_channel = ip6 ?: ip6_sadr; +} static struct proto * dev_init(struct proto_config *CF) { struct proto *P = proto_new(CF); struct rt_dev_proto *p = (void *) P; - // struct rt_dev_config *cf = (void *) CF; + struct rt_dev_config *cf = (void *) CF; - proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)); - proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6)); + proto_configure_channel(P, &p->ip4_channel, cf->ip4_channel); + proto_configure_channel(P, &p->ip6_channel, cf->ip6_channel); P->if_notify = dev_if_notify; P->ifa_notify = dev_ifa_notify; @@ -136,8 +160,8 @@ dev_reconfigure(struct proto *P, struct proto_config *CF) return 0; return - proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)) && - proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6)); + proto_configure_channel(P, &p->ip4_channel, n->ip4_channel) && + proto_configure_channel(P, &p->ip6_channel, n->ip6_channel); return 1; } @@ -162,9 +186,10 @@ struct protocol proto_device = { .name = "Direct", .template = "direct%d", .preference = DEF_PREF_DIRECT, - .channel_mask = NB_IP, + .channel_mask = NB_IP | NB_IP6_SADR, .proto_size = sizeof(struct rt_dev_proto), .config_size = sizeof(struct rt_dev_config), + .postconfig = dev_postconfig, .init = dev_init, .reconfigure = dev_reconfigure, .copy_config = dev_copy_config diff --git a/nest/rt-dev.h b/nest/rt-dev.h index 20b88a64..5d91242a 100644 --- a/nest/rt-dev.h +++ b/nest/rt-dev.h @@ -13,6 +13,9 @@ struct rt_dev_config { struct proto_config c; list iface_list; /* list of struct iface_patt */ int check_link; + + struct channel_config *ip4_channel; + struct channel_config *ip6_channel; }; struct rt_dev_proto { diff --git a/nest/rt-table.c b/nest/rt-table.c index 686d0e84..b885c6e3 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -2076,6 +2076,13 @@ rt_unlock_table(rtable *r) } } +static struct rtable_config * +rt_find_table_config(struct config *cf, char *name) +{ + struct symbol *sym = cf_find_symbol(cf, name); + return (sym && (sym->class == SYM_TABLE)) ? sym->def : NULL; +} + /** * rt_commit - commit new routing table configuration * @new: new configuration @@ -2101,11 +2108,10 @@ rt_commit(struct config *new, struct config *old) rtable *ot = o->table; if (!ot->deleted) { - struct symbol *sym = cf_find_symbol(new, o->name); - if (sym && sym->class == SYM_TABLE && !new->shutdown) + r = rt_find_table_config(new, o->name); + if (r && (r->addr_type == o->addr_type) && !new->shutdown) { DBG("\t%s: same\n", o->name); - r = sym->def; r->table = ot; ot->name = r->name; ot->config = r; diff --git a/proto/babel/config.Y b/proto/babel/config.Y index 25ce5ba0..7adfb4bb 100644 --- a/proto/babel/config.Y +++ b/proto/babel/config.Y @@ -22,9 +22,10 @@ CF_DEFINES CF_DECLS -CF_KEYWORDS(BABEL, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT, WIRED, - WIRELESS, RX, TX, BUFFER, LENGTH, CHECK, LINK, BABEL_METRIC, NEXT, HOP, - IPV4, IPV6) +CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT, + TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK, + NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS, + ENTRIES) CF_GRAMMAR diff --git a/sysdep/config.h b/sysdep/config.h index a552e6b6..393f0281 100644 --- a/sysdep/config.h +++ b/sysdep/config.h @@ -13,7 +13,7 @@ #ifdef GIT_LABEL #define BIRD_VERSION XSTR1(GIT_LABEL) #else -#define BIRD_VERSION "2.0.1" +#define BIRD_VERSION "2.0.2" #endif /* Include parameters determined by configure script */ |