Compare commits

...

199 Commits

Author SHA1 Message Date
256e198fee
add drone pipeline
Some checks reported errors
continuous-integration/drone/push Build is passing
continuous-integration/drone Build encountered an error
2021-12-30 13:19:02 +00:00
18853a82c6
Reset Extendend Length flag when encoding BGP attributes 2021-03-27 14:29:55 +00:00
e6133456c1
Bump version in build script 2021-03-22 09:09:19 +00:00
b86d3f9a2e
Revert "Add patch from JRB0001 to reload protocols on RPKI change"
This reverts commit 3155bb34bed49c909f00e5d3aadc9a74c2e6ef5c.
2021-03-22 09:05:39 +00:00
910f9127f0
Add additional error information on NETLINK failures 2021-03-22 09:03:24 +00:00
ce7a2736e9
add tag to build 2021-03-22 09:03:24 +00:00
397c52070a
Add build script 2021-03-22 09:03:24 +00:00
Simon Marsh
4aecc5f8cd
Add patch from JRB0001 to provide more verbose error logging on bad next hop. 2021-03-22 09:03:23 +00:00
Simon Marsh
3155bb34be
Add patch from JRB0001 to reload protocols on RPKI change 2021-03-22 09:03:23 +00:00
Ondrej Zajicek (work)
82f19ba95e NEWS and version update 2021-03-18 20:18:38 +01:00
Ondrej Zajicek (work)
f1ffe6a231 Add new BGP tests 2021-03-18 15:54:44 +01:00
Ondrej Zajicek (work)
5a6e8380f8 BGP: Do not show statistics
BGP statistics code was preliminary and i wanted to replace it by
separate 'show X stats' command. The patch hides the preliminary
output in 'show protocols all' so it is not part of the released
version.
2021-03-18 15:44:04 +01:00
Ondrej Zajicek (work)
454ae30445 RPKI: Improve error handling of DNS resolver 2021-03-17 17:24:00 +01:00
Ondrej Zajicek (work)
0a3db4c680 Minor fixes for restricted builds 2021-03-17 15:56:12 +01:00
Ondrej Zajicek (work)
2f98153490 Pipe: Propagate debug flags from protocol to channels
Pipe channels are kind-of implicit, so setting protocol debug flags
should also set pipe debug flags.
2021-03-16 20:10:00 +01:00
Ondrej Zajicek (work)
ae9ae864d3 OSPFv3: Update neighbor authentication state from Hello packets
In OSPFv3, only Hello and DBDes packets contain flags specifying whether
RFC 7166 authentication trailer is used. Other packets are processed
based on stored authentication state in neighbor structure. Update this
state with each received Hello to handle authentication change from
reconfigurations.

Thanks to Joakim Tjernlund and Kenth Eriksson for the bugreport.
2021-03-16 16:34:42 +01:00
Ondrej Zajicek (work)
94abefc00b Filter: Update 'gw' to handle IPv6 link-local addresses
When a link-local address is set, use the existing iface for scope.

Thanks to Marcel Krüger for the bugreport.
2021-03-15 18:37:18 +01:00
Ondrej Zajicek (work)
0d1a11cca3 Doc: Document automatic RPKI reload 2021-03-15 17:51:33 +01:00
Ondrej Zajicek (work)
6489a2450e Doc: Document channel debug options 2021-03-15 16:16:32 +01:00
Ondrej Zajicek (work)
7be3af7fa6 Rate-limit scheduling of work-events
In general, events are code handling some some condition, which is
scheduled when such condition happened and executed independently from
I/O loop. Work-events are a subgroup of events that are scheduled
repeatedly until some (often significant) work is done (e.g. feeding
routes to protocol). All scheduled events are executed during each
I/O loop iteration.

Separate work-events from regular events to a separate queue and
rate limit their execution to a fixed number per I/O loop iteration.
That should prevent excess latency when many work-events are
scheduled at one time (e.g. simultaneous reload of many BGP sessions).
2021-03-12 15:35:56 +01:00
Ondrej Zajicek (work)
9cf3d53311 Static: Implement reload hook 2021-03-10 15:07:19 +01:00
Ondrej Zajicek (work)
211fe69c98 Nest: No automatic ROA reload on non-reloadable channels 2021-03-09 18:37:52 +01:00
Ondrej Zajicek (work)
d3782c72b9 Nest: Add option to control automatic RPKI reload
Also, no automatic reload for BGP channels without import/export table.
2021-02-12 05:05:18 +01:00
Ondrej Zajicek (work)
77ce849ecf Tests: Add missing mockup function to tests 2021-02-10 17:29:14 +01:00
Vincent Bernat
714238716e BGP: Add support for BGP hostname capability
This is an implementation of draft-walton-bgp-hostname-capability-02.
It is implemented since quite some time for FRR and in datacenter, this
gives a nice output to avoid using IP addresses.

It is disabled by default. The hostname is retrieved from uname(2) and
can be overriden with "hostname" option. The domain name is never set
nor displayed.

Minor changes by committer.
2021-02-10 16:53:57 +01:00
Ondrej Zajicek (work)
00b85905b9 Nest: Automatic channel reloads based on RPKI changes
If there are roa_check() calls in channel filters, then the channel
subscribes to ROA table notifications, which are sent when ROA tables
are updated (subject to settle time) and trigger channel reload or
refeed.
2021-02-10 03:09:57 +01:00
Ondrej Zajicek (work)
d06a875b04 Filter: Recursive filter iteration code
Add macros for recursive filter iteration that allows to examine
all instructions reachable from a filter.
2021-02-07 19:21:42 +01:00
Ondrej Zajicek (work)
5d414309ec MRT: Fix MP-BGP next hops
Flag signalling that MP-BGP mode should be used got reset after first
batch of routes, so remaining routes were processed without that, leading
to missing MP_REACH_NLRI attribute.

Thanks to Piotr Wydrych for the bugreport.
2021-01-22 04:34:15 +01:00
Ondrej Zajicek (work)
df83f62697 Netlink: Ignore dead routes
With net.ipv4.conf.XXX.ignore_routes_with_linkdown sysctl, a user can
ensure the kernel does not use a route whose target interface is down.
Such route is marked with a 'dead' / RTNH_F_DEAD flag.

Ignore these routes or multipath nexthops during scan.

Thanks to Vincent Bernat for the original patch.
2021-01-14 02:01:07 +01:00
Ondrej Zajicek (work)
a40ddf5c61 Build: Fix tags generation 2021-01-12 15:43:54 +01:00
Ondrej Zajicek (work)
d774f6d721 MRT: Fix IPv6 table dumps
Add fake MP_REACH_NLRI attribute with BGP next hop when encoding MRT
table dumps for IPv6 routes. That is necessary to encode next hop as
NEXT_HOP attribute is not used for MP-BGP.

Thanks to Santiago Aggio for the bugreport.
2021-01-12 15:37:01 +01:00
Ondrej Zajicek (work)
910adaa08b BFD: Dispatch sessions also by interface index
Direct BFD sessions needs to be dispatched not only by IP addresses, but
also by interfaces, in order to avoid collisions between neighbors with
the same IPv6 link-local addresses.

Extend BFD session hash_ip key by interface index to handle that. Use 0
for multihop sessions.

Thanks to Sebastian Hahn for the original patch.
2021-01-10 15:29:02 +01:00
Ondrej Zajicek (work)
17663b6a7c RPKI: Remove port (and SSH username) from 'Cache server' output line
It was mixed-up if hostname is IPv6 address, and reporting separate
values (like port) on separate lines fits better into key-value style
of 'show protocols all' output. Also, the patch simplifies transport
identification formatting (although it is unused now).

Thanks to Alarig Le Lay for the suggestion.
2021-01-07 06:04:31 +01:00
Ondrej Zajicek (work)
2a8cc7259e Kernel: Do not check templates
So one can define kernel protocol template without channels.
For other protocols, it is either irrelevant or already done.

Thanks to Clemens Schrimpe for the bugreport.
2021-01-07 01:56:00 +01:00
Ondrej Zajicek (work)
a141959f07 Doc: Describe per-nexthop static route options
Also remove description of (no longer supported) per-route 'bfd' option,
and add examples of IPv6 routes with link-local nexthops.
2021-01-07 01:20:56 +01:00
Ondrej Zajicek (work)
7a1f4baac1 Nest: remove last_tx_filter_change
No longer needed after redesign of export handling.
2021-01-06 14:51:49 +01:00
Ondrej Zajicek (work)
4155104c90 BGP: Deprecate 'missing lladdr' option
The option is not implemented since transition to 2.0 and no plan to add it.
Also remove some deprecated RTS_* valus from documentation.

Thanks to Sébastien Parisot for notification.
2021-01-06 14:44:23 +01:00
Ondrej Zajicek (work)
21f9acd2a0 Kernel: Fix handling of krt_realm with ECMP routes
For ECMP routes, RTA_FLOW attribute must be set per-nexthop, not
per-route. Our corresponding krt_realm attribute is per-route.

Thanks to Mikhail Petrov for the bugreport.
2021-01-06 05:25:59 +01:00
James Lu
455c13dc99 Nest: Read Babel metric as IGP metric
(Minor syntactic changes by committer)
2020-12-29 02:25:21 +01:00
Ondrej Zajicek (work)
ea3c6c1a15 Static: Fix handling of 'net' attribute in per-route filters
We need to define 'net' field temporarily as it may be accessed by
per-route filters.

Thanks to Damian Zaremba for the bugreport.
2020-12-28 21:19:27 +01:00
Ondrej Zajicek (work)
9e2635505a Filter: Fix return on top-level
Broken detection of top-level case caused crash when return was called
from top-of-stack position. It should behave as reject/accept.

Thanks to Damian Zaremba for the bugreport.
2020-12-28 15:23:28 +01:00
Ondrej Zajicek (work)
61dae32b29 Nest: Per-channel debug flags
The patch add support for per-channel debug flags, currently just
'states', 'routes', and 'filters'. Flag 'states' is used for channel
state changes, remaining two for routes passed through the channel.
The per-protocol debug flags 'routes'/'filters' still enable reporting
of routes for all channels, to keep existing behavior.

The patch causes minor changes in some log messages.
2020-12-07 22:19:40 +01:00
Ondrej Zajicek (work)
8cc5bb09e3 Filter: Add 'weight' route attribute
Add 'weight' route attribute that allows to get and set ECMP weight of
nexthops. Similar to 'gw' attribute, it is limited to the first nexthop,
but it is useful for handling BGP multipath, where an ECMP route is
merged from multiple regular routes.
2020-12-02 05:02:26 +01:00
Ondrej Zajicek (work)
2465867712 BGP: Zero the newly allocated bucket structure
This fixes an issue with dirty node passed to add_tail().

Thanks to Andreas Rammhold for the initial patch.
2020-11-25 15:48:22 +01:00
Ondrej Zajicek (work)
62d57b9bdf Log: Fix locking during log reconfiguration
The log subsystem should be locked earlier, as default_log_list() may
internally manipulate with the current_log_list (if it is also a default
log list).
2020-11-25 15:15:13 +01:00
Ondrej Zajicek (work)
0ef082c51e Log: Reinitialize the static logging structures
The static logging structures are reused, we need to reinitialize them
otherwise add_tail() would fail in debug build. Reinitializing these
structures should be fine as the list they belong to is being
reinitialized on entry to the very same function.

Thanks to Andreas Rammhold and Mikael Magnusson for patches.
2020-11-25 15:04:34 +01:00
Ondrej Zajicek (work)
30b8468269 Minor cleanups with cfg_allocz()
Also fixes some more failed asserts due to add_tail().
2020-11-24 04:09:11 +01:00
Ondrej Zajicek (work)
1678bc0746 Fix some failed asserts due to add_tail()
When config structures are copied due to template application,
we need to reset list node structure before calling add_tail().

Thanks to Mikael Magnusson for patches.
2020-11-24 03:42:23 +01:00
Ondrej Zajicek (work)
c9ae81656f Some minor sl_allocz() cleanups 2020-11-24 03:21:44 +01:00
Toke Høiland-Jørgensen
db2d29073a lib/slab: introduce sl_allocz() function and use it in Babel
The babel protocol code was initialising objects returned from the slab
allocator by assigning to each of the struct members individually, but
wasn't touching the NODE member while doing so. This leads to warnings on
debug builds since commit:

baac7009063d ("List expensive check.")

To fix this, introduce an sl_allocz() variant of the slab allocator which
will zero out the memory before returning it, and switch all the babel call
sites to use this version. The overhead for doing this should be negligible
for small objects, and in the case of babel, the largest object being
allocated was being zeroed anyway, so we can drop the memset in
babel_read_tlv().
2020-11-24 02:36:31 +01:00
Ondrej Zajicek (work)
3347aaafec Static: Support for multiple routes with the same network
Add support for proper handling of multiple routes with the same network
to the static protocol. Routes are distinguished by internal index, which
is assigned automatically (sequentially for routes within each network).
Having different route preference or igp_metric attribute is optional.
2020-11-19 16:38:39 +01:00
Nigel Kukard
df65d519d6 Doc: Added example of static routes with BGP large communities 2020-11-18 18:00:12 +01:00
Ondrej Zajicek (work)
00ddd18b02 OSPFv3: Fix intra-area-prefix-LSA origination on DR
When a new link-LSA is originated, we need to notify intra-area-prefix-LSA
handling, like when a new link-LSA is received. Otherwise a new network
prefix added to a DR is not propagated immediately.

Thanks to Bala Sajja for the bugreport.
2020-11-18 17:37:29 +01:00
Ondrej Zajicek (work)
6ea8a46ccb Doc: Fix typo
Thanks to Hexhu for the bugreport.
2020-11-15 16:28:13 +01:00
Ondrej Zajicek (work)
b962967e20 Nest: Fix crash in receive limit handling in import table
Logging as a result of triggered receive limit in import table code
accesset rte->net, which was not filed yet.

Thanks to Pier Carlo Chiodi for the bugreport.
2020-11-15 16:01:19 +01:00
Ondrej Zajicek (work)
4a42e7e925 BFD: Update documentation about per-session options 2020-11-12 04:50:45 +01:00
Ondrej Zajicek (work)
3b56bf8849 BFD: Better handling of BFD options in BGP configs
Merge multiple BFD option blocks in BGP configs instead of using the last
one. That is necessary for proper handling of templates when BFD options
are used both in a BGP template and in a BGP protocol derived from that
template.
2020-11-12 04:02:38 +01:00
Ondrej Zajicek (work)
99ad208dd7 BFD: Fix superfluous reconfiguration of sessions 2020-11-12 02:48:35 +01:00
Ondrej Zajicek (work)
9d3fc3062b BFD: Allow per-request session options
BFD session options are configured per interface in BFD protocol. This
patch allows to specify them also per-request in protocols requesting
sessions (currently limited to BGP).
2020-11-08 15:33:22 +01:00
Ondrej Zajicek (work)
fc1e3211b1 RPKI: Add 'ignore max length' option
Add 'ignore max length' option to RPKI protocol, which ignores received
max length in ROA records and instead uses max value (32 or 128). This
may be useful for implementing loose RPKI check for blackholes.
2020-10-11 01:00:54 +02:00
Ondrej Zajicek (work)
6c11dbcf28 Doc: Fix missing semicolons
Thanks to Marco Gartmann for the bugreport.
2020-10-05 14:52:55 +02:00
Ondrej Zajicek (work)
14ce8904e7 Doc: Fix typo
Thanks to Sergey Kulikov for the bugreport.
2020-10-05 14:45:01 +02:00
Maria Matejka
600eb695b1 OSPF: Fixed a debug assert 2020-08-31 15:41:39 +02:00
Ondrej Zajicek (work)
dc8d9dec4a OSPF: Skip out-of-state packets earlier
Sometimes multicast OSPF packet is received when neighbor adjacency is
not established. Such packet should be ignored earlier in packet
processing as otherwise it causes strange error messages when OSPFv3
authentication is enabled.
2020-08-12 19:42:44 +02:00
Ondrej Zajicek (work)
c0e1f534c9 Nest: Keep route ordering during route updates
Put new non-best routes to the end of list instead of the second
position. Put updated routes to their old position. Position is changed
just by best route selection.
2020-07-16 15:02:10 +02:00
Ondrej Zajicek (work)
c26c6bc2d7 Show info from multiple protocols when protocol is not specified
Most commands like 'show ospf neighbors' fail when protocol is not
specified and there are multiple instances of given protocol type.
This is annoying in BIRD 2, as many protocols have IPv4 and IPv6
instances. The patch changes that by showing output from all protocol
instances of appropriate type.

Note that the patch also removes terminating cli_msg() call from these
commands and moves it to the common iterating code.
2020-06-28 15:38:47 +02:00
Kazuki Yamaguchi
a948cf9a5c Filter: Improve handling of sets in BGP path masks
Compare the content of PM_ASN_SET in path masks. A reconfiguration
was not properly triggering a reload of affected protocols when the
members of a set in a path mask change.

Also, update the printing code to so that it can display sets in a path
mask.
2020-06-28 15:37:01 +02:00
Kazuki Yamaguchi
4ef0a96639 Filter: Fix comparison of BGP path mask
Add a missing return statement. Path masks with the same length were all
considered the same. Comparing two with different length would cause
out-of-bounds memory access.
2020-06-28 15:33:26 +02:00
Ondrej Zajicek (work)
82937b465b OSPF: Fix bad header length test
Thanks to Slava Aseev for the thorough bugreport.
2020-06-10 13:27:14 +02:00
Kenth Eriksson
71e08edd94 Doc: Add 'ptp address' to OSPF doc overview 2020-06-03 23:05:29 +02:00
Ondrej Zajicek (work)
63451c1961 Test: Fix unit test mockups 2020-06-03 16:15:29 +02:00
Kazuki Yamaguchi
f1b5f179db Netlink: Fix parsing of MPLS multipath routes
Add support for RTA_MULTIPATH attribute parsing for AF_MPLS routes.

BIRD is capable of installing a multipath route into kernel on Linux,
but it would not be seen because parsing fails. This made BIRD attempt
to install the same route repeatedly.

(The patch minorly updated by committer)
2020-06-03 15:18:02 +02:00
Kazuki Yamaguchi
19f8f17320 RPKI: Fix unnecessary reconnection on reconfiguration
Compare the new timing parameters with the old configuration, not with
the temporary state of the current connection.

The timing values in struct rpki_cache is updated by a version 1 End Of
Data PDU, unless this behavior is suppressed by the configuration
explicitly by the "keep" keyword. Consequently, every reconfiguration
of BIRD triggers a reconnection even if it is not necessary.
2020-06-03 15:05:35 +02:00
Ondrej Zajicek (work)
fae5448134 Log: Do not open logfiles when parse-and-exit option is active
This is a quick workaround for an issue where configured logfiles are
opened/created during parsing of a config file even when parse-and-exit
option is active. We should later refactor the logging code to avoid
opening log during parsing altogether.
2020-06-03 14:59:20 +02:00
Maria Matejka
eee8af4db2 OSPF: setting list node to zero before enlisting 2020-06-02 16:58:06 +02:00
Ondrej Zajicek (work)
4e8f8afc68 Babel: Set onlink flag for IPv4 routes with unreachable next hop
If the next hop of a route is not a reachable address, the route should be
installed as onlink. This enables a configuration common in mesh networks
where the mesh interface is assigned a /32 and babel handles the routing by
installing onlink routes.

Thanks to Toke Hoiland-Jorgensen for the patch.
2020-05-26 23:43:13 +02:00
Ondrej Zajicek (work)
c1632ad0f3 OSPF: Fix handling of unnumbered PtPs
This issue has a long history. In 2012, we changed data field for
unnumbered PtP links from iface id (specified by RFC) to IP address based
on reports of bugs in Quagga that required it, and we used out-of-band
information to distinquish unnumberred PtPs with the same local IP
address.

Then with OSPF graceful restart implementation, we found that we can no
longer use out-of-band information, and we need to use only LSAdb info
for routing table calculation, but i forgot to finish handling of this
case, so multiple unnumbered PtPs with the same local IP addresses were
broken.

Considering that even recent Mikrotik RouterOS has broken next hop
calculation that depends on IP address in PtP link data field, we
cannot just switch back to the iface id for unnumbered PtP links.

The patch makes two changes: First, it goes back to use out-of-band
(position) info for distinguishing local interfaces in SPF when graceful
restart is not enabled, while still uses LSAdb-only approach for SPF
calculation when graceful restart is enabled.

Second, it adds OSPF interface option 'ptp address', which controls
whether IP address or iface id is used in data field. It is enabled
by default except for unnumbered PtP links with enabled graceful
restart.

Thanks to Kenth Eriksson for the bugreport and Joakim Tjernlund for
suggestions.
2020-05-26 18:21:43 +02:00
Ondrej Zajicek (work)
1ca7665fa4 Nest: Allow key id 0
There is nothing in RFCs specifying that id 0 is not allowed. Some
implementations does not support it, while some other use key id 0 by
default. We allow it but start with key id 1 by default.

Thanks to Kenth Eriksson for the bugreport.
2020-05-19 02:50:47 +02:00
Ondrej Zajicek (work)
b729e731f9 RIP: Triggered RIP (demand circuit) documentation 2020-05-19 02:42:22 +02:00
Ondrej Zajicek (work)
ec430a7fee Nest: Implement BGP path mask loop operator
Implement regex-like '+' operator in BGP path masks to match previous
path mask item multiple times. This is useful as ASNs may appear
multiple times in paths due to path prepending for traffic engineering
purposes.
2020-05-18 16:25:08 +02:00
Ondrej Zajicek (work)
5fc8407177 RIP: Fix handling of passive mode for demand circuit interfaces 2020-05-12 03:46:47 +02:00
Ondrej Zajicek (work)
b8bbbbaf56 Nest: Fix neighbor handling for colliding ranges
Resolve neighbors using longest prefix match. Although interface ranges
should not generally collide, it may happen for unnumbered links.

Thanks to Kenth Eriksson for the bugreport.
2020-05-11 04:29:36 +02:00
Ondrej Zajicek (work)
f7c34aa227 Tests: Activate BGP-int test 2020-05-05 02:20:30 +02:00
Matous Holinka
e6785c469b Tests: Change unsupported Ubuntu 19.04 for supported version 19.10 2020-05-05 02:16:28 +02:00
Ondrej Zajicek (work)
82bfee76f0 Filter: Remove quitbird command
No need for this debug filter command and it can be abused from CLI.
2020-05-02 02:47:18 +02:00
Maria Matejka
b12442c985 Fixed a harmless warning in production build 2020-05-01 15:41:42 +02:00
Maria Matejka
048eb2ddf1 Merge remote-tracking branch 'origin/mq-static-analysis' 2020-05-01 15:34:17 +02:00
Maria Matejka
59238768b3 Slab: Init node in slab head to NULLs. 2020-05-01 15:19:12 +02:00
Maria Matejka
ea259d6201 Timer: Adding missing initializer. 2020-05-01 15:19:12 +02:00
Maria Matejka
0c3b8ffe25 Lexer: strtoul shall never set endptr to NULL; it should be an error 2020-05-01 15:19:12 +02:00
Maria Matejka
cdde3550dc Unix socket: Path length check directly before copying the path.
This is not needed as the string is always short enough, anyway
it may be needed in future and one strlen during BIRD start is
cheap enough.
2020-05-01 15:19:12 +02:00
Maria Matejka
9ac13d7af2 Lists: Replaced replace_node() by update_node() which is the only use of that function. 2020-05-01 15:19:12 +02:00
Maria Matejka
e26a5195dd Lists: fix a stupid sanitizer bug 2020-05-01 15:19:12 +02:00
Maria Matejka
3bb10b4d31 Uninitialized list nodes fixes 2020-05-01 15:19:12 +02:00
Maria Matejka
258be56539 Nest: Added const to ea_show just to declare that this shouldn't really change anything 2020-05-01 15:19:12 +02:00
Maria Matejka
a7d9b8f116 OSPF: Zero-initialization of a temporary neighbor 2020-05-01 15:19:12 +02:00
Maria Matejka
0fa8bf91cd Nest: Several assumptions to tame the static analyzer 2020-05-01 15:19:12 +02:00
Maria Matejka
bbe49ae569 Nest: Assumption in rt-show for not-so-intuitive invariant. 2020-05-01 15:19:12 +02:00
Maria Matejka
a08853a269 Static scanner and expensive debugging setup fix 2020-05-01 15:19:12 +02:00
Maria Matejka
5f60d14ede RPKI: fixed rare va_list leak 2020-05-01 15:19:12 +02:00
Maria Matejka
b748220906 Static check: Don't report dead code 2020-05-01 15:19:12 +02:00
Maria Matejka
9e64ac4b7c OSPF: Adding a note about a static analyzer result. 2020-05-01 15:19:12 +02:00
Maria Matejka
dccee40826 OSPF: variable-length array of size 0 replaced by alloca()'d pointer
NULL pointer is safer than a random pointer onto stack if this function
gets changed and eventually broken.
2020-05-01 15:19:12 +02:00
Maria Matejka
baac700906 List expensive check. 2020-05-01 15:19:12 +02:00
Maria Matejka
a0d0a71a18 Expensive check declaration
Intended to be run at every operation with complex data structures
to check their consistency and validity.
2020-05-01 15:19:12 +02:00
Maria Matejka
a1b61a271a IPv6 address parser: fail on incomplete addresses 2020-05-01 15:19:12 +02:00
Maria Matejka
d65a926a67 Filter: Don't alloc varargs array if its length would be zero 2020-05-01 15:19:12 +02:00
Maria Matejka
30ba7c1661 Filter: Removed forgotten dead code 2020-05-01 15:19:12 +02:00
Maria Matejka
bf9486bf20 Non-null function argument declaration 2020-05-01 15:18:48 +02:00
Ondrej Zajicek (work)
17de3a023f BGP: Fix handling of strange IPv6 link-local-only next hops
There are three common ways how to encode IPv6 link-local-only next hops:
(:: ll), (ll), and (ll ll). We use the first one but we should accept all
three. The patch fixes handling of the last one.

Thanks to Sebastian Hahn for the bugreport.
2020-04-29 02:50:29 +02:00
Maria Matejka
8029ae527e More assertion categories 2020-04-28 16:21:06 +02:00
Maria Matejka
d607205486 Not calling memcpy with n=0. 2020-04-28 16:21:06 +02:00
Maria Matejka
124d860f64 Filter: fixed omitted overflow check in EC constructor 2020-04-28 16:21:06 +02:00
Maria Matejka
59a86cbc7c Makefile rule for static analyzer 2020-04-28 16:21:06 +02:00
Ondrej Zajicek (work)
b465604eb1 Tests: Activate BGP-auth test 2020-04-28 15:26:26 +02:00
Ondrej Zajicek (work)
716e11a584 Tests: Activate OSPF-VRF test 2020-04-28 03:52:47 +02:00
Ondrej Zajicek (work)
3c838ad9fd Tests: Activate BGP test 2020-04-22 17:14:02 +02:00
Nasato Goto
a6548d5b5b BGP: Fix handling of 16bit-only ASN translation
The bug generated invalid AGGREGATOR attribute during translation of
32bit ASN to 16bit-only BGP peer. The patch fixes that.
2020-04-15 03:46:53 +02:00
Maria Matejka
fd9f0c0640 Configuration strings are constant.
This is merely a const propagation. There was no problem in there.
2020-04-09 15:37:14 +02:00
Ondrej Zajicek (work)
a109056145 Doc: Update prefix set comment 2020-04-08 13:11:51 +02:00
Maria Matejka
2928c5bcc7 Fletcher16 test fixed to work at bigendian architectures.
To be honest, it was wrong in concept, anyway it accidentally worked.
2020-04-05 01:15:26 +02:00
Ondrej Zajicek (work)
c9d11e6230 Filter: Remove mixed address tests and fix formatting 2020-03-26 04:59:15 +01:00
Ondrej Zajicek (work)
2755002890 Filter: Optimize IPv4 prefix sets
Use separate IPv4 and IPv6 implementation of prefix sets. Just this
change makes IPv4 prefix sets 60% smaller and 50% faster.
2020-03-26 03:57:48 +01:00
Ondrej Zajicek (work)
d516c68ad8 RIP: Improvements to demand circuit mode
Restart iface after changing demand circuit mode during reconfiguration.
Fix next_regular interval reset during reconfiguration. Send flushing
response when iface goes down.
2020-03-14 17:04:49 +01:00
Maria Matejka
dc042d87cb Perf: changed route update pattern to be more like common protocols 2020-03-12 09:26:05 +01:00
Ondrej Zajicek (work)
e2630a494e Netlink: Handle interfaces with missing broadcast addresses 2020-03-07 05:11:21 +01:00
Ondrej Zajicek (work)
1ad98965c5 Tests: Enforce cleanup before running a test 2020-03-05 22:01:30 +01:00
Ondrej Zajicek (work)
ead531ffef Tests: Activate OSPF tests 2020-03-05 17:39:52 +01:00
Ondrej Zajicek (work)
e6746da6de Flowspec: Fix tests
Missing dst no longer generates error.
2020-03-03 19:04:33 +01:00
Ondrej Zajicek (work)
78e4a123bb BGP: Handle flowspec rules without dst part
The RFC 5575 does not explicitly reject flowspec rules without dst part,
it just requires dst part in validation procedure for feasibility, which
we do not implement anyway. Thus flow without dst prefix is syntactically
valid, but unfeasible (if feasibilty testing is done).

Thanks to Alex D. for the bugreport.
2020-03-03 17:45:16 +01:00
Ondrej Zajicek (work)
757cab18d6 BGP: Support for MD5SIG together with remote range
When dynamic BGP with remote range is configured, MD5SIG needs to use
newer socket option (TCP_MD5SIG_EXT) to specify remote addres range for
listening socket.

Thanks to Adam Kułagowski for the suggestion.
2020-02-27 17:29:17 +01:00
Ondrej Zajicek (work)
22c3cf955d RIP: Demand circuit support (RFC 2091) 2020-02-21 02:35:50 +01:00
Ondrej Zajicek (work)
3343088a71 RIP: Fix crash when interface is removed
Recent changes in neighbor code caused RIP to access neighbor field which
is NULL during interface/neighbor removal and caused crash when debug
messages are enabled. Use correct field to get iface from neighbor.
2020-02-14 22:43:27 +01:00
Maria Matejka
ab089f4fb5 Conf: Better error message when reading iproute2 config
Reported by: Martin Weinelt <martin@darmstadt.freifunk.net>
2020-02-04 10:34:46 +01:00
Maria Matejka
027a3e66f7 RPKI: Allow build without libSSH 2020-02-04 10:15:35 +01:00
Maria Matejka
4bbc10614f Added missing extern
Thanks to Robert Scheck <bird@robert-scheck.de> who reported it
and Toke Høiland-Jørgensen <toke@toke.dk> who suggested this patch.
2020-02-04 10:11:16 +01:00
Ondrej Zajicek (work)
7f9adafc10 BFD: Option to specify which class of BFD sessions are accepted
Allows to configure IPv4/IPv6-only or direct/multihop-only BFD protocol
instances.
2020-01-28 18:07:25 +01:00
Ondrej Zajicek (work)
9f2670277c OSPF: Fix bad initialization of tx_hdrlen field
Function ifa_tx_hdrlen() uses fields autype and passwords, so it must be
called after these are set.

Thanks to Kenth Eriksson for the bugreport.
2020-01-09 03:02:15 +01:00
Ondrej Zajicek (work)
7d767c5a3d KRT: Improve syncer code to avoid using temporary data in rtable
The old code stored route verdicts and temporary routes directly in
rtable. The new code do not store received routes (it immediately
compares them with exported routes and resolves conflicts) and uses
internal bitmap to keep track of which routes were received and which
needs to be reinstalled.

By not putting 'invalid' temporary routes to rtable, we keep rtable
in consistent state, therefore scan no longer needs to be atomic
operation and could be splitted to multiple events.
2020-01-07 18:35:03 +01:00
Ondrej Zajicek (work)
ef8c45749c Filter: Fix typecheck for AND/OR.
Do not apply dynamic type check for second argument of AND/OR, as it is
not evaluated immediately like regular argument would be.

Thanks to Mikael for the bugreport.
2020-01-07 01:24:30 +01:00
Ondrej Zajicek (work)
cc75b3e1dc KRT: Remove KRF_SYNC_ERROR flag
This info is now stored in an internal bmap. Unfortunately, net.flags
is still needed for temporary kernel data.
2019-12-19 16:34:35 +01:00
Ondrej Zajicek (work)
90a9c97e38 KRT: Fix removal of KRF_INSTALLED
Use route id from net->routes to check export_map. Route received from
sysdep KRT code does not have proper id.
2019-12-17 16:30:29 +01:00
Ondrej Zajicek (work)
3dabf7b8d0 Test: Improve filter_test
Initial parsing of test.conf must be done directly in filter_test main,
while reconfiguration is handled as a regular test. Also fix several
minor issues in test code.
2019-12-17 00:01:53 +01:00
Ondrej Zajicek (work)
3232d17186 Doc: Fix documentation of BGP gateway option
Thanks to Nico Schottelius for the bugreport.
2019-12-16 18:08:40 +01:00
Ondrej Zajicek (work)
c132acae36 KRT: Remove KRF_INSTALLED flag
The same information is stored in export_map of kernel protocol.
2019-12-16 02:42:24 +01:00
Maria Matejka
d3aa4f2aed Filter: fix filter comparison test 2019-12-12 15:42:46 +01:00
Ondrej Zajicek (work)
dfb3eb7716 Filter: Fix function comparison
Check the SYM_FLAG_SAME in new symbols. The old code checked that
in old symbols (f2).
2019-12-10 18:53:16 +01:00
Ondrej Zajicek (work)
4ab54f1aef Nest: Fix bitmap cleanup
Channel currently does not have independent pool and uses protocol pool,
which is freed when protocol changes state to down, while channel is
still in flushing. Move some some cleanup code to channel_do_flush()
so it is done before freeing of protocol pool.
2019-12-10 18:18:02 +01:00
Ondrej Zajicek (work)
ff2ca10cba Filter: Add support for src/dst accessors for Flowspec and SADR 2019-12-09 04:23:01 +01:00
Ondrej Zajicek (work)
21d09632a5 BGP: Add some statistics
Add some statistic counters to BGP consistent with BGP MIB (RFC 4273),
including persistent 'FSM established transitions'.
2019-12-03 18:05:41 +01:00
Matous Holinka
92249894b3 CI: Add more build tests
Add more Docker images with distributions (CentOS 8, Debian 10,
Fedora 27-31, OpenSUSE 15.0 & 15.1, and Ubuntu 18.04 & 19.04).
Fix some issues with older ones.
2019-11-26 19:43:56 +01:00
Ondrej Zajicek (work)
0adfa0ec07 CI: Cleanup of job templates
Env templates were used for separate IPv4/IPv6 build, that is no longer
needed.
2019-11-26 19:43:56 +01:00
Ondrej Zajicek (work)
6a314d26cb CI: Update new netlab location 2019-11-26 19:43:56 +01:00
Ondrej Zajicek (work)
148bd9ee92 CI: Minor update 2019-11-26 19:43:56 +01:00
Ondrej Zajicek (work)
faa43a755e Apply relevant changes from branch mh-test-gitlab 2019-11-26 19:43:56 +01:00
Ondrej Zajicek (work)
5176455f1a Gitlab test 2019-11-26 19:43:56 +01:00
Ondrej Zajicek (work)
5ea39eaa96 Nest: Use bitmaps to keep track of exported routes
Use a hierarchical bitmap in a routing table to assign ids to routes, and
then use bitmaps (indexed by route id) in channels to keep track whether
routes were exported. This avoids unreliable and inefficient re-evaluation
of filters for old routes in order to determine whether they were exported.
2019-11-26 18:39:25 +01:00
Ondrej Zajicek (work)
af02b83b88 Lib: Basic and hierarchical bitmaps
Basic bitmap is obvious. Hierarchical bitmap is structure of several
bitmaps, where higher levels are conjunctions of intervals on level
below, allowing for efficient lookup of first unset bit.
2019-11-26 18:39:02 +01:00
Ondrej Zajicek (work)
d033e6327d CLI: Fix continuation lines after final one
Continuation lines may use short form (with space instead of message
number), but this should not be done when previous line is final.

Thanks to Kenth Eriksson for the bugreport and analysis.
2019-11-26 16:43:09 +01:00
Ondrej Zajicek (work)
0f88200247 BGP: Fix processing of IPv6 Flowspec
During NLRI parsing of IPv6 Flowspec, dst prefix was not properly
extracted from NLRI, therefore a received flow was stored in a different
position in flowspec routing table, and was not reachable by command
'show route <flow>'.

Add proper prefix part accessors to flowspec code and use them from BGP
NLRI parsing code.

Thanks to Alex D. for the bugreport.
2019-11-18 17:56:51 +01:00
Ondrej Zajicek
53401bef63 Netlink: Handle IPv4 routes with IPv6 nexthops
Accept RTA_VIA attribute in all cases. The old code always used
RTA_GATEWAY for IPv4 / IPv6 and RTA_VIA for MPLS. The new code uses
RTA_VIA in cases where AF of network and AF of nexthop differs.
2019-11-12 18:13:21 +01:00
Ondrej Zajicek (work)
0b228fca04 BGP: Add option to enforce first AS in AS_PATH
This is optional check described in RFC 4271. Although this can be also
done by filters, it is widely implemented option in BGP implementations.

Thanks to Eugene Bogomazov for the original patch.
2019-11-10 02:06:07 +01:00
Ondrej Zajicek (work)
becda5638a Doc: Minor fix 2019-11-05 16:29:47 +01:00
Ondrej Zajicek (work)
d54a69ac7f Doc: Add documentation for BGP option 'allow as sets' 2019-11-05 16:00:25 +01:00
Ondrej Zajicek (work)
10c4cd9677 Filter: Add type info for more instructions 2019-11-05 15:30:20 +01:00
Ondrej Zajicek (work)
87512e9751 Filter: Improve typecheck error messages 2019-11-05 15:30:16 +01:00
Ondrej Zajicek (work)
c00c20a799 Filter: Better constant promotion
We use constant promotion from IPv4 to Router-ID values, as they have
same literals. Instead of ad-hoc code in filter instructions, add
constant promotion code to parse-time typecheck code.
2019-11-05 15:28:47 +01:00
Ondrej Zajicek (work)
26194bd684 Filter: Improved parse-time typechecks 2019-11-05 15:28:47 +01:00
Ondrej Zajicek
6fbcd8914a Filter: Parse-time typechecks
Most expressions can be type-validated in parse time. It is not
strong enough to eliminate runtime checks, but at least one gets
errors immediately during reconfigure.
2019-11-05 15:28:47 +01:00
Ondrej Zajicek (work)
a52476c9be BGP: Add option to reject AS_SETs
There is a pending draft to make them obsolete
2019-11-04 22:09:35 +01:00
Ondrej Zajicek (work)
0edf0c8cd9 Support for address family constants
We already had them defined on BGP level, but they are more general.
2019-11-03 22:25:44 +01:00
Ondrej Zajicek (work)
08c4c9a30b Nest: Fix bug in export table
For regular channels do not compare src in export table, as we want to
keep here only the best (exported) route per network.
2019-11-03 20:25:42 +01:00
Ondrej Zajicek (work)
be7c1aef42 BGP: RFC 8654 got released 2019-10-26 01:32:24 +02:00
Ondrej Zajicek (work)
498d8145c0 Nest: Fix primary flag in show route
The route is changed by rte_make_tmp_attrs(), so we need to compare
net->routes to the original one.

Thanks to Kenth Eriksson for the bugreport.
2019-10-25 13:28:51 +02:00
Ondrej Zajicek (work)
ec331acf48 BGP: Fix handling of transitive extended communities
Transitive extended communities should be removed on external sessions,
the old code them in all cases.

Thanks to Jean-Daniel Pauget for the original patch.
2019-10-24 17:50:19 +02:00
Ondrej Zajicek
5ce881be82 Accept uppercase letters in iproute2 names
Names read from texfiles in /etc/iproute2/* are normalized by replacing
non-alphanumeric chars with underscore. The patch fixes handling of
uppercase letters, which were handled as non-alphanumberic.

Thanks to Igor Gavrilov for the bugreport.
2019-10-22 16:25:38 +02:00
Fabrice Fontaine
f9eb9b4cab Nest: Fix build without protocols
(CHECK keyword added by commiter)
2019-10-19 12:50:27 +02:00
Ondrej Zajicek (work)
4e23b49969 RPKI: Fix handling of IPv6 cache addresses
The old code used just sizeof(struct sockaddr) bytes of IP address.
2019-10-19 03:39:07 +02:00
Ondrej Zajicek (work)
b000a94275 NEWS and version update 2019-10-11 00:18:38 +02:00
Ondrej Zajicek (work)
6c9cda6f92 BGP: Fix reconfiguration with import table
Change of some options requires route refresh, but when import table is
active, channel reload is done from it instead of doing full route
refresh. So in this case we request it internally.
2019-10-10 23:33:40 +02:00
Ondrej Zajicek (work)
eeb2c61653 Doc: Minor documentation fixes
Thanks to Christoph for the bugreport.
2019-10-10 22:43:41 +02:00
Ondrej Zajicek (work)
843b10c8b0 Nest: Handle non-MPLS on MPLS case in recursive route update
When non-MPLS recursive route resolves to MPLS underlying route,
then it should get MPLS labels from the the underlying route.
2019-10-10 15:25:36 +02:00
Ondrej Zajicek (work)
9eace84342 Nest: Handle PtP links in recursive route update
Underlying (IGP) route may lead to PtP link, in this case it does not
need gateway. Which is different than direct route without gateway.

When recursive (BGP) route uses PtP route, it should not use recursive
next hop as immediate next hop, while for direct routes it should.
2019-10-10 15:06:32 +02:00
Ondrej Zajicek (work)
cb2b6e0494 Nest: Fix recursive route update
Missing cleanup can lead to dangling pointer to old next hops.
2019-10-10 14:01:16 +02:00
Ondrej Zajicek (work)
09ee846d92 BGP: AIGP metric support (RFC 7311) 2019-10-09 17:53:23 +02:00
Ondrej Zajicek (work)
759b204be3 Lib: Support for 64-bit numbers in bvsnprintf()
Use 'l' for s64/u64 instead of for long/ulong, as that is much more
useful. Also make number() correct with regard to signed/unsigned
typecasts.
2019-10-09 17:53:23 +02:00
Maria Matejka
cc95b4594a Build: Pass -g to cc called as linker to explicitly keep debug info 2019-10-09 17:47:14 +02:00
Maria Matejka
d6eea6caee Testing measures times 2019-10-09 17:47:14 +02:00
Maria Matejka
368f70604f LTO: debug info also kept with the final binary 2019-10-09 17:47:14 +02:00
Maria Matejka
6dda6931d1 Perf: allow testing with cached route attributes. 2019-10-09 17:47:14 +02:00
Ondrej Zajicek
15a7583787 Doc: Fix duplicated lines
Thanks to elados93 for the patch.
2019-10-08 14:20:25 +02:00
Maria Matejka
c41a914d6e Testing: Don't call vsnprintf with NULL format 2019-10-04 20:52:07 +02:00
Maria Matejka
24493e9169 Fixed undefined behavior on signals.
The C11 specification allows only sig_atomic_t and _Atomic variable
access. All other accesses to global variables are undefined behavior.

Using int was probably OK on x86 and x86_64; yet there were some reports
from other architectures (especially some MIPS) that in rare cases,
after issuing SIGHUP, BIRD did strange things.
2019-10-04 20:52:07 +02:00
Ondrej Zajicek (work)
4821251ebb BFD: Fix reconfiguration of neighbors
The bfd_reconfigure_neighbors() returned after first reconfigured
neighbor instead of continuing with the next one.

Thanks to Winston Chen for the bugreport and a patch.
2019-09-30 19:10:14 +02:00
Ondrej Zajicek (work)
ca2dacfcee Nest: Fix bug in export table
Exported route may be in modified state, we need to get cached one for
rte_same() and rta_clone() to work properly.
2019-09-24 17:17:37 +02:00
Ondrej Zajicek (work)
ea0917bcba Filter: Fix eval command 2019-09-24 00:18:48 +02:00
Ondrej Zajicek (work)
9c79022153 Nest: Fix help for 'graceful restart' command
Multi-worded commands are not automatically added to top-level
help output.

Thanks to Christoph for the bugreport.
2019-09-23 14:52:31 +02:00
Maria Matejka
3f477ccb03 Filters: Function body comparison result now used.
Function bodies were compared in post-parse time, yet the result was not
used and the functions were incorrectly considered the same as before.

Now the result is used to reload affected protocols.
2019-09-23 14:03:26 +02:00
Ondrej Zajicek (work)
eb1e43a9af BGP: Fix setup with multiple dynamic BGP ranges
Based on a patch from Liam Nattrass, thanks.
2019-09-17 14:45:14 +02:00
156 changed files with 5779 additions and 2157 deletions

57
.drone.yml Normal file
View File

@ -0,0 +1,57 @@
---
kind: pipeline
type: docker
name: deploy
steps:
- name: build alpine
image: c8n.io/simonburblecom/bird-build:alpine
commands:
- /build.sh
- name: build ubuntu
image: c8n.io/simonburblecom/bird-build:ubuntu
commands:
- /build.sh
- name: upload
image: plugins/s3
settings:
bucket: artifacts
access_key:
from_secret: MINIO_ACCESS_KEY
secret_key:
from_secret: MINIO_SECRET_KEY
endpoint: https://minio.burble.dn42
region: uk-lon3
path_style: true
source: artifacts/**/*
strip_prefix: artifacts/
target: /bird/${DRONE_BRANCH}
image_pull_secrets:
- CONFIG_JSON
---
kind: secret
name: CONFIG_JSON
get:
path: burble.dn42/kv/data/drone/docker
name: configjson
---
kind: secret
name: MINIO_ACCESS_KEY
get:
path: burble.dn42/kv/data/drone/minio
name: ACCESS_KEY
---
kind: secret
name: MINIO_SECRET_KEY
get:
path: burble.dn42/kv/data/drone/minio
name: SECRET_KEY

View File

@ -4,10 +4,12 @@ variables:
GIT_STRATEGY: fetch
DOCKER_CMD: docker --config="$HOME/.docker/$CI_JOB_ID/"
IMG_BASE: registry.labs.nic.cz/labs/bird
TOOLS_DIR: /var/lib/gitlab-runner/bird-tools
stages:
- image
- build
- test
.docker: &docker_build
stage: image
@ -34,36 +36,46 @@ docker_debian-7-amd64:
IMG_NAME: "debian-7-amd64"
<<: *docker_build
docker_debian-8-amd64:
variables:
IMG_NAME: "debian-8-amd64"
<<: *docker_build
docker_debian-9-amd64:
variables:
IMG_NAME: "debian-9-amd64"
<<: *docker_build
docker_debian-testing-amd64:
variables:
IMG_NAME: "debian-testing-amd64"
<<: *docker_build
docker_debian-7-i386:
variables:
IMG_NAME: "debian-7-i386"
<<: *docker_build
docker_debian-8-amd64:
variables:
IMG_NAME: "debian-8-amd64"
<<: *docker_build
docker_debian-8-i386:
variables:
IMG_NAME: "debian-8-i386"
<<: *docker_build
docker_debian-9-amd64:
variables:
IMG_NAME: "debian-9-amd64"
<<: *docker_build
docker_debian-9-i386:
variables:
IMG_NAME: "debian-9-i386"
<<: *docker_build
docker_debian-10-amd64:
variables:
IMG_NAME: "debian-10-amd64"
<<: *docker_build
docker_debian-10-i386:
variables:
IMG_NAME: "debian-10-i386"
<<: *docker_build
docker_debian-testing-amd64:
variables:
IMG_NAME: "debian-testing-amd64"
<<: *docker_build
docker_debian-testing-i386:
variables:
IMG_NAME: "debian-testing-i386"
@ -79,125 +91,73 @@ docker_fedora-26-amd64:
IMG_NAME: "fedora-26-amd64"
<<: *docker_build
docker_fedora-27-amd64:
variables:
IMG_NAME: "fedora-27-amd64"
<<: *docker_build
docker_fedora-28-amd64:
variables:
IMG_NAME: "fedora-28-amd64"
<<: *docker_build
docker_fedora-29-amd64:
variables:
IMG_NAME: "fedora-29-amd64"
<<: *docker_build
docker_fedora-30-amd64:
variables:
IMG_NAME: "fedora-30-amd64"
<<: *docker_build
docker_fedora-31-amd64:
variables:
IMG_NAME: "fedora-31-amd64"
<<: *docker_build
docker_centos-7-amd64:
variables:
IMG_NAME: "centos-7-amd64"
<<: *docker_build
docker_opensuse-42_3-amd64:
docker_centos-8-amd64:
variables:
IMG_NAME: "opensuse-42.3-amd64"
IMG_NAME: "centos-8-amd64"
<<: *docker_build
docker_ubuntu-14_04-amd64:
variables:
IMG_NAME: "ubuntu-14.04-amd64"
<<: *docker_build
docker_ubuntu-16_04-amd64:
variables:
IMG_NAME: "ubuntu-16.04-amd64"
<<: *docker_build
docker_ubuntu-18_04-amd64:
variables:
IMG_NAME: "ubuntu-18.04-amd64"
<<: *docker_build
docker_ubuntu-19_10-amd64:
variables:
IMG_NAME: "ubuntu-19.10-amd64"
<<: *docker_build
.debian-7-i386: &debian-7-i386_env
image: registry.labs.nic.cz/labs/bird:debian-7-i386
tags:
- docker
- linux
- amd64
docker_opensuse-15.0-amd64:
variables:
IMG_NAME: "opensuse-15.0-amd64"
<<: *docker_build
.debian-8-i386: &debian-8-i386_env
image: registry.labs.nic.cz/labs/bird:debian-8-i386
tags:
- docker
- linux
- amd64
docker_opensuse-15.1-amd64:
variables:
IMG_NAME: "opensuse-15.1-amd64"
<<: *docker_build
.debian-9-i386: &debian-9-i386_env
image: registry.labs.nic.cz/labs/bird:debian-9-i386
tags:
- docker
- linux
- amd64
.debian-testing-i386: &debian-testing-i386_env
image: registry.labs.nic.cz/labs/bird:debian-testing-i386
tags:
- docker
- linux
- amd64
.debian-7-amd64: &debian-7-amd64_env
image: registry.labs.nic.cz/labs/bird:debian-7-amd64
tags:
- docker
- linux
- amd64
.debian-8-amd64: &debian-8-amd64_env
image: registry.labs.nic.cz/labs/bird:debian-8-amd64
tags:
- docker
- linux
- amd64
.debian-9-amd64: &debian-9-amd64_env
image: registry.labs.nic.cz/labs/bird:debian-9-amd64
tags:
- docker
- linux
- amd64
.debian-testing-amd64: &debian-testing-amd64_env
image: registry.labs.nic.cz/labs/bird:debian-testing-amd64
tags:
- docker
- linux
- amd64
.fedora-25-amd64: &fedora-25-amd64_env
image: registry.labs.nic.cz/labs/bird:fedora-25-amd64
tags:
- docker
- linux
- amd64
.fedora-26-amd64: &fedora-26-amd64_env
image: registry.labs.nic.cz/labs/bird:fedora-26-amd64
tags:
- docker
- linux
- amd64
.centos-7-amd64: &centos-7-amd64_env
image: registry.labs.nic.cz/labs/bird:centos-7-amd64
tags:
- docker
- linux
- amd64
.opensuse-42_3-amd64: &opensuse-42_3-amd64_env
image: registry.labs.nic.cz/labs/bird:opensuse-42.3-amd64
tags:
- docker
- linux
- amd64
.ubuntu-14_04-amd64: &ubuntu-14_04-amd64_env
image: registry.labs.nic.cz/labs/bird:ubuntu-14.04-amd64
tags:
- docker
- linux
- amd64
.ubuntu-16_04-amd64: &ubuntu-16_04-amd64_env
image: registry.labs.nic.cz/labs/bird:ubuntu-16.04-amd64
tags:
- docker
- linux
- amd64
# TODO We want to copy these BSDs to our own virtual machines, to make sure someone doesn't update them by accident.
# TODO We want to copy these BSDs to our own virtual machines, to make sure
# someone doesn't update them by accident.
.freebsd-11-i386: &freebsd-11-i386_env
tags:
- freebsd
@ -211,12 +171,8 @@ docker_ubuntu-16_04-amd64:
tags:
- freebsd
- amd64
#only:
#- master
#- triggers
#- tags
.build: &build_job
.build: &build-base
stage: build
script:
- autoreconf
@ -228,66 +184,232 @@ docker_ubuntu-16_04-amd64:
# Run tests if they are available
- $MAKE check
.build-linux: &build-linux
<<: *build-base
tags:
- docker
- linux
- amd64
build-debian-7-amd64:
<<: *debian-7-amd64_env
<<: *build_job
build-debian-8-amd64:
<<: *debian-8-amd64_env
<<: *build_job
build-debian-9-amd64:
<<: *debian-9-amd64_env
<<: *build_job
build-debian-testing-amd64:
<<: *debian-testing-amd64_env
<<: *build_job
build-fedora-25-amd64:
<<: *fedora-25-amd64_env
<<: *build_job
build-fedora-26-amd64:
<<: *fedora-26-amd64_env
<<: *build_job
build-centos-7-amd64:
<<: *centos-7-amd64_env
<<: *build_job
build-opensuse-42_3-amd64:
<<: *opensuse-42_3-amd64_env
<<: *build_job
build-ubuntu-14_04-amd64:
<<: *ubuntu-14_04-amd64_env
<<: *build_job
build-ubuntu-16_04-amd64:
<<: *ubuntu-16_04-amd64_env
<<: *build_job
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-7-amd64
build-debian-7-i386:
<<: *debian-7-i386_env
<<: *build_job
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-7-i386
build-debian-8-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-8-amd64
build-debian-8-i386:
<<: *debian-8-i386_env
<<: *build_job
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-8-i386
build-debian-9-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-9-amd64
build-debian-9-i386:
<<: *debian-9-i386_env
<<: *build_job
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-9-i386
build-debian-10-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-10-amd64
build-debian-10-i386:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-10-i386
build-debian-testing-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-testing-amd64
build-debian-testing-i386:
<<: *debian-testing-i386_env
<<: *build_job
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-testing-i386
build-fedora-25-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-25-amd64
build-fedora-26-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-26-amd64
build-fedora-27-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-27-amd64
build-fedora-28-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-28-amd64
build-fedora-29-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-29-amd64
build-fedora-30-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-30-amd64
build-fedora-31-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-31-amd64
build-centos-7-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:centos-7-amd64
build-centos-8-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:centos-8-amd64
build-ubuntu-14_04-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:ubuntu-14.04-amd64
build-ubuntu-16_04-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:ubuntu-16.04-amd64
build-ubuntu-18_04-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:ubuntu-18.04-amd64
build-ubuntu-19_04-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:ubuntu-19.04-amd64
build-opensuse-15.0-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:opensuse-15.0-amd64
build-opensuse-15.1-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:opensuse-15.1-amd64
build-freebsd-11-amd64:
<<: *freebsd-11-amd64_env
<<: *build_job
<<: *build-base
tags:
- freebsd
- amd64
build-freebsd-11-i386:
<<: *freebsd-11-i386_env
<<: *build_job
<<: *build-base
tags:
- freebsd
- i386
build-birdlab:
stage: build
tags:
- birdlab
- amd64
script:
- DIR=$(pwd)
- autoreconf
- ./configure
- make
- cd $TOOLS_DIR
- sudo git clean -fx
- git pull --ff-only
- mv $DIR/bird $DIR/birdc netlab/common
.test: &test-base
stage: test
needs: [build-birdlab]
tags:
- birdlab
- amd64
script:
- cd $TOOLS_DIR/netlab
- sudo ./stop
- sudo ./runtest -m check $TEST_NAME
test-ospf-base:
<<: *test-base
variables:
TEST_NAME: cf-ospf-base
test-ospf-default:
<<: *test-base
variables:
TEST_NAME: cf-ospf-default
test-ospf-priority:
<<: *test-base
variables:
TEST_NAME: cf-ospf-priority
test-ospf-nbma:
<<: *test-base
variables:
TEST_NAME: cf-ospf-nbma
test-ospf-ptmp:
<<: *test-base
variables:
TEST_NAME: cf-ospf-ptmp
test-ospf-authentication:
<<: *test-base
variables:
TEST_NAME: cf-ospf-authentication
test-ospf-bfd:
<<: *test-base
variables:
TEST_NAME: cf-ospf-bfd
test-ospf-custom:
<<: *test-base
variables:
TEST_NAME: cf-ospf-custom
test-ospf-vrf:
<<: *test-base
variables:
TEST_NAME: cf-ospf-vrf
test-bgp-base:
<<: *test-base
variables:
TEST_NAME: cf-bgp-base
test-bgp-auth:
<<: *test-base
variables:
TEST_NAME: cf-bgp-auth
test-bgp-int:
<<: *test-base
variables:
TEST_NAME: cf-bgp-int
test-bgp-merged:
<<: *test-base
variables:
TEST_NAME: cf-bgp-merged
test-ebgp-loop:
<<: *test-base
variables:
TEST_NAME: cf-ebgp-loop
test-ebgp-star:
<<: *test-base
variables:
TEST_NAME: cf-ebgp-star
test-ibgp-loop:
<<: *test-base
variables:
TEST_NAME: cf-ibgp-loop
test-ibgp-star:
<<: *test-base
variables:
TEST_NAME: cf-ibgp-flat

View File

@ -184,11 +184,20 @@ check: tests tests_run
tests: $(tests_targets)
tests_run: $(tests_targets_ok)
STATIC_CHECKERS_ENABLE := nullability.NullableDereferenced nullability.NullablePassedToNonnull nullability.NullableReturnedFromNonnull optin.portability.UnixAPI valist.CopyToSelf valist.Uninitialized valist.Unterminated
STATIC_CHECKERS_DISABLE := deadcode.DeadStores
STATIC_SCAN_FLAGS := -o $(objdir)/static-scan/ $(addprefix -enable-checker ,$(STATIC_CHECKERS_ENABLE)) $(addprefix -disable-checker ,$(STATIC_CHECKERS_DISABLE))
static-scan:
$(E)echo Running static code analysis
$(Q)$(MAKE) clean
$(Q)scan-build $(STATIC_SCAN_FLAGS) $(MAKE) -$(MAKEFLAGS)
tags:
cd $(srcdir) ; etags -lc `find $(dirs) -name *.[chY]`
cd $(srcdir) ; etags -lc `find $(dirs) -name '*.[chY]'`
cscope:
cd $(srcdir) ; find $(dirs) -name *.[chY] > cscope.files ; cscope -b
cd $(srcdir) ; find $(dirs) -name '*.[chY]' > cscope.files ; cscope -b
# Install

46
NEWS
View File

@ -1,3 +1,49 @@
Version 2.0.8 (2021-03-18)
o Automatic channel reloads based on RPKI changes
o Multiple static routes with the same network
o Use bitmaps to keep track of exported routes
o Per-channel debug flags
o CLI commands show info from multiple protocols
o Linux: IPv4 routes with IPv6 nexthops
o Filter: Optimized redesign of prefix sets
o Filter: Improved type checking of user filters
o Filter: New src/dst accessors for Flowspec and SADR
o Filter: New 'weight' route attribute
o Filter: BGP path mask loop operator
o Filter: Remove quitbird command
o RIP: Demand circuit support (RFC 2091)
o BGP: New 'allow as sets' and 'enforce first as' options
o BGP: Support for BGP hostname capability
o BGP: Support for MD5SIG with dynamic BGP
o BFD: Optional separation of IPv4 / IPv6 BFD instances
o BFD: Per-peer session options
o RPKI: Allow build without libSSH
o RPKI: New 'ignore max length' option
o OSPF: Redesign of handling of unnumbered PtPs
o OSPF: Allow key id 0 in authentication
o Babel: Use onlink flag for routes with unreachable next hop
o Many bugfixes
Notes:
Automatic channel reloads based on RPKI changes are enabled by default,
but require import table enabled when used in BGP import filter.
BIRD now uses bitmaps to keep track of exported routes instead of
re-evaluation of export filters. That should improve speed and accuracy in
route export handling during reconfiguration, but takes some more memory.
Per-channel debug logging and some CLI commands (like 'show ospf neighbors')
defaulting to all protocol instances lead to some minor changes in log and
CLI output. Caution is recommended when logs or CLI output are monitored by
scripts.
Version 2.0.7 (2019-10-11)
o BGP: Accumulated IGP metric (RFC 7311)
o Important filter reconfiguration bugfix
o Several other bugfixes
Version 2.0.6 (2019-09-10)
o RAdv: Solicited unicast RAs
o BGP: Optional Adj-RIB-Out

View File

@ -30,6 +30,7 @@ class BIRDFValPrinter(BIRDPrinter):
"T_ENUM_ROA": "i",
"T_ENUM_NETTYPE": "i",
"T_ENUM_RA_PREFERENCE": "i",
"T_ENUM_AF": "i",
"T_IP": "ip",
"T_NET": "net",
"T_STRING": "s",

View File

@ -169,7 +169,7 @@ WHITE [ \t]
errno = 0;
l = bstrtoul10(yytext, &e);
if (e && (*e != ':') || (errno == ERANGE) || (l >> 32))
if (!e || (*e != ':') || (errno == ERANGE) || (l >> 32))
cf_error("ASN out of range");
if (l >> 16)
@ -187,7 +187,7 @@ WHITE [ \t]
errno = 0;
l = bstrtoul10(e+1, &e);
if (e && *e || (errno == ERANGE) || (l >> len2))
if (!e || *e || (errno == ERANGE) || (l >> len2))
cf_error("Number out of range");
cf_lval.i64 |= l;
@ -214,13 +214,13 @@ WHITE [ \t]
errno = 0;
l = bstrtoul10(yytext+2, &e);
if (e && (*e != ':') || (errno == ERANGE) || (l >> len1))
if (!e || (*e != ':') || (errno == ERANGE) || (l >> len1))
cf_error("ASN out of range");
cf_lval.i64 |= ((u64) l) << len2;
errno = 0;
l = bstrtoul10(e+1, &e);
if (e && *e || (errno == ERANGE) || (l >> len2))
if (!e || *e || (errno == ERANGE) || (l >> len2))
cf_error("Number out of range");
cf_lval.i64 |= l;
@ -242,7 +242,7 @@ WHITE [ \t]
errno = 0;
l = bstrtoul10(e, &e);
if (e && *e || (errno == ERANGE) || (l >> 16))
if (!e || *e || (errno == ERANGE) || (l >> 16))
cf_error("Number out of range");
cf_lval.i64 |= l;
@ -266,7 +266,7 @@ WHITE [ \t]
unsigned long int l;
errno = 0;
l = bstrtoul16(yytext+2, &e);
if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
if (!e || *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
cf_error("Number out of range");
cf_lval.i = l;
return NUM;
@ -277,7 +277,7 @@ WHITE [ \t]
unsigned long int l;
errno = 0;
l = bstrtoul10(yytext, &e);
if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
if (!e || *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
cf_error("Number out of range");
cf_lval.i = l;
return NUM;
@ -737,7 +737,7 @@ cf_lex_init(int is_cli, struct config *c)
void
cf_push_scope(struct symbol *sym)
{
struct sym_scope *s = cfg_alloc(sizeof(struct sym_scope));
struct sym_scope *s = cfg_allocz(sizeof(struct sym_scope));
s->next = conf_this_scope;
conf_this_scope = s;

View File

@ -55,6 +55,7 @@
#include "lib/timer.h"
#include "conf/conf.h"
#include "filter/filter.h"
#include "sysdep/unix/unix.h"
static jmp_buf conf_jmpbuf;
@ -217,6 +218,14 @@ config_del_obstacle(struct config *c)
static int
global_commit(struct config *new, struct config *old)
{
if (!new->hostname)
{
new->hostname = get_hostname(new->mem);
if (!new->hostname)
log(L_WARN "Cannot determine hostname");
}
if (!old)
return 0;
@ -573,6 +582,7 @@ cfg_copy_list(list *dest, list *src, unsigned node_size)
{
dn = cfg_alloc(node_size);
memcpy(dn, sn, node_size);
memset(dn, 0, sizeof(node));
add_tail(dest, dn);
}
}

View File

@ -27,18 +27,20 @@ struct config {
list symbols; /* Configured symbols in config order */
int mrtdump_file; /* Configured MRTDump file (sysdep, fd in unix) */
char *syslog_name; /* Name used for syslog (NULL -> no syslog) */
const char *syslog_name; /* Name used for syslog (NULL -> no syslog) */
struct rtable_config *def_tables[NET_MAX]; /* Default routing tables for each network */
struct iface_patt *router_id_from; /* Configured list of router ID iface patterns */
u32 router_id; /* Our Router ID */
unsigned proto_default_debug; /* Default protocol debug mask */
unsigned proto_default_mrtdump; /* Default protocol mrtdump mask */
u32 proto_default_debug; /* Default protocol debug mask */
u32 proto_default_mrtdump; /* Default protocol mrtdump mask */
u32 channel_default_debug; /* Default channel debug mask */
struct timeformat tf_route; /* Time format for 'show route' */
struct timeformat tf_proto; /* Time format for 'show protocol' */
struct timeformat tf_log; /* Time format for the logfile */
struct timeformat tf_base; /* Time format for other purposes */
u32 gr_wait; /* Graceful restart wait timeout (sec) */
const char *hostname; /* Hostname */
int cli_debug; /* Tracing of CLI connections and commands */
int latency_debug; /* I/O loop tracks duration of each event */
@ -198,11 +200,11 @@ struct symbol *cf_localize_symbol(struct symbol *sym);
* Result: Pointer to the newly defined symbol. If we are in the top-level
* scope, it's the same @sym as passed to the function.
*/
#define cf_define_symbol(sym_, type_, var_, def_) ({ \
struct symbol *sym = cf_localize_symbol(sym_); \
sym->class = type_; \
sym->var_ = def_; \
sym; })
#define cf_define_symbol(osym_, type_, var_, def_) ({ \
struct symbol *sym_ = cf_localize_symbol(osym_); \
sym_->class = type_; \
sym_->var_ = def_; \
sym_; })
void cf_push_scope(struct symbol *);
void cf_pop_scope(void);

View File

@ -19,6 +19,7 @@ CF_HDR
#include "nest/protocol.h"
#include "nest/iface.h"
#include "nest/route.h"
#include "nest/bfd.h"
#include "nest/cli.h"
#include "filter/filter.h"
@ -60,9 +61,10 @@ CF_DECLS
net_addr net;
net_addr *net_ptr;
struct symbol *s;
char *t;
const char *t;
struct rtable_config *r;
struct channel_config *cc;
struct channel *c;
struct f_inst *x;
struct {
struct f_inst *begin, *end;
@ -147,7 +149,7 @@ conf: definition ;
definition:
DEFINE symbol '=' term ';' {
struct f_val *val = cfg_alloc(sizeof(struct f_val));
struct f_val *val = cfg_allocz(sizeof(struct f_val));
if (f_eval(f_linearize($4), cfg_mem, val) > F_RETURN) cf_error("Runtime error");
cf_define_symbol($2, SYM_CONSTANT | val->type, val, val);
}

View File

@ -34,9 +34,11 @@ m4_define(CF_CLI, `CF_KEYWORDS(m4_translit($1, [[ ]], [[,]]))
')
# Enums are translated to C initializers: use CF_ENUM(typename, prefix, values)
m4_define(CF_enum, `m4_divert(1){ "CF_enum_prefix[[]]$1", -((CF_enum_type<<16) | CF_enum_prefix[[]]$1), NULL },
# For different prefix: CF_ENUM_PX(typename, external prefix, C prefix, values)
m4_define(CF_enum, `m4_divert(1){ "CF_enum_prefix_ext[[]]$1", -((CF_enum_type<<16) | CF_enum_prefix_int[[]]$1), NULL },
m4_divert(-1)')
m4_define(CF_ENUM, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix]],$2)CF_iterate([[CF_enum]], [[m4_shift(m4_shift($@))]])DNL')
m4_define(CF_ENUM, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$2)CF_iterate([[CF_enum]], [[m4_shift(m4_shift($@))]])DNL')
m4_define(CF_ENUM_PX, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$3)CF_iterate([[CF_enum]], [[m4_shift(m4_shift(m4_shift($@)))]])DNL')
# After all configuration templates end, we generate the
m4_m4wrap(`

View File

@ -44,6 +44,7 @@ m4_define(CF_CLI_HELP, `')
# ENUM declarations are ignored
m4_define(CF_ENUM, `')
m4_define(CF_ENUM_PX, `')
# After all configuration templates end, we finally generate the grammar file.
m4_m4wrap(`

View File

@ -24,6 +24,12 @@ AC_ARG_ENABLE([debug-generated],
[enable_debug_generated=no]
)
AC_ARG_ENABLE([debug-expensive],
[AS_HELP_STRING([--enable-debug-expensive], [enable expensive consistency checks (implies --enable-debug) @<:@no@:>@])],
[],
[enable_debug_expensive=no]
)
AC_ARG_ENABLE([memcheck],
[AS_HELP_STRING([--enable-memcheck], [check memory allocations when debugging @<:@yes@:>@])],
[],
@ -37,7 +43,7 @@ AC_ARG_ENABLE([pthreads],
)
AC_ARG_ENABLE([libssh],
[AS_HELP_STRING([--enable-libssh], [enable LibSSH support together with RPKI @<:@try@:>@])],
[AS_HELP_STRING([--enable-libssh], [enable LibSSH support in RPKI @<:@try@:>@])],
[],
[enable_libssh=try]
)
@ -72,6 +78,9 @@ AC_ARG_VAR([FLEX], [location of the Flex program])
AC_ARG_VAR([BISON], [location of the Bison program])
AC_ARG_VAR([M4], [location of the M4 program])
if test "$enable_debug_expensive" = yes; then
enable_debug=yes
fi
if test "$srcdir" = . ; then
# Building in current directory => create obj directory holding all objects
@ -150,7 +159,9 @@ if test "$bird_cflags_default" = yes ; then
if test "$bird_cv_c_lto" = yes; then
CFLAGS="$CFLAGS -flto"
LDFLAGS="$LDFLAGS -flto=4"
LDFLAGS="$LDFLAGS -flto=4 -g"
else
LDFLAGS="$LDFLAGS -g"
fi
CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes -Wno-parentheses"
@ -269,7 +280,6 @@ if test "$enable_libssh" != no ; then
if test "$fail" != yes ; then
AC_DEFINE([HAVE_LIBSSH], [1], [Define to 1 if you have the `ssh' library (-lssh).])
DAEMON_LIBS="-lssh $DAEMON_LIBS"
proto_rpki=rpki
enable_libssh=yes
else
if test "$enable_libssh" = yes ; then
@ -294,7 +304,7 @@ if test "$enable_mpls_kernel" != no ; then
fi
fi
all_protocols="$proto_bfd babel bgp mrt ospf perf pipe radv rip $proto_rpki static"
all_protocols="$proto_bfd babel bgp mrt ospf perf pipe radv rip rpki static"
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
@ -387,6 +397,10 @@ if test "$enable_debug" = yes ; then
AC_CHECK_LIB([efence], [malloc])
fi
fi
if test "enable_debug_expensive" = yes ; then
AC_DEFINE([ENABLE_EXPENSIVE_CHECKS], [1], [Define to 1 if you want to run expensive consistency checks.])
fi
fi
CLIENT=birdcl
@ -451,6 +465,7 @@ AC_MSG_RESULT([ System configuration: $sysdesc])
AC_MSG_RESULT([ Debugging: $enable_debug])
AC_MSG_RESULT([ POSIX threads: $enable_pthreads])
AC_MSG_RESULT([ Routing protocols: $protocols])
AC_MSG_RESULT([ LibSSH support in RPKI: $enable_libssh])
AC_MSG_RESULT([ Kernel MPLS support: $enable_mpls_kernel])
AC_MSG_RESULT([ Client: $enable_client])

View File

@ -1,5 +1,5 @@
/*
* This is an example configuration file for MB-BGP setting
* This is an example configuration file for MP-BGP setting
*/

View File

@ -430,11 +430,11 @@ a comment, whitespace characters are treated as a single space. If there's a
variable number of options, they are grouped using the <cf/{ }/ brackets. Each
option is terminated by a <cf/;/. Configuration is case sensitive. There are two
ways how to name symbols (like protocol names, filter names, constants etc.).
You can either use a simple string starting with a letter followed by any
combination of letters and numbers (e.g. <cf/R123/, <cf/myfilter/, <cf/bgp5/) or
you can enclose the name into apostrophes (<cf/'/) and than you can use any
combination of numbers, letters. hyphens, dots and colons (e.g.
<cf/'1:strange-name'/, <cf/'-NAME-'/, <cf/'cool::name'/).
You can either use a simple string starting with a letter (or underscore)
followed by any combination of letters, numbers and underscores (e.g. <cf/R123/,
<cf/my_filter/, <cf/bgp5/) or you can enclose the name into apostrophes (<cf/'/)
and than you can use any combination of numbers, letters, underscores, hyphens,
dots and colons (e.g. <cf/'1:strange-name'/, <cf/'-NAME-'/, <cf/'cool::name'/).
<p>Here is an example of a simple config file. It enables synchronization of
routing tables with OS kernel, learns network interfaces and runs RIP on all
@ -504,8 +504,14 @@ include "tablename.conf";;
command-line option.
<tag><label id="opt-debug-protocols">debug protocols all|off|{ states|routes|filters|interfaces|events|packets [, <m/.../] }</tag>
Set global defaults of protocol debugging options. See <cf/debug/ in the
following section. Default: off.
Set global defaults of protocol debugging options.
See <ref id="proto-debug" name="debug"> in the following section.
Default: off.
<tag><label id="opt-debug-channels">debug channels all|off|{ states|routes|filters|events [, <m/.../] }</tag>
Set global defaults of channel debugging options.
See <ref id="channel-debug" name="debug"> in the channel section.
Default: off.
<tag><label id="opt-debug-commands">debug commands <m/number/</tag>
Control logging of client connections (0 for no logging, 1 for logging
@ -570,7 +576,7 @@ include "tablename.conf";;
<tag><label id="opt-attribute">attribute <m/type/ <m/name/</tag>
Declare a custom route attribute. You can set and get it in filters like
any other route atribute. This feature is intended for marking routes
any other route attribute. This feature is intended for marking routes
in import filters for export filtering purposes instead of locally
assigned BGP communities which have to be deleted in export filters.
@ -585,6 +591,9 @@ include "tablename.conf";;
See <ref id="proto-iface" name="interface"> section for detailed
description of interface patterns with extended clauses.
<tag><label id="opt-hostname">hostname "<m/name/"</tag>
Set hostname. Default: node name as returned by `uname -n'.
<tag><label id="opt-graceful-restart">graceful restart wait <m/number/</tag>
During graceful restart recovery, BIRD waits for convergence of routing
protocols. This option allows to specify a timeout for the recovery to
@ -654,12 +663,14 @@ agreement").
Set protocol debugging options. If asked, each protocol is capable of
writing trace messages about its work to the log (with category
<cf/trace/). You can either request printing of <cf/all/ trace messages
or only of the types selected: <cf/states/ for protocol state changes
or only of the selected types: <cf/states/ for protocol state changes
(protocol going up, down, starting, stopping etc.), <cf/routes/ for
routes exchanged with the routing table, <cf/filters/ for details on
route filtering, <cf/interfaces/ for interface change events sent to the
protocol, <cf/events/ for events internal to the protocol and <cf/packets/
for packets sent and received by the protocol. Default: off.
route filtering, <cf/interfaces/ for interface change events sent to
the protocol, <cf/events/ for events internal to the protocol and
<cf/packets/ for packets sent and received by the protocol. Classes
<cf/routes/ and <cf/filters/ can be also set per-channel using
<ref id="channel-debug" name="channel debugging option">) Default: off.
<tag><label id="proto-mrtdump">mrtdump all|off|{ states|messages [, <m/.../] }</tag>
Set protocol MRTdump flags. MRTdump is a standard binary format for
@ -748,10 +759,6 @@ agreement").
on all interfaces that have address from 192.168.0.0/16, but not from
192.168.1.0/24.
<cf>interface -192.168.1.0/24, 192.168.0.0/16;</cf> - start the protocol
on all interfaces that have address from 192.168.0.0/16, but not from
192.168.1.0/24.
<cf>interface "eth*" 192.168.1.0/24;</cf> - start the protocol on all
ethernet interfaces that have address from 192.168.1.0/24.
@ -787,11 +794,12 @@ agreement").
<descrip>
<tag><label id="proto-pass-id">id <M>num</M></tag>
ID of the password, (1-255). If it is not used, BIRD will choose ID based
on an order of the password item in the interface. For example, second
password item in one interface will have default ID 2. ID is used by
some routing protocols to identify which password was used to
authenticate protocol packets.
ID of the password, (0-255). If it is not specified, BIRD will choose ID
based on an order of the password item in the interface, starting from
1. For example, second password item in one interface will have default
ID 2. ID 0 is allowed by BIRD, but some other implementations may not
allow it. ID is used by some routing protocols to identify which
password was used to authenticate protocol packets.
<tag><label id="proto-pass-gen-from">generate from "<m/time/"</tag>
The start time of the usage of the password for packet signing.
@ -831,6 +839,16 @@ templates. Multiple definitions of the same channel are forbidden, but channels
inherited from templates can be updated by new definitions.
<descrip>
<tag><label id="channel-debug">debug all|off|{ states|routes|filters [, <m/.../] }</tag>
Set channel debugging options. Like in <ref id="proto-debug"
name="protocol debugging">, channels are capable of writing trace
messages about its work to the log (with category <cf/trace/). You can
either request printing of <cf/all/ trace messages or only of the
selected types: <cf/states/ for channel state changes (channel going up,
down, feeding, reloading etc.), <cf/routes/ for routes propagated
through the channel, <cf/filters/ for details on route filtering,
remaining debug flags are not used in channel debug. Default: off.
<tag><label id="proto-table">table <m/name/</tag>
Specify a table to which the channel is connected. Default: the first
table of given nettype.
@ -857,6 +875,19 @@ inherited from templates can be updated by new definitions.
possible to show them using <cf/show route filtered/. Note that this
option does not work for the pipe protocol. Default: off.
<tag><label id="proto-rpki-reload">rpki reload <m/switch/</tag>
Import or export filters may depend on route RPKI status (using
<cf/roa_check()/ operator). In contrast to to other filter operators,
this status for the same route may change as the content of ROA tables
changes. When this option is active, BIRD activates automatic reload of
affected channels whenever ROA tables are updated (after a short settle
time). When disabled, route reloads have to be requested manually. The
option is ignored if <cf/roa_check()/ is not used in channel filters.
Note that for BGP channels, automatic reload requires
<ref id="bgp-import-table" name="import table"> or
<ref id="bgp-export-table" name="export table"> (for respective
direction). Default: on.
<tag><label id="proto-import-limit">import limit [<m/number/ | off ] [action warn | block | restart | disable]</tag>
Specify an import route limit (a maximum number of routes imported from
the protocol) and optionally the action to be taken when the limit is
@ -1238,8 +1269,8 @@ bird>
<label id="data-types">
<p>Each variable and each value has certain type. Booleans, integers and enums
are incompatible with each other (that is to prevent you from shooting in the
foot).
are incompatible with each other (that is to prevent you from shooting oneself
in the foot).
<descrip>
<tag><label id="type-bool">bool</tag>
@ -1275,8 +1306,8 @@ foot).
<tag><label id="type-ip">ip</tag>
This type can hold a single IP address. The IPv4 addresses are stored as
IPv4-Mapped IPv6 addresses so one data type for both of them is used.
Whether the address is IPv4 or not may be checked by <cf>.is_ip4</cf>
which returns <cf/bool/. IP addresses are written in the standard
Whether the address is IPv4 or not may be checked by <cf>.is_v4</cf>
which returns a <cf/bool/. IP addresses are written in the standard
notation (<cf/10.20.30.40/ or <cf/fec0:3:4::1/). You can apply special
operator <cf>.mask(<M>num</M>)</cf> on values of type ip. It masks out
all but first <cf><M>num</M></cf> bits from the IP address. So
@ -1299,7 +1330,9 @@ foot).
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.
operators as IP prefixes, but just for the destination part. They also
support <cf/.src/ and <cf/.dst/ operators to get respective parts of the
address as separate <cf/NET_IP6/ values.
<cf/NET_VPN4/ and <cf/NET_VPN6/ prefixes hold an IP prefix with VPN
Route Distinguisher (<rfc id="4364">). They support the same special
@ -1313,7 +1346,9 @@ foot).
and <cf/.asn/ which extracts the ASN.
<cf/NET_FLOW4/ and <cf/NET_FLOW6/ hold an IP prefix together with a
flowspec rule. Filters currently don't support flowspec parsing.
flowspec rule. Filters currently do not support much flowspec parsing,
only <cf/.src/ and <cf/.dst/ operators to get source and destination
parts of the flowspec as separate <cf/NET_IP4/ / <cf/NET_IP6/ values.
<cf/NET_MPLS/ holds a single MPLS label and its handling is currently
not implemented.
@ -1419,9 +1454,10 @@ foot).
<cf>192.168.0.0/16{16,24}</cf> and <cf>192.168.0.0/16 ge 24</cf> as
<cf>192.168.0.0/16{24,32}</cf>.
It is possible to mix IPv4 and IPv6 prefixes/addresses in a prefix/ip set
but its behavior may change between versions without any warning; don't do
it unless you are more than sure what you are doing. (Really, don't do it.)
It is not possible to mix IPv4 and IPv6 prefixes in a prefix set. It is
currently possible to mix IPv4 and IPv6 addresses in an ip set, but that
behavior may change between versions without any warning; don't do it
unless you are more than sure what you are doing. (Really, don't do it.)
<tag><label id="type-enum">enum</tag>
Enumeration types are fixed sets of possibilities. You can't define your
@ -1470,11 +1506,16 @@ foot).
<cf/*/ matches any (even empty) sequence of arbitrary AS numbers and
<cf/?/ matches one arbitrary AS number. For example, if <cf>bgp_path</cf>
is 4 3 2 1, then: <tt>bgp_path &tilde; [= * 4 3 * =]</tt> is true,
but <tt>bgp_path &tilde; [= * 4 5 * =]</tt> is false. BGP mask
expressions can also contain integer expressions enclosed in parenthesis
and integer variables, for example <tt>[= * 4 (1+2) a =]</tt>. You can
also use ranges (e.g. <tt>[= * 3..5 2 100..200 * =]</tt>) and sets
(e.g. <tt>[= 1 2 [3, 5, 7] * =]</tt>).
but <tt>bgp_path &tilde; [= * 4 5 * =]</tt> is false. There is also
<cf/+/ operator which matches one or multiple instances of previous
expression, e.g. <tt>[= 1 2+ 3 =]</tt> matches both path 1 2 3 and path
1 2 2 2 3, but not 1 3 nor 1 2 4 3. Note that while <cf/*/ and <cf/?/
are wildcard-style operators, <cf/+/ is regex-style operator.
BGP mask expressions can also contain integer expressions enclosed in
parenthesis and integer variables, for example <tt>[= * 4 (1+2) a =]</tt>.
You can also use ranges (e.g. <tt>[= * 3..5 2 100..200 * =]</tt>)
and sets (e.g. <tt>[= 1 2 [3, 5, 7] * =]</tt>).
<tag><label id="type-clist">clist</tag>
Clist is similar to a set, except that unlike other sets, it can be
@ -1514,7 +1555,7 @@ foot).
<cf/!&tilde;/ membership operators) can be used to modify or test
eclists, with ECs instead of pairs as arguments.
<tag><label id="type-lclist">lclist/</tag>
<tag><label id="type-lclist">lclist</tag>
Lclist is a data type used for BGP large community lists. Like eclists,
lclists are very similar to clists, but they are sets of LCs instead of
pairs. The same operations (like <cf/add/, <cf/delete/ or <cf/&tilde;/
@ -1546,8 +1587,8 @@ the clist that is also a member of the pair/quad set).
<p>There is one operator related to ROA infrastructure - <cf/roa_check()/. It
examines a ROA table and does <rfc id="6483"> route origin validation for a
given network prefix. The basic usage is <cf>roa_check(<m/table/)</cf>, which
checks current route (which should be from BGP to have AS_PATH argument) in the
specified ROA table and returns ROA_UNKNOWN if there is no relevant ROA,
checks the current route (which should be from BGP to have AS_PATH argument) in
the specified ROA table and returns ROA_UNKNOWN if there is no relevant ROA,
ROA_VALID if there is a matching ROA, or ROA_INVALID if there are some relevant
ROAs but none of them match. There is also an extended variant
<cf>roa_check(<m/table/, <m/prefix/, <m/asn/)</cf>, which allows to specify a
@ -1640,9 +1681,8 @@ Common route attributes are:
<tag><label id="rta-source"><m/enum/ source</tag>
what protocol has told me about this route. Possible values:
<cf/RTS_DUMMY/, <cf/RTS_STATIC/, <cf/RTS_INHERIT/, <cf/RTS_DEVICE/,
<cf/RTS_STATIC_DEVICE/, <cf/RTS_REDIRECT/, <cf/RTS_RIP/, <cf/RTS_OSPF/,
<cf/RTS_OSPF_IA/, <cf/RTS_OSPF_EXT1/, <cf/RTS_OSPF_EXT2/, <cf/RTS_BGP/,
<cf/RTS_PIPE/, <cf/RTS_BABEL/.
<cf/RTS_RIP/, <cf/RTS_OSPF/, <cf/RTS_OSPF_IA/, <cf/RTS_OSPF_EXT1/,
<cf/RTS_OSPF_EXT2/, <cf/RTS_BGP/, <cf/RTS_PIPE/, <cf/RTS_BABEL/.
<tag><label id="rta-dest"><m/enum/ dest</tag>
Type of destination the packets should be sent to
@ -1667,6 +1707,15 @@ Common route attributes are:
creation/removal. Zero is returned for routes with undefined outgoing
interfaces. Read-only.
<tag><label id="rta-weight"><m/int/ weight</tag>
Multipath weight of route next hops. Valid values are 1-256. Reading
returns the weight of the first next hop, setting it sets weights of all
next hops to the specified value. Therefore, this attribute is not much
useful for manipulating individual next hops of an ECMP route, but can
be used in BGP multipath setup to set weights of individual routes that
are merged to one ECMP route during export to the Kernel protocol
(with active <ref id="krt-merge-paths" name="marge paths"> option).
<tag><label id="rta-igp-metric"><m/int/ igp_metric</tag>
The optional attribute that can be used to specify a distance to the
network for routes that do not have a native protocol metric attribute
@ -1697,9 +1746,6 @@ protocol sections.
<tag><label id="print">print|printn <m/expr/ [<m/, expr.../]</tag>
Prints given expressions; useful mainly while debugging filters. The
<cf/printn/ variant does not terminate the line.
<tag><label id="quitbird">quitbird</tag>
Terminates BIRD. Useful when debugging the filter interpreter.
</descrip>
@ -1939,6 +1985,8 @@ configuration is often sufficient.
<p>Note that to use BFD for other protocols like OSPF or BGP, these protocols
also have to be configured to request BFD sessions, usually by <cf/bfd/ option.
In BGP case, it is also possible to specify per-peer BFD session options (e.g.
rx/tx intervals) as a part of the <cf/bfd/ option.
<p>A BFD instance not associated with any VRF handles session requests from all
other protocols, even ones associated with a VRF. Such setup would work for
@ -1955,6 +2003,7 @@ milliseconds.
<code>
protocol bfd [&lt;name&gt;] {
accept [ipv4|ipv6] [direct|multihop];
interface &lt;interface pattern&gt; {
interval &lt;time&gt;;
min rx interval &lt;time&gt;;
@ -1989,6 +2038,14 @@ protocol bfd [&lt;name&gt;] {
</code>
<descrip>
<tag><label id="bfd-accept">accept [ipv4|ipv6] [direct|multihop]</tag>
A BFD protocol instance accepts (by default) all BFD session requests
(with regard to VRF restrictions, see above). This option controls
whether IPv4 / IPv6 and direct / multihop session requests are accepted
(and which listening sockets are opened). It can be used, for example,
to configure separate BFD protocol instances for IPv4 and for IPv6
sessions.
<tag><label id="bfd-iface">interface <m/pattern/ [, <m/.../] { <m/options/ }</tag>
Interface definitions allow to specify options for sessions associated
with such interfaces and also may contain interface specific options.
@ -2165,6 +2222,7 @@ avoid routing loops.
<item> <rfc id="6286"> - AS-Wide Unique BGP Identifier
<item> <rfc id="6608"> - Subcodes for BGP Finite State Machine Error
<item> <rfc id="6793"> - BGP Support for 4-Octet AS Numbers
<item> <rfc id="7311"> - Accumulated IGP Metric Attribute for BGP
<item> <rfc id="7313"> - Enhanced Route Refresh Capability for BGP
<item> <rfc id="7606"> - Revised Error Handling for BGP UPDATE Messages
<item> <rfc id="7911"> - Advertisement of Multiple Paths in BGP
@ -2301,14 +2359,17 @@ using the following configuration parameters:
immediately shut down. Note that this option cannot be used with
multihop BGP. Default: enabled for direct BGP, disabled otherwise.
<tag><label id="bgp-bfd">bfd <M>switch</M>|graceful</tag>
<tag><label id="bgp-bfd">bfd <M>switch</M>|graceful| { <m/options/ }</tag>
BGP could use BFD protocol as an advisory mechanism for neighbor
liveness and failure detection. If enabled, BIRD setups a BFD session
for the BGP neighbor and tracks its liveness by it. This has an
advantage of an order of magnitude lower detection times in case of
failure. When a neighbor failure is detected, the BGP session is
restarted. Optionally, it can be configured (by <cf/graceful/ argument)
to trigger graceful restart instead of regular restart. Note that BFD
to trigger graceful restart instead of regular restart. It is also
possible to specify section with per-peer BFD session options instead of
just switch argument. Most BFD session specific options are allowed here
with the exception of authentication options. here Note that BFD
protocol also has to be configured, see <ref id="bfd" name="BFD">
section for details. Default: disabled.
@ -2398,6 +2459,25 @@ using the following configuration parameters:
completely disabled and you should ensure loop-free behavior by some
other means. Default: 0 (no local AS number allowed).
<tag><label id="bgp-allow-as-sets">allow as sets [<m/switch/]</tag>
AS path attribute received with BGP routes may contain not only
sequences of AS numbers, but also sets of AS numbers. These rarely used
artifacts are results of inter-AS route aggregation. AS sets are
deprecated (<rfc id="6472">), and likely to be rejected in the future,
as they complicate security features like RPKI validation. When this
option is disabled, then received AS paths with AS sets are rejected as
malformed and corresponding BGP updates are treated as withdraws.
Default: on.
<tag><label id="bgp-enforce-first-as">enforce first as [<m/switch/]</tag>
Routes received from an EBGP neighbor are generally expected to have the
first (leftmost) AS number in their AS path equal to the neighbor AS
number. This is not enforced by default as there are legitimate cases
where it is not true, e.g. connections to route servers. When this
option is enabled, routes with non-matching first AS number are rejected
and corresponding updates are treated as withdraws. The option is valid
on EBGP sessions only. Default: off.
<tag><label id="bgp-enable-route-refresh">enable route refresh <m/switch/</tag>
After the initial route exchange, BGP protocol uses incremental updates
to keep BGP speakers synchronized. Sometimes (e.g., if BGP speaker
@ -2472,8 +2552,8 @@ using the following configuration parameters:
<tag><label id="bgp-enable-extended-messages">enable extended messages <m/switch/</tag>
The BGP protocol uses maximum message length of 4096 bytes. This option
provides an extension to allow extended messages with length up
to 65535 bytes. Default: off.
provides an extension (<rfc id="8654">) to allow extended messages with
length up to 65535 bytes. Default: off.
<tag><label id="bgp-capabilities">capabilities <m/switch/</tag>
Use capability advertisement to advertise optional capabilities. This is
@ -2490,6 +2570,9 @@ using the following configuration parameters:
This option is relevant to IPv4 mode with enabled capability
advertisement only. Default: on.
<tag><label id="bgp-advertise-hostname">advertise hostname <m/switch/</tag>
Advertise hostname capability along with the hostname. Default: off.
<tag><label id="bgp-disable-after-error">disable after error <m/switch/</tag>
When an error is encountered (either locally or by the other side),
disable the instance automatically and wait for an administrator to fix
@ -2658,30 +2741,15 @@ be used in explicit configuration.
BGP session (if acceptable), or the preferred address of an associated
interface.
<tag><label id="bgp-missing-lladdr">missing lladdr self|drop|ignore</tag>
Next Hop attribute in BGP-IPv6 sometimes contains just the global IPv6
address, but sometimes it has to contain both global and link-local IPv6
addresses. This option specifies what to do if BIRD have to send both
addresses but does not know link-local address. This situation might
happen when routes from other protocols are exported to BGP, or when
improper updates are received from BGP peers. <cf/self/ means that BIRD
advertises its own local address instead. <cf/drop/ means that BIRD
skips that prefixes and logs error. <cf/ignore/ means that BIRD ignores
the problem and sends just the global address (and therefore forms
improper BGP update). Default: <cf/self/, unless BIRD is configured as a
route server (option <cf/rs client/), in that case default is <cf/ignore/,
because route servers usually do not forward packets themselves.
<tag><label id="bgp-gateway">gateway direct|recursive</tag>
For received routes, their <cf/gw/ (immediate next hop) attribute is
computed from received <cf/bgp_next_hop/ attribute. This option
specifies how it is computed. Direct mode means that the IP address from
<cf/bgp_next_hop/ is used if it is directly reachable, otherwise the
neighbor IP address is used. Recursive mode means that the gateway is
computed by an IGP routing table lookup for the IP address from
<cf/bgp_next_hop/. Note that there is just one level of indirection in
recursive mode - the route obtained by the lookup must not be recursive
itself, to prevent mutually recursive routes.
<cf/bgp_next_hop/ is used and must be directly reachable. Recursive mode
means that the gateway is computed by an IGP routing table lookup for
the IP address from <cf/bgp_next_hop/. Note that there is just one level
of indirection in recursive mode - the route obtained by the lookup must
not be recursive itself, to prevent mutually recursive routes.
Recursive mode is the behavior specified by the BGP
standard. Direct mode is simpler, does not require any routes in a
@ -2743,6 +2811,36 @@ be used in explicit configuration.
TX direction. When active, all available routes accepted by the export
filter are advertised to the neighbor. Default: off.
<tag><label id="bgp-aigp">aigp <m/switch/|originate</tag>
The BGP protocol does not use a common metric like other routing
protocols, instead it uses a set of criteria for route selection
consisting both overall AS path length and a distance to the nearest AS
boundary router. Assuming that metrics of different autonomous systems
are incomparable, once a route is propagated from an AS to a next one,
the distance in the old AS does not matter.
The AIGP extension (<rfc id="7311">) allows to propagate accumulated
IGP metric (in the AIGP attribute) through both IBGP and EBGP links,
computing total distance through multiple autonomous systems (assuming
they use comparable IGP metric). The total AIGP metric is compared in
the route selection process just after Local Preference comparison (and
before AS path length comparison).
This option controls whether AIGP attribute propagation is allowed on
the session. Optionally, it can be set to <cf/originate/, which not only
allows AIGP attribute propagation, but also new AIGP attributes are
automatically attached to non-BGP routes with valid IGP metric (e.g.
<cf/ospf_metric1/) as they are exported to the BGP session. Default:
enabled for IBGP (and intra-confederation EBGP), disabled for regular
EBGP.
<tag><label id="bgp-cost">cost <m/number/</tag>
When BGP <ref id="bgp-gateway" name="gateway mode"> is <cf/recursive/
(mainly multihop IBGP sessions), then the distance to BGP next hop is
based on underlying IGP metric. This option specifies the distance to
BGP next hop for BGP sessions in direct gateway mode (mainly direct
EBGP sessions).
<tag><label id="bgp-graceful-restart-c">graceful restart <m/switch/</tag>
Although BGP graceful restart is configured mainly by protocol-wide
<ref id="bgp-graceful-restart" name="options">, it is possible to
@ -2811,9 +2909,11 @@ some of them (marked with `<tt/O/') are optional.
presence of which indicates that the route has been aggregated from
multiple routes by some router on the path from the originator.
<!-- we don't handle aggregators right since they are of a very obscure type
<tag>bgp_aggregator</tag>
-->
<tag><label id="rta-bgp-aggregator">void bgp_aggregator [O]</tag>
This is an optional attribute specifying AS number and IP address of the
BGP router that created the route by aggregating multiple BGP routes.
Currently, the attribute is not accessible from filters.
<tag><label id="rta-bgp-community">clist bgp_community [O]</tag>
List of community values associated with the route. Each such value is a
pair (represented as a <cf/pair/ data type inside the filters) of 16-bit
@ -2848,6 +2948,11 @@ some of them (marked with `<tt/O/') are optional.
<tag><label id="rta-bgp-cluster-list">clist bgp_cluster_list [I, O]</tag>
This attribute contains a list of cluster IDs of route reflectors. Each
route reflector prepends its cluster ID when reflecting the route.
<tag><label id="rta-bgp-aigp">void bgp_aigp [O]</tag>
This attribute contains accumulated IGP metric, which is a total
distance to the destination through multiple autonomous systems.
Currently, the attribute is not accessible from filters.
</descrip>
<sect1>Example
@ -3324,18 +3429,18 @@ protocol ospf [v2|v3] &lt;name&gt; {
networks {
&lt;prefix&gt;;
&lt;prefix&gt; hidden;
}
};
external {
&lt;prefix&gt;;
&lt;prefix&gt; hidden;
&lt;prefix&gt; tag &lt;num&gt;;
}
};
stubnet &lt;prefix&gt;;
stubnet &lt;prefix&gt; {
hidden &lt;switch&gt;;
summary &lt;switch&gt;;
cost &lt;num&gt;;
}
};
interface &lt;interface pattern&gt; [instance &lt;num&gt;] {
cost &lt;num&gt;;
stub &lt;switch&gt;;
@ -3355,6 +3460,7 @@ protocol ospf [v2|v3] &lt;name&gt; {
strict nonbroadcast &lt;switch&gt;;
real broadcast &lt;switch&gt;;
ptp netmask &lt;switch&gt;;
ptp address &lt;switch&gt;;
check link &lt;switch&gt;;
bfd &lt;switch&gt;;
ecmp weight &lt;num&gt;;
@ -3691,11 +3797,28 @@ protocol ospf [v2|v3] &lt;name&gt; {
In <cf/type ptp/ network configurations, OSPFv2 implementations should
ignore received netmask field in hello packets and should send hello
packets with zero netmask field on unnumbered PtP links. But some OSPFv2
implementations perform netmask checking even for PtP links. This option
specifies whether real netmask will be used in hello packets on <cf/type
ptp/ interfaces. You should ignore this option unless you meet some
compatibility problems related to this issue. Default value is no for
unnumbered PtP links, yes otherwise.
implementations perform netmask checking even for PtP links.
This option specifies whether real netmask will be used in hello packets
on <cf/type ptp/ interfaces. You should ignore this option unless you
meet some compatibility problems related to this issue. Default value is
no for unnumbered PtP links, yes otherwise.
<tag><label id="ospf-ptp-address">ptp address <m/switch/</tag>
In <cf/type ptp/ network configurations, OSPFv2 implementations should
use IP address for regular PtP links and interface id for unnumbered PtP
links in data field of link description records in router LSA. This data
field has only local meaning for PtP links, but some broken OSPFv2
implementations assume there is an IP address and use it as a next hop
in SPF calculations. Note that interface id for unnumbered PtP links is
necessary when graceful restart is enabled to distinguish PtP links with
the same local IP address.
This option specifies whether an IP address will be used in data field
for <cf/type ptp/ interfaces, it is ignored for other interfaces. You
should ignore this option unless you meet some compatibility problems
related to this issue. Default value is no for unnumbered PtP links when
graceful restart is enabled, yes otherwise.
<tag><label id="ospf-check-link">check link <M>switch</M></tag>
If set, a hardware link state (reported by OS) is taken into consideration.
@ -3842,7 +3965,7 @@ protocol ospf MyOSPF {
networks {
172.16.1.0/24;
172.16.2.0/24 hidden;
}
};
interface "-arc0" , "arc*" {
type nonbroadcast;
authentication none;
@ -4375,7 +4498,8 @@ you can't use RIP on networks where maximal distance is higher than 15
hosts.
<p>BIRD supports RIPv1 (<rfc id="1058">), RIPv2 (<rfc id="2453">), RIPng (<rfc
id="2080">), and RIP cryptographic authentication (<rfc id="4822">).
id="2080">), Triggered RIP for demand circuits (<rfc id="2091">), and RIP
cryptographic authentication (<rfc id="4822">).
<p>RIP is a very simple protocol, and it has a lot of shortcomings. Slow
convergence, big network load and inability to handle larger networks makes it
@ -4405,6 +4529,7 @@ protocol rip [ng] [&lt;name&gt;] {
version 1|2;
split horizon &lt;switch&gt;;
poison reverse &lt;switch&gt;;
demand circuit &lt;switch&gt;;
check zero &lt;switch&gt;;
update time &lt;number&gt;;
timeout time &lt;number&gt;;
@ -4509,6 +4634,16 @@ protocol rip [ng] [&lt;name&gt;] {
used. The poisoned reverse has some advantages in faster convergence,
but uses more network traffic. Default: yes.
<tag><label id="rip-iface-demand-circuit">demand circuit <m/switch/</tag>
Regular RIP sends periodic full updates on an interface. There is the
Triggered RIP extension for demand circuits (<rfc id="2091">), which
removes periodic updates and introduces update acknowledgments. When
enabled, there is no RIP communication in steady-state network. Note
that in order to work, it must be enabled on both sides. As there are
no hello packets, it depends on hardware link state to detect neighbor
failures. Also, it is designed for PtP links and it does not work
properly with multiple RIP neighbors on an interface. Default: no.
<tag><label id="rip-iface-check-zero">check zero <m/switch/</tag>
Received RIPv1 packets with non-zero values in reserved fields should
be discarded. This option specifies whether the check is performed or
@ -4639,21 +4774,21 @@ protocol rip {
<sect1>Introduction
<p>The Resource Public Key Infrastructure (RPKI) is mechanism for origin
validation of BGP routes (RFC 6480). BIRD supports only so-called RPKI-based
origin validation. There is implemented RPKI to Router (RPKI-RTR) protocol (RFC
6810). It uses some of the RPKI data to allow a router to verify that the
autonomous system announcing an IP address prefix is in fact authorized to do
so. This is not crypto checked so can be violated. But it should prevent the
vast majority of accidental hijackings on the Internet today, e.g. the famous
Pakastani accidental announcement of YouTube's address space.
validation of BGP routes (<rfc id="6480">). BIRD supports only so-called
RPKI-based origin validation. There is implemented RPKI to Router (RPKI-RTR)
protocol (<rfc id="6810">). It uses some of the RPKI data to allow a router to
verify that the autonomous system announcing an IP address prefix is in fact
authorized to do so. This is not crypto checked so can be violated. But it
should prevent the vast majority of accidental hijackings on the Internet today,
e.g. the famous Pakistani accidental announcement of YouTube's address space.
<p>The RPKI-RTR protocol receives and maintains a set of ROAs from a cache
server (also called validator). You can validate routes (RFC 6483) using
function <cf/roa_check()/ in filter and set it as import filter at the BGP
protocol. BIRD should re-validate all of affected routes after RPKI update by
RFC 6811, but we don't support it yet! You can use a BIRD's client command
<cf>reload in <m/bgp_protocol_name/</cf> for manual call of revalidation of all
routes.
server (also called validator). You can validate routes (<rfc id="6483">,
<rfc id="6811">) using function <cf/roa_check()/ in filter and set it as import
filter at the BGP protocol. BIRD offers crude automatic re-validating of
affected routes after RPKI update, see option <ref id="proto-rpki-reload"
name="rpki reload">. Or you can use a BIRD client command <cf>reload in
<m/bgp_protocol_name/</cf> for manual call of revalidation of all routes.
<sect1>Supported transports
<p>
@ -4727,6 +4862,11 @@ specify both channels.
suppresses updating this value by a cache server.
Default: 7200 seconds
<tag>ignore max length <m/switch/</tag>
Ignore received max length in ROA records and use max value (32 or 128)
instead. This may be useful for implementing loose RPKI check for
blackholes. Default: disabled.
<tag>transport tcp</tag> Unprotected transport over TCP. It's a default
transport. Should be used only on secure private networks.
Default: tcp
@ -4775,7 +4915,7 @@ protocol rpki {
filter peer_in_v4 {
if (roa_check(r4, net, bgp_path.last) = ROA_INVALID) then
{
print "Ignore invalid ROA ", net, " for ASN ", bgp_path.last;
print "Ignore RPKI invalid ", net, " for ASN ", bgp_path.last;
reject;
}
accept;
@ -4829,8 +4969,15 @@ default route to prevent routing loops).
<p>There are three classes of definitions in Static protocol configuration --
global options, static route definitions, and per-route options. Usually, the
definition of the protocol contains mainly a list of static routes.
Static routes have no specific attributes.
definition of the protocol contains mainly a list of static routes. Static
routes have no specific attributes, but <ref id="rta-igp-metric" name="igp_metric">
attribute is used to compare static routes with the same preference.
<p>The list of static routes may contain multiple routes for the same network
(usually, but not necessary, distinquished by <cf/preference/ or <cf/igp_metric/),
but only routes of the same network type are allowed, as the static protocol
has just one channel. E.g., to have both IPv4 and IPv6 static routes, define two
static protocols, each with appropriate routes and channel.
<p>Global options:
@ -4855,8 +5002,8 @@ Static routes have no specific attributes.
<ref id="type-prefix" name="dependent on network type">.
<descrip>
<tag>route <m/prefix/ via <m/ip/|<m/"interface"/ [mpls <m/num/[/<m/num/[/<m/num/[...]]]]</tag>
Next hop routes may bear one or more <ref id="route-next-hop" name="next hops">.
<tag>route <m/prefix/ via <m/ip/|<m/"interface"/ [<m/per-nexthop options/] [via ...]</tag>
Regular routes may bear one or more <ref id="route-next-hop" name="next hops">.
Every next hop is preceded by <cf/via/ and configured as shown.
<tag>route <m/prefix/ recursive <m/ip/ [mpls <m/num/[/<m/num/[/<m/num/[...]]]]</tag>
@ -4875,6 +5022,46 @@ the next hop of the route is not a neighbor at the moment), Static just
uninstalls the route from the table it is connected to and adds it again as soon
as the destination becomes adjacent again.
<sect2>Per-nexthop options
<p>There are several options that in a case of multipath route are per-nexthop
(i.e., they can be used multiple times for a route, one time for each nexthop).
Syntactically, they are not separate options but just parts of <cf/route/
statement after each <cf/via/ statement, not separated by semicolons. E.g.,
statement <cf/route 10.0.0.0/8 via 192.0.2.1 bfd weight 1 via 192.0.2.2 weight
2;/ describes a route with two nexthops, the first nexthop has two per-nexthop
options (<cf/bfd/ and <cf/weight 1/), the second nexthop has just <cf/weight 2/.
<descrip>
<tag><label id="static-route-bfd">bfd <m/switch/</tag>
The Static protocol could use BFD protocol for next hop liveness
detection. If enabled, a BFD session to the route next hop is created
and the static route is BFD-controlled -- the static route is announced
only if the next hop liveness is confirmed by BFD. If the BFD session
fails, the static route (or just the affected nexthop from multiple
ones) is removed. Note that this is a bit different compared to other
protocols, which may use BFD as an advisory mechanism for fast failure
detection but ignore it if a BFD session is not even established. Note
that BFD protocol also has to be configured, see <ref id="bfd" name="BFD">
section for details. Default value is no.
<tag><label id="static-route-mpls">mpls <m/num/[/<m/num/[/<m/num/[...]]]</tag>
MPLS labels that should be pushed to packets forwarded by the route.
The option could be used for both IP routes (on MPLS ingress routers)
and MPLS switching rules (on MPLS transit routers). Default value is
no labels.
<tag><label id="static-route-onlink">onlink <m/switch/</tag>
Onlink flag means that the specified nexthop is accessible on the
(specified) interface regardless of IP prefixes of the interface. The
interface must be attached to nexthop IP address using link-local-scope
format (e.g. <cf/192.0.2.1%eth0/). Default value is no.
<tag><label id="static-route-weight">weight <m/switch/</tag>
For multipath routes, this value specifies a relative weight of the
nexthop. Allowed values are 1-256. Default value is 1.
</descrip>
<sect1>Route Origin Authorization
<p>The ROA config is just <cf>route <m/prefix/ max <m/int/ as <m/int/</cf> with no nexthop.
@ -5013,21 +5200,6 @@ protocol static {
<sect1>Per-route options
<p>
<descrip>
<tag><label id="static-route-bfd">bfd <m/switch/</tag>
The Static protocol could use BFD protocol for next hop liveness
detection. If enabled, a BFD session to the route next hop is created
and the static route is BFD-controlled -- the static route is announced
only if the next hop liveness is confirmed by BFD. If the BFD session
fails, the static route is removed. Note that this is a bit different
compared to other protocols, which may use BFD as an advisory mechanism
for fast failure detection but ignores it if a BFD session is not even
established.
This option can be used for static routes with a direct next hop, or
also for for individual next hops in a static multipath route (see
above). Note that BFD protocol also has to be configured, see
<ref id="bfd" name="BFD"> section for details. Default value is no.
<tag><label id="static-route-filter"><m/filter expression/</tag>
This is a special option that allows filter expressions to be configured
on per-route basis. Can be used multiple times. These expressions are
@ -5037,7 +5209,8 @@ protocol static {
exported to the OSPF protocol.
</descrip>
<sect1>Example static config
<sect1>Example static configs
<label id="static-example">
<p><code>
protocol static {
@ -5048,16 +5221,29 @@ protocol static {
via 198.51.100.10 weight 2
via 198.51.100.20 bfd # BFD-controlled next hop
via 192.0.2.1;
route 203.0.113.0/24 unreachable; # Sink route
route 203.0.113.0/24 blackhole; # Sink route
route 10.2.0.0/24 via "arc0"; # Secondary network
route 192.168.10.0/24 via 198.51.100.100 {
ospf_metric1 = 20; # Set extended attribute
}
route 192.168.10.0/24 via 198.51.100.100 {
};
route 192.168.11.0/24 via 198.51.100.100 {
ospf_metric2 = 100; # Set extended attribute
ospf_tag = 2; # Set extended attribute
bfd; # BFD-controlled route
}
};
route 192.168.12.0/24 via 198.51.100.100 {
bgp_community.add((65535, 65281)); # Set extended BGP attribute
bgp_large_community.add((64512, 1, 1)); # Set extended BGP attribute
};
}
protocol static {
ipv6; # Channel is mandatory
route 2001:db8:10::/48 via 2001:db8:1::1; # Route with global nexthop
route 2001:db8:20::/48 via fe80::10%eth0; # Route with link-local nexthop
route 2001:db8:30::/48 via fe80::20%'eth1.60'; # Iface with non-alphanumeric characters
route 2001:db8:40::/48 via "eth2"; # Direct route to eth2
route 2001:db8::/32 unreachable; # Unreachable route
route ::/0 via 2001:db8:1::1 bfd; # BFD-controlled default route
}
</code>

View File

@ -185,159 +185,6 @@ f_generate_empty(struct f_dynamic_attr dyn)
return f_new_inst(FI_EA_SET, f_new_inst(FI_CONSTANT, empty), dyn);
}
#if 0
static inline struct f_inst *
f_generate_dpair(struct f_inst *t1, struct f_inst *t2)
{
struct f_inst *rv;
if ((t1->fi_code == FI_CONSTANT) && (t2->fi_code == FI_CONSTANT)) {
if ((t1->val.type != T_INT) || (t2->val.type != T_INT))
cf_error( "Can't operate with value of non-integer type in pair constructor");
check_u16(t1->a[1].i);
check_u16(t2->a[1].i);
rv = f_new_inst(FI_CONSTANT);
rv->val = (struct f_val) {
.type = T_PAIR,
.val.i = pair(t1->a[1].i, t2->a[1].i),
};
}
else {
rv = f_new_inst(FI_PAIR_CONSTRUCT);
rv->a[0].p = t1;
rv->a[1].p = t2;
}
return rv;
}
static inline struct f_inst *
f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
{
struct f_inst *rv;
int c1 = 0, c2 = 0, ipv4_used = 0;
u32 key = 0, val2 = 0;
if (tk->fi_code == FI_CONSTANT) {
c1 = 1;
struct f_val *val = &(tk->val);
if (val->type == T_INT) {
ipv4_used = 0; key = val->val.i;
}
else if (tk->val.type == T_QUAD) {
ipv4_used = 1; key = val->val.i;
}
else if ((val->type == T_IP) && ipa_is_ip4(val->val.ip)) {
ipv4_used = 1; key = ipa_to_u32(val->val.ip);
}
else
cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor");
}
if (tv->fi_code == FI_CONSTANT) {
if (tv->val.type != T_INT)
cf_error("Can't operate with value of non-integer type in EC constructor");
c2 = 1;
val2 = tv->val.val.i;
}
if (c1 && c2) {
u64 ec;
if (kind == EC_GENERIC) {
ec = ec_generic(key, val2);
}
else if (ipv4_used) {
check_u16(val2);
ec = ec_ip4(kind, key, val2);
}
else if (key < 0x10000) {
ec = ec_as2(kind, key, val2);
}
else {
check_u16(val2);
ec = ec_as4(kind, key, val2);
}
rv = f_new_inst(FI_CONSTANT);
rv->val = (struct f_val) {
.type = T_EC,
.val.ec = ec,
};
}
else {
rv = f_new_inst(FI_EC_CONSTRUCT);
rv->aux = kind;
rv->a[0].p = tk;
rv->a[1].p = tv;
}
return rv;
}
static inline struct f_inst *
f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3)
{
struct f_inst *rv;
if ((t1->fi_code == FI_CONSTANT) && (t2->fi_code == FI_CONSTANT) && (t3->fi_code == FI_CONSTANT)) {
if ((t1->val.type != T_INT) || (t2->val.type != T_INT) || (t3->val.type != T_INT))
cf_error( "LC - Can't operate with value of non-integer type in tuple constructor");
rv = f_new_inst(FI_CONSTANT);
rv->val = (struct f_val) {
.type = T_LC,
.val.lc = (lcomm) { t1->a[1].i, t2->a[1].i, t3->a[1].i },
};
}
else
{
rv = f_new_inst(FI_LC_CONSTRUCT);
rv->a[0].p = t1;
rv->a[1].p = t2;
rv->a[2].p = t3;
}
return rv;
}
static inline struct f_inst *
f_generate_path_mask(struct f_inst *t)
{
uint len = 0;
uint dyn = 0;
for (const struct f_inst *tt = t; tt; tt = tt->next) {
if (tt->fi_code != FI_CONSTANT)
dyn++;
len++;
}
if (dyn) {
struct f_inst *pmc = f_new_inst(FI_PATHMASK_CONSTRUCT);
pmc->a[0].p = t;
pmc->a[1].i = len;
return pmc;
}
struct f_path_mask *pm = cfg_allocz(sizeof(struct f_path_mask) + len * sizeof(struct f_path_mask_item));
uint i = 0;
for (const struct f_inst *tt = t; tt; tt = tt->next)
pm->item[i++] = tt->val.val.pmi;
pm->len = i;
struct f_inst *pmc = f_new_inst(FI_CONSTANT);
pmc->val = (struct f_val) { .type = T_PATH_MASK, .val.path_mask = pm, };
return pmc;
}
#endif
/*
* Remove all new lines and doubled whitespaces
* and convert all tabulators to spaces
@ -426,14 +273,14 @@ assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const
CF_DECLS
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
ACCEPT, REJECT, ERROR, QUITBIRD,
ACCEPT, REJECT, ERROR,
INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
IF, THEN, ELSE, CASE,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX,
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT,
PREFERENCE,
ROA_CHECK, ASN, SRC,
ROA_CHECK, ASN, SRC, DST,
IS_V4, IS_V6,
LEN, MAXLEN,
DEFINED,
@ -775,8 +622,8 @@ fprefix:
;
fprefix_set:
fprefix { $$ = f_new_trie(cfg_mem, sizeof(struct f_trie_node)); trie_add_prefix($$, &($1.net), $1.lo, $1.hi); }
| fprefix_set ',' fprefix { $$ = $1; trie_add_prefix($$, &($3.net), $3.lo, $3.hi); }
fprefix { $$ = f_new_trie(cfg_mem, 0); trie_add_prefix($$, &($1.net), $1.lo, $1.hi); }
| fprefix_set ',' fprefix { $$ = $1; if (!trie_add_prefix($$, &($3.net), $3.lo, $3.hi)) cf_error("Mixed IPv4/IPv6 prefixes in prefix set"); }
;
switch_body: /* EMPTY */ { $$ = NULL; }
@ -815,6 +662,7 @@ bgp_path_tail:
}
| '*' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_ASTERISK }, }); $$->next = $2; }
| '?' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_QUESTION }, }); $$->next = $2; }
| '+' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_LOOP }, }); $$->next = $2; }
| bgp_path_expr bgp_path_tail { $$ = $1; $$->next = $2; }
| { $$ = NULL; }
;
@ -902,6 +750,7 @@ static_attr:
| DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 0); }
| IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
| IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); }
| WEIGHT { $$ = f_new_static_attr(T_INT, SA_WEIGHT, 0); }
;
term:
@ -940,7 +789,8 @@ term:
| term '.' LEN { $$ = f_new_inst(FI_LENGTH, $1); }
| term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN, $1); }
| term '.' ASN { $$ = f_new_inst(FI_ROA_ASN, $1); }
| term '.' SRC { $$ = f_new_inst(FI_SADR_SRC, $1); }
| term '.' SRC { $$ = f_new_inst(FI_NET_SRC, $1); }
| term '.' DST { $$ = f_new_inst(FI_NET_DST, $1); }
| term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK, $1, $5); }
| term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST, $1); }
| term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST, $1); }
@ -974,8 +824,7 @@ term:
;
break_command:
QUITBIRD { $$ = F_QUITBIRD; }
| ACCEPT { $$ = F_ACCEPT; }
ACCEPT { $$ = F_ACCEPT; }
| REJECT { $$ = F_REJECT; }
| ERROR { $$ = F_ERROR; }
;

View File

@ -25,6 +25,49 @@
#include "filter/f-inst.h"
#include "filter/data.h"
static const char * const f_type_str[] = {
[T_VOID] = "void",
[T_INT] = "int",
[T_BOOL] = "bool",
[T_PAIR] = "pair",
[T_QUAD] = "quad",
[T_ENUM_RTS] = "enum rts",
[T_ENUM_BGP_ORIGIN] = "enum bgp_origin",
[T_ENUM_SCOPE] = "enum scope",
[T_ENUM_RTC] = "enum rtc",
[T_ENUM_RTD] = "enum rtd",
[T_ENUM_ROA] = "enum roa",
[T_ENUM_NETTYPE] = "enum nettype",
[T_ENUM_RA_PREFERENCE] = "enum ra_preference",
[T_ENUM_AF] = "enum af",
[T_IP] = "ip",
[T_NET] = "prefix",
[T_STRING] = "string",
[T_PATH_MASK] = "bgpmask",
[T_PATH] = "bgppath",
[T_CLIST] = "clist",
[T_EC] = "ec",
[T_ECLIST] = "eclist",
[T_LC] = "lc",
[T_LCLIST] = "lclist",
[T_RD] = "rd",
};
const char *
f_type_name(enum f_type t)
{
if (t < ARRAY_SIZE(f_type_str))
return f_type_str[t] ?: "?";
if ((t == T_SET) || (t == T_PREFIX_SET))
return "set";
return "?";
}
const struct f_val f_const_empty_path = {
.type = T_PATH,
.val.ad = &null_adata,
@ -50,6 +93,8 @@ adata_empty(struct linpool *pool, int l)
static void
pm_format(const struct f_path_mask *p, buffer *buf)
{
int loop = 0;
buffer_puts(buf, "[= ");
for (uint i=0; i<p->len; i++)
@ -68,14 +113,28 @@ pm_format(const struct f_path_mask *p, buffer *buf)
buffer_puts(buf, "* ");
break;
case PM_LOOP:
loop = 1;
break;
case PM_ASN_RANGE:
buffer_print(buf, "%u..%u ", p->item[i].from, p->item[i].to);
break;
case PM_ASN_SET:
tree_format(p->item[i].set, buf);
buffer_puts(buf, " ");
break;
case PM_ASN_EXPR:
ASSERT(0);
}
if (loop && (p->item[i].kind != PM_LOOP))
{
buffer_puts(buf, "+ ");
loop = 0;
}
}
buffer_puts(buf, "=]");
@ -167,6 +226,10 @@ pmi_same(const struct f_path_mask_item *mi1, const struct f_path_mask_item *mi2)
if (mi1->to != mi2->to)
return 0;
break;
case PM_ASN_SET:
if (!same_tree(mi1->set, mi2->set))
return 0;
break;
}
return 1;
@ -176,6 +239,7 @@ static int
pm_same(const struct f_path_mask *m1, const struct f_path_mask *m2)
{
if (m1->len != m2->len)
return 0;
for (uint i=0; i<m1->len; i++)
if (!pmi_same(&(m1->item[i]), &(m2->item[i])))

View File

@ -38,6 +38,7 @@ enum f_type {
T_ENUM_ROA = 0x35,
T_ENUM_NETTYPE = 0x36,
T_ENUM_RA_PREFERENCE = 0x37,
T_ENUM_AF = 0x38,
/* new enums go here */
T_ENUM_EMPTY = 0x3f, /* Special hack for atomic_aggr */
@ -71,7 +72,7 @@ struct f_val {
lcomm lc;
ip_addr ip;
const net_addr *net;
char *s;
const char *s;
const struct f_tree *t;
const struct f_trie *ti;
const struct adata *ad;
@ -98,6 +99,7 @@ enum f_sa_code {
SA_DEST,
SA_IFNAME,
SA_IFINDEX,
SA_WEIGHT,
} PACKED;
/* Static attribute definition (members of struct rta) */
@ -137,19 +139,35 @@ struct f_tree {
void *data;
};
struct f_trie_node4
{
ip4_addr addr, mask, accept;
uint plen;
struct f_trie_node4 *c[2];
};
struct f_trie_node6
{
ip6_addr addr, mask, accept;
uint plen;
struct f_trie_node6 *c[2];
};
struct f_trie_node
{
ip_addr addr, mask, accept;
uint plen;
struct f_trie_node *c[2];
union {
struct f_trie_node4 v4;
struct f_trie_node6 v6;
};
};
struct f_trie
{
linpool *lp;
int zero;
uint node_size;
struct f_trie_node root[0]; /* Root trie node follows */
u8 zero;
s8 ipv4; /* -1 for undefined / empty */
u16 data_size; /* Additional data for each trie node */
struct f_trie_node root; /* Root trie node */
};
struct f_tree *f_new_tree(void);
@ -157,8 +175,9 @@ struct f_tree *build_tree(struct f_tree *);
const struct f_tree *find_tree(const struct f_tree *t, const struct f_val *val);
int same_tree(const struct f_tree *t0, const struct f_tree *t2);
void tree_format(const struct f_tree *t, buffer *buf);
void tree_walk(const struct f_tree *t, void (*hook)(const struct f_tree *, void *), void *data);
struct f_trie *f_new_trie(linpool *lp, uint node_size);
struct f_trie *f_new_trie(linpool *lp, uint data_size);
void *trie_add_prefix(struct f_trie *t, const net_addr *n, uint l, uint h);
int trie_match_net(const struct f_trie *t, const net_addr *n);
int trie_same(const struct f_trie *t1, const struct f_trie *t2);
@ -166,6 +185,8 @@ void trie_format(const struct f_trie *t, buffer *buf);
#define F_CMP_ERROR 999
const char *f_type_name(enum f_type t);
int val_same(const struct f_val *v1, const struct f_val *v2);
int val_compare(const struct f_val *v1, const struct f_val *v2);
void val_format(const struct f_val *v, buffer *buf);

View File

@ -40,6 +40,7 @@ m4_divert(-1)m4_dnl
# 106 comparator body
# 107 struct f_line_item content
# 108 interpreter body
# 109 iterator body
#
# Here are macros to allow you to _divert to the right directions.
m4_define(FID_STRUCT_IN, `m4_divert(101)')
@ -50,6 +51,7 @@ m4_define(FID_LINEARIZE_BODY, `m4_divert(105)')
m4_define(FID_SAME_BODY, `m4_divert(106)')
m4_define(FID_LINE_IN, `m4_divert(107)')
m4_define(FID_INTERPRET_BODY, `m4_divert(108)')
m4_define(FID_ITERATE_BODY, `m4_divert(109)')
# Sometimes you want slightly different code versions in different
# outputs.
@ -104,7 +106,7 @@ FID_STRUCT_IN()m4_dnl
struct f_inst * f$1;
FID_NEW_ARGS()m4_dnl
, struct f_inst * f$1
FID_NEW_BODY
FID_NEW_BODY()m4_dnl
whati->f$1 = f$1;
for (const struct f_inst *child = f$1; child; child = child->next) {
what->size += child->size;
@ -138,7 +140,7 @@ FID_IFCONST([[
}
FID_IFCONST([[
const struct f_inst **items = NULL;
if (constargs) {
if (constargs && whati->varcount) {
items = alloca(whati->varcount * sizeof(struct f_inst *));
const struct f_inst *child = fvar;
for (uint i=0; child; i++)
@ -160,9 +162,28 @@ FID_HIC(,[[
')
# Some arguments need to check their type. After that, ARG_ANY is called.
m4_define(ARG, `ARG_ANY($1)
m4_define(ARG, `ARG_ANY($1) ARG_TYPE($1,$2)')
m4_define(ARG_TYPE, `ARG_TYPE_STATIC($1,$2) ARG_TYPE_DYNAMIC($1,$2)')
m4_define(ARG_TYPE_STATIC, `
FID_NEW_BODY()m4_dnl
if (f$1->type && (f$1->type != ($2)) && !f_const_promotion(f$1, ($2)))
cf_error("Argument $1 of %s must be of type %s, got type %s",
f_instruction_name(what->fi_code), f_type_name($2), f_type_name(f$1->type));
FID_INTERPRET_BODY()')
m4_define(ARG_TYPE_DYNAMIC, `
FID_INTERPRET_EXEC()m4_dnl
if (v$1.type != $2) runtime("Argument $1 of instruction %s must be of type $2, got 0x%02x", f_instruction_name(what->fi_code), v$1.type)m4_dnl
if (v$1.type != ($2))
runtime("Argument $1 of %s must be of type %s, got type %s",
f_instruction_name(what->fi_code), f_type_name($2), f_type_name(v$1.type));
FID_INTERPRET_BODY()')
m4_define(ARG_SAME_TYPE, `
FID_NEW_BODY()m4_dnl
if (f$1->type && f$2->type && (f$1->type != f$2->type) &&
!f_const_promotion(f$2, f$1->type) && !f_const_promotion(f$1, f$2->type))
cf_error("Arguments $1 and $2 of %s must be of the same type", f_instruction_name(what->fi_code));
FID_INTERPRET_BODY()')
# Executing another filter line. This replaces the recursion
@ -192,6 +213,8 @@ FID_LINEARIZE_BODY()m4_dnl
item->fl$1 = f_linearize(whati->f$1);
FID_SAME_BODY()m4_dnl
if (!f_same(f1->fl$1, f2->fl$1)) return 0;
FID_ITERATE_BODY()m4_dnl
if (whati->fl$1) BUFFER_PUSH(fit->lines) = whati->fl$1;
FID_INTERPRET_EXEC()m4_dnl
do { if (whati->fl$1) {
LINEX_(whati->fl$1);
@ -202,11 +225,27 @@ FID_INTERPRET_BODY()')
# Some of the instructions have a result. These constructions
# state the result and put it to the right place.
m4_define(RESULT, `RESULT_VAL([[ (struct f_val) { .type = $1, .val.$2 = $3 } ]])')
m4_define(RESULT, `RESULT_TYPE([[$1]]) RESULT_([[$1]],[[$2]],[[$3]])')
m4_define(RESULT_, `RESULT_VAL([[ (struct f_val) { .type = $1, .val.$2 = $3 } ]])')
m4_define(RESULT_VAL, `FID_HIC(, [[do { res = $1; fstk->vcnt++; } while (0)]],
[[return fi_constant(what, $1)]])')
m4_define(RESULT_VOID, `RESULT_VAL([[ (struct f_val) { .type = T_VOID } ]])')
m4_define(ERROR,
`m4_errprint(m4___file__:m4___line__: $*
)m4_m4exit(1)')
# This macro specifies result type and makes there are no conflicting definitions
m4_define(RESULT_TYPE,
`m4_ifdef([[INST_RESULT_TYPE]],
[[m4_ifelse(INST_RESULT_TYPE,$1,,[[ERROR([[Multiple type definitons]])]])]],
[[m4_define(INST_RESULT_TYPE,$1) RESULT_TYPE_($1)]])')
m4_define(RESULT_TYPE_, `
FID_NEW_BODY()m4_dnl
what->type = $1;
FID_INTERPRET_BODY()')
# Some common filter instruction members
m4_define(SYMBOL, `FID_MEMBER(struct symbol *, sym, [[strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)]], "symbol %s", item->sym->name)')
m4_define(RTC, `FID_MEMBER(struct rtable_config *, rtc, [[strcmp(f1->rtc->name, f2->rtc->name)]], "route table %s", item->rtc->name)')
@ -230,6 +269,7 @@ m4_define(ACCESS_RTE, `FID_HIC(,[[do { if (!fs->rte) runtime("No route to access
# 7 dump line item callers
# 8 linearize
# 9 same (filter comparator)
# 10 iterate
# 1 union in struct f_inst
# 3 constructors + interpreter
#
@ -250,6 +290,7 @@ m4_define(FID_DUMP, `FID_ZONE(6, Dump line)')
m4_define(FID_DUMP_CALLER, `FID_ZONE(7, Dump line caller)')
m4_define(FID_LINEARIZE, `FID_ZONE(8, Linearize)')
m4_define(FID_SAME, `FID_ZONE(9, Comparison)')
m4_define(FID_ITERATE, `FID_ZONE(10, Iteration)')
# This macro does all the code wrapping. See inline comments.
m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[
@ -337,14 +378,22 @@ m4_undivert(106)m4_dnl
#undef f2
break;
FID_ITERATE()m4_dnl The iterator
case INST_NAME():
#define whati (&(what->i_]]INST_NAME()[[))
m4_undivert(109)m4_dnl
#undef whati
break;
m4_divert(-1)FID_FLUSH(101,200)m4_dnl And finally this flushes all the unused diversions
]])')
m4_define(INST, `m4_dnl This macro is called on beginning of each instruction.
INST_FLUSH()m4_dnl First, old data is flushed
m4_define([[INST_NAME]], [[$1]])m4_dnl Then we store instruction name,
m4_define([[INST_INVAL]], [[$2]])m4_dnl instruction input value count
m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl and reset NEVER_CONSTANT trigger.
m4_define([[INST_INVAL]], [[$2]])m4_dnl instruction input value count,
m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl reset NEVER_CONSTANT trigger,
m4_undefine([[INST_RESULT_TYPE]])m4_dnl and reset RESULT_TYPE value.
FID_INTERPRET_BODY()m4_dnl By default, every code is interpreter code.
')
@ -404,7 +453,7 @@ FID_WR_PUT(5)
};
const char *
f_instruction_name(enum f_instruction_code fi)
f_instruction_name_(enum f_instruction_code fi)
{
if (fi < (sizeof(f_instruction_name_str) / sizeof(f_instruction_name_str[0])))
return f_instruction_name_str[fi];
@ -430,6 +479,25 @@ fi_constant(struct f_inst *what, struct f_val val)
return what;
}
static int
f_const_promotion(struct f_inst *arg, enum f_type want)
{
if (arg->fi_code != FI_CONSTANT)
return 0;
struct f_val *c = &arg->i_FI_CONSTANT.val;
if ((c->type == T_IP) && ipa_is_ip4(c->val.ip) && (want == T_QUAD)) {
*c = (struct f_val) {
.type = T_QUAD,
.val.i = ipa_to_u32(c->val.ip),
};
return 1;
}
return 0;
}
#define v1 whati->f1->i_FI_CONSTANT.val
#define v2 whati->f2->i_FI_CONSTANT.val
#define v3 whati->f3->i_FI_CONSTANT.val
@ -459,7 +527,7 @@ void f_dump_line(const struct f_line *dest, uint indent)
debug("%sFilter line %p (len=%u)\n", INDENT, dest, dest->len);
for (uint i=0; i<dest->len; i++) {
const struct f_line_item *item = &dest->items[i];
debug("%sInstruction %s at line %u\n", INDENT, f_instruction_name(item->fi_code), item->lineno);
debug("%sInstruction %s at line %u\n", INDENT, f_instruction_name_(item->fi_code), item->lineno);
switch (item->fi_code) {
FID_WR_PUT(7)
default: bug("Unknown instruction %x in f_dump_line", item->fi_code);
@ -494,7 +562,7 @@ f_linearize_concat(const struct f_inst * const inst[], uint count)
for (uint i=0; i<count; i++)
out->len = linearize(out, inst[i], out->len);
#if DEBUGGING
#ifdef LOCAL_DEBUG
f_dump_line(out, 0);
#endif
return out;
@ -527,6 +595,27 @@ FID_WR_PUT(9)
return 1;
}
/* Part of FI_SWITCH filter iterator */
static void
f_add_tree_lines(const struct f_tree *t, void *fit_)
{
struct filter_iterator * fit = fit_;
if (t->data)
BUFFER_PUSH(fit->lines) = t->data;
}
/* Filter line iterator */
void
f_add_lines(const struct f_line_item *what, struct filter_iterator *fit)
{
switch(what->fi_code) {
FID_WR_PUT(10)
}
}
#if defined(__GNUC__) && __GNUC__ >= 6
#pragma GCC diagnostic pop
#endif
@ -541,6 +630,7 @@ FID_WR_PUT(4)m4_dnl
struct f_inst {
struct f_inst *next; /* Next instruction */
enum f_instruction_code fi_code; /* Instruction code */
enum f_type type; /* Type of returned value, if known */
int size; /* How many instructions are underneath */
int lineno; /* Line number */
union {

View File

@ -104,7 +104,7 @@
* m4_dnl (103) [[ put it here ]]
* m4_dnl ...
* m4_dnl if (all arguments are constant)
* m4_dnl (108) [[ put it here ]]
* m4_dnl (108) [[ put it here ]]
* m4_dnl }
* m4_dnl For writing directly to constructor argument list, use FID_NEW_ARGS.
* m4_dnl For computing something in constructor (103), use FID_NEW_BODY.
@ -172,6 +172,22 @@
* m4_dnl use macros f1 and f2.
* m4_dnl For writing directly here, use FID_SAME_BODY.
*
* m4_dnl f_add_lines(...)
* m4_dnl {
* m4_dnl switch (what_->fi_code) {
* m4_dnl case FI_EXAMPLE:
* m4_dnl (109) [[ put it here ]]
* m4_dnl break;
* m4_dnl }
* m4_dnl }
* m4_dnl This code adds new filter lines reachable from the instruction
* m4_dnl to the filter iterator line buffer. This is for instructions
* m4_dnl that changes conrol flow, like FI_CONDITION or FI_CALL, most
* m4_dnl instructions do not need to update it. It is used in generic
* m4_dnl filter iteration code (FILTER_ITERATE*). For accessing your
* m4_dnl custom instruction data, use macros f1 and f2. For writing
* m4_dnl directly here, use FID_ITERATE_BODY.
*
* m4_dnl interpret(...)
* m4_dnl {
* m4_dnl switch (what->fi_code) {
@ -226,6 +242,9 @@
}
INST(FI_AND, 1, 1) {
ARG(1,T_BOOL);
ARG_TYPE_STATIC(2,T_BOOL);
RESULT_TYPE(T_BOOL);
if (v1.val.i)
LINE(2,0);
else
@ -233,6 +252,9 @@
}
INST(FI_OR, 1, 1) {
ARG(1,T_BOOL);
ARG_TYPE_STATIC(2,T_BOOL);
RESULT_TYPE(T_BOOL);
if (!v1.val.i)
LINE(2,0);
else
@ -255,7 +277,7 @@
FID_MEMBER(enum ec_subtype, ecs, f1->ecs != f2->ecs, "ec subtype %s", ec_subtype_str(item->ecs));
int check, ipv4_used;
int ipv4_used;
u32 key, val;
if (v1.type == T_INT) {
@ -273,21 +295,20 @@
val = v2.val.i;
if (ecs == EC_GENERIC) {
check = 0; RESULT(T_EC, ec, ec_generic(key, val));
}
else if (ipv4_used) {
check = 1; RESULT(T_EC, ec, ec_ip4(ecs, key, val));
}
else if (key < 0x10000) {
check = 0; RESULT(T_EC, ec, ec_as2(ecs, key, val));
}
else {
check = 1; RESULT(T_EC, ec, ec_as4(ecs, key, val));
}
if (check && (val > 0xFFFF))
runtime("Value %u > %u out of bounds in EC constructor", val, 0xFFFF);
if (ecs == EC_GENERIC)
RESULT(T_EC, ec, ec_generic(key, val));
else if (ipv4_used)
if (val <= 0xFFFF)
RESULT(T_EC, ec, ec_ip4(ecs, key, val));
else
runtime("4-byte value %u can't be used with IP-address key in extended community", val);
else if (key < 0x10000)
RESULT(T_EC, ec, ec_as2(ecs, key, val));
else
if (val <= 0xFFFF)
RESULT(T_EC, ec, ec_as4(ecs, key, val));
else
runtime("4-byte value %u can't be used with 4-byte ASN in extended community", val);
}
INST(FI_LC_CONSTRUCT, 3, 1) {
@ -306,6 +327,17 @@
for (uint i=0; i<whati->varcount; i++) {
switch (vv(i).type) {
case T_PATH_MASK_ITEM:
if (vv(i).val.pmi.kind == PM_LOOP)
{
if (i == 0)
runtime("Path mask iterator '+' cannot be first");
/* We want PM_LOOP as prefix operator */
pm->item[i] = pm->item[i - 1];
pm->item[i - 1] = vv(i).val.pmi;
break;
}
pm->item[i] = vv(i).val.pmi;
break;
@ -351,6 +383,8 @@
INST(FI_LT, 2, 1) {
ARG_ANY(1);
ARG_ANY(2);
ARG_SAME_TYPE(1, 2);
int i = val_compare(&v1, &v2);
if (i == F_CMP_ERROR)
runtime( "Can't compare values of incompatible types" );
@ -360,6 +394,8 @@
INST(FI_LTE, 2, 1) {
ARG_ANY(1);
ARG_ANY(2);
ARG_SAME_TYPE(1, 2);
int i = val_compare(&v1, &v2);
if (i == F_CMP_ERROR)
runtime( "Can't compare values of incompatible types" );
@ -416,18 +452,7 @@
NEVER_CONSTANT;
ARG_ANY(1);
SYMBOL;
if ((sym->class != (SYM_VARIABLE | v1.type)) && (v1.type != T_VOID))
{
/* IP->Quad implicit conversion */
if ((sym->class == (SYM_VARIABLE | T_QUAD)) && val_is_ip4(&v1))
v1 = (struct f_val) {
.type = T_QUAD,
.val.i = ipa_to_u32(v1.val.ip),
};
else
runtime( "Assigning to variable of incompatible type" );
}
ARG_TYPE(1, sym->class & 0xff);
fstk->vstk[curline.vbase + sym->offset] = v1;
}
@ -435,6 +460,7 @@
INST(FI_VAR_GET, 0, 1) {
SYMBOL;
NEVER_CONSTANT;
RESULT_TYPE(sym->class & 0xff);
RESULT_VAL(fstk->vstk[curline.vbase + sym->offset]);
}
@ -447,6 +473,7 @@
val_dump(&(item->val))
);
RESULT_TYPE(val.type);
RESULT_VAL(val);
}
@ -479,8 +506,6 @@
FID_MEMBER(enum filter_return, fret, f1->fret != f2->fret, "%s", filter_return_str(item->fret));
switch (whati->fret) {
case F_QUITBIRD:
die( "Filter asked me to die" );
case F_ACCEPT: /* Should take care about turning ACCEPT into MODIFY */
case F_ERROR:
case F_REJECT: /* Maybe print complete route along with reason to reject route? */
@ -507,6 +532,7 @@
case SA_DEST: RESULT(sa.f_type, i, rta->dest); break;
case SA_IFNAME: RESULT(sa.f_type, s, rta->nh.iface ? rta->nh.iface->name : ""); break;
case SA_IFINDEX: RESULT(sa.f_type, i, rta->nh.iface ? rta->nh.iface->index : 0); break;
case SA_WEIGHT: RESULT(sa.f_type, i, rta->nh.weight + 1); break;
default:
bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
@ -518,8 +544,7 @@
ACCESS_RTE;
ARG_ANY(1);
STATIC_ATTR;
if (sa.f_type != v1.type)
runtime( "Attempt to set static attribute to incompatible type" );
ARG_TYPE(1, sa.f_type);
f_rta_cow(fs);
{
@ -534,7 +559,8 @@
case SA_GW:
{
ip_addr ip = v1.val.ip;
neighbor *n = neigh_find(rta->src->proto, ip, NULL, 0);
struct iface *ifa = ipa_is_link_local(ip) ? rta->nh.iface : NULL;
neighbor *n = neigh_find(rta->src->proto, ip, ifa, 0);
if (!n || (n->scope == SCOPE_HOST))
runtime( "Invalid gw address" );
@ -578,6 +604,20 @@
}
break;
case SA_WEIGHT:
{
int i = v1.val.i;
if (i < 1 || i > 256)
runtime( "Setting weight value out of bounds" );
if (rta->dest != RTD_UNICAST)
runtime( "Setting weight needs regular nexthop " );
/* Set weight on all next hops */
for (struct nexthop *nh = &rta->nh; nh; nh = nh->next)
nh->weight = i - 1;
}
break;
default:
bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
}
@ -588,31 +628,32 @@
DYNAMIC_ATTR;
ACCESS_RTE;
ACCESS_EATTRS;
RESULT_TYPE(da.f_type);
{
eattr *e = ea_find(*fs->eattrs, da.ea_code);
if (!e) {
/* A special case: undefined as_path looks like empty as_path */
if (da.type == EAF_TYPE_AS_PATH) {
RESULT(T_PATH, ad, &null_adata);
RESULT_(T_PATH, ad, &null_adata);
break;
}
/* The same special case for int_set */
if (da.type == EAF_TYPE_INT_SET) {
RESULT(T_CLIST, ad, &null_adata);
RESULT_(T_CLIST, ad, &null_adata);
break;
}
/* The same special case for ec_set */
if (da.type == EAF_TYPE_EC_SET) {
RESULT(T_ECLIST, ad, &null_adata);
RESULT_(T_ECLIST, ad, &null_adata);
break;
}
/* The same special case for lc_set */
if (da.type == EAF_TYPE_LC_SET) {
RESULT(T_LCLIST, ad, &null_adata);
RESULT_(T_LCLIST, ad, &null_adata);
break;
}
@ -623,31 +664,31 @@
switch (e->type & EAF_TYPE_MASK) {
case EAF_TYPE_INT:
RESULT(da.f_type, i, e->u.data);
RESULT_(da.f_type, i, e->u.data);
break;
case EAF_TYPE_ROUTER_ID:
RESULT(T_QUAD, i, e->u.data);
RESULT_(T_QUAD, i, e->u.data);
break;
case EAF_TYPE_OPAQUE:
RESULT(T_ENUM_EMPTY, i, 0);
RESULT_(T_ENUM_EMPTY, i, 0);
break;
case EAF_TYPE_IP_ADDRESS:
RESULT(T_IP, ip, *((ip_addr *) e->u.ptr->data));
RESULT_(T_IP, ip, *((ip_addr *) e->u.ptr->data));
break;
case EAF_TYPE_AS_PATH:
RESULT(T_PATH, ad, e->u.ptr);
RESULT_(T_PATH, ad, e->u.ptr);
break;
case EAF_TYPE_BITFIELD:
RESULT(T_BOOL, i, !!(e->u.data & (1u << da.bit)));
RESULT_(T_BOOL, i, !!(e->u.data & (1u << da.bit)));
break;
case EAF_TYPE_INT_SET:
RESULT(T_CLIST, ad, e->u.ptr);
RESULT_(T_CLIST, ad, e->u.ptr);
break;
case EAF_TYPE_EC_SET:
RESULT(T_ECLIST, ad, e->u.ptr);
RESULT_(T_ECLIST, ad, e->u.ptr);
break;
case EAF_TYPE_LC_SET:
RESULT(T_LCLIST, ad, e->u.ptr);
RESULT_(T_LCLIST, ad, e->u.ptr);
break;
case EAF_TYPE_UNDEF:
RESULT_VOID;
@ -663,6 +704,7 @@
ACCESS_EATTRS;
ARG_ANY(1);
DYNAMIC_ATTR;
ARG_TYPE(1, da.f_type);
{
struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr));
@ -675,20 +717,7 @@
switch (da.type) {
case EAF_TYPE_INT:
if (v1.type != da.f_type)
runtime( "Setting int attribute to non-int value" );
l->attrs[0].u.data = v1.val.i;
break;
case EAF_TYPE_ROUTER_ID:
/* IP->Quad implicit conversion */
if (val_is_ip4(&v1)) {
l->attrs[0].u.data = ipa_to_u32(v1.val.ip);
break;
}
/* T_INT for backward compatibility */
if ((v1.type != T_QUAD) && (v1.type != T_INT))
runtime( "Setting quad attribute to non-quad value" );
l->attrs[0].u.data = v1.val.i;
break;
@ -696,9 +725,7 @@
runtime( "Setting opaque attribute is not allowed" );
break;
case EAF_TYPE_IP_ADDRESS:
if (v1.type != T_IP)
runtime( "Setting ip attribute to non-ip value" );
case EAF_TYPE_IP_ADDRESS:;
int len = sizeof(ip_addr);
struct adata *ad = lp_alloc(fs->pool, sizeof(struct adata) + len);
ad->length = len;
@ -707,14 +734,13 @@
break;
case EAF_TYPE_AS_PATH:
if (v1.type != T_PATH)
runtime( "Setting path attribute to non-path value" );
case EAF_TYPE_INT_SET:
case EAF_TYPE_EC_SET:
case EAF_TYPE_LC_SET:
l->attrs[0].u.ptr = v1.val.ad;
break;
case EAF_TYPE_BITFIELD:
if (v1.type != T_BOOL)
runtime( "Setting bit in bitfield attribute to non-bool value" );
{
/* First, we have to find the old value */
eattr *e = ea_find(*fs->eattrs, da.ea_code);
@ -727,24 +753,6 @@
}
break;
case EAF_TYPE_INT_SET:
if (v1.type != T_CLIST)
runtime( "Setting clist attribute to non-clist value" );
l->attrs[0].u.ptr = v1.val.ad;
break;
case EAF_TYPE_EC_SET:
if (v1.type != T_ECLIST)
runtime( "Setting eclist attribute to non-eclist value" );
l->attrs[0].u.ptr = v1.val.ad;
break;
case EAF_TYPE_LC_SET:
if (v1.type != T_LCLIST)
runtime( "Setting lclist attribute to non-lclist value" );
l->attrs[0].u.ptr = v1.val.ad;
break;
default:
bug("Unknown dynamic attribute type");
}
@ -803,18 +811,76 @@
}
}
INST(FI_SADR_SRC, 1, 1) { /* Get SADR src prefix */
INST(FI_NET_SRC, 1, 1) { /* Get src prefix */
ARG(1, T_NET);
if (!net_is_sadr(v1.val.net))
runtime( "SADR expected" );
net_addr_ip6_sadr *net = (void *) v1.val.net;
net_addr_union *net = (void *) v1.val.net;
net_addr *src = falloc(sizeof(net_addr_ip6));
net_fill_ip6(src, net->src_prefix, net->src_pxlen);
const byte *part;
switch(v1.val.net->type) {
case NET_FLOW4:
part = flow4_get_part(&net->flow4, FLOW_TYPE_SRC_PREFIX);
if (part)
net_fill_ip4(src, flow_read_ip4_part(part), flow_read_pxlen(part));
else
net_fill_ip4(src, IP4_NONE, 0);
break;
case NET_FLOW6:
part = flow6_get_part(&net->flow6, FLOW_TYPE_SRC_PREFIX);
if (part)
net_fill_ip6(src, flow_read_ip6_part(part), flow_read_pxlen(part));
else
net_fill_ip6(src, IP6_NONE, 0);
break;
case NET_IP6_SADR:
net_fill_ip6(src, net->ip6_sadr.src_prefix, net->ip6_sadr.src_pxlen);
break;
default:
runtime( "Flow or SADR expected" );
}
RESULT(T_NET, net, src);
}
INST(FI_NET_DST, 1, 1) { /* Get dst prefix */
ARG(1, T_NET);
net_addr_union *net = (void *) v1.val.net;
net_addr *dst = falloc(sizeof(net_addr_ip6));
const byte *part;
switch(v1.val.net->type) {
case NET_FLOW4:
part = flow4_get_part(&net->flow4, FLOW_TYPE_DST_PREFIX);
if (part)
net_fill_ip4(dst, flow_read_ip4_part(part), flow_read_pxlen(part));
else
net_fill_ip4(dst, IP4_NONE, 0);
break;
case NET_FLOW6:
part = flow6_get_part(&net->flow6, FLOW_TYPE_DST_PREFIX);
if (part)
net_fill_ip6(dst, flow_read_ip6_part(part), flow_read_pxlen(part));
else
net_fill_ip6(dst, IP6_NONE, 0);
break;
case NET_IP6_SADR:
net_fill_ip6(dst, net->ip6_sadr.dst_prefix, net->ip6_sadr.dst_pxlen);
break;
default:
runtime( "Flow or SADR expected" );
}
RESULT(T_NET, net, dst);
}
INST(FI_ROA_MAXLEN, 1, 1) { /* Get ROA max prefix length */
ARG(1, T_NET);
if (!net_is_roa(v1.val.net))
@ -849,14 +915,14 @@
INST(FI_AS_PATH_FIRST, 1, 1) { /* Get first ASN from AS PATH */
ARG(1, T_PATH);
int as = 0;
u32 as = 0;
as_path_get_first(v1.val.ad, &as);
RESULT(T_INT, i, as);
}
INST(FI_AS_PATH_LAST, 1, 1) { /* Get last ASN from AS PATH */
ARG(1, T_PATH);
int as = 0;
u32 as = 0;
as_path_get_last(v1.val.ad, &as);
RESULT(T_INT, i, as);
}
@ -873,18 +939,17 @@
uint retpos = fstk->vcnt;
/* Drop every sub-block including ourselves */
while ((fstk->ecnt-- > 0) && !(fstk->estk[fstk->ecnt].emask & FE_RETURN))
;
do fstk->ecnt--;
while ((fstk->ecnt > 0) && !(fstk->estk[fstk->ecnt].emask & FE_RETURN));
/* Now we are at the caller frame; if no such, try to convert to accept/reject. */
if (!fstk->ecnt)
{
if (fstk->vstk[retpos].type == T_BOOL)
if (fstk->vstk[retpos].val.i)
return F_ACCEPT;
else
return F_REJECT;
return (fstk->vstk[retpos].val.i) ? F_ACCEPT : F_REJECT;
else
runtime("Can't return non-bool from non-function");
}
/* Set the value stack position, overwriting the former implicit void */
fstk->vcnt = fstk->estk[fstk->ecnt].ventry - 1;
@ -897,6 +962,15 @@
NEVER_CONSTANT;
SYMBOL;
FID_SAME_BODY()
if (!(f1->sym->flags & SYM_FLAG_SAME))
return 0;
FID_ITERATE_BODY()
BUFFER_PUSH(fit->lines) = whati->sym->function;
FID_INTERPRET_BODY()
/* Push the body on stack */
LINEX(sym->function);
curline.emask |= FE_RETURN;
@ -924,6 +998,10 @@
FID_MEMBER(struct f_tree *, tree, [[!same_tree(f1->tree, f2->tree)]], "tree %p", item->tree);
FID_ITERATE_BODY()
tree_walk(whati->tree, f_add_tree_lines, fit);
FID_INTERPRET_BODY()
const struct f_tree *t = find_tree(tree, &v1);
if (!t) {
v1.type = T_VOID;
@ -955,6 +1033,8 @@
INST(FI_CLIST_ADD, 2, 1) { /* (Extended) Community list add */
ARG_ANY(1);
ARG_ANY(2);
RESULT_TYPE(f1->type);
if (v1.type == T_PATH)
runtime("Can't add to path");
@ -964,14 +1044,14 @@
struct f_val dummy;
if ((v2.type == T_PAIR) || (v2.type == T_QUAD))
RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]);
RESULT_(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]);
/* IP->Quad implicit conversion */
else if (val_is_ip4(&v2))
RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
RESULT_(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy))
runtime("Can't add set");
else if (v2.type == T_CLIST)
RESULT(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
RESULT_(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
else
runtime("Can't add non-pair");
}
@ -982,11 +1062,11 @@
if ((v2.type == T_SET) && eclist_set_type(v2.val.t))
runtime("Can't add set");
else if (v2.type == T_ECLIST)
RESULT(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
RESULT_(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
else if (v2.type != T_EC)
runtime("Can't add non-ec");
else
RESULT(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]);
RESULT_(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]);
}
else if (v1.type == T_LCLIST)
@ -995,11 +1075,11 @@
if ((v2.type == T_SET) && lclist_set_type(v2.val.t))
runtime("Can't add set");
else if (v2.type == T_LCLIST)
RESULT(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
RESULT_(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
else if (v2.type != T_LC)
runtime("Can't add non-lc");
else
RESULT(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]);
RESULT_(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]);
}
@ -1010,6 +1090,8 @@
INST(FI_CLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
ARG_ANY(1);
ARG_ANY(2);
RESULT_TYPE(f1->type);
if (v1.type == T_PATH)
{
const struct f_tree *set = NULL;
@ -1022,7 +1104,7 @@
else
runtime("Can't delete non-integer (set)");
RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, set, key, 0) ]]);
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, set, key, 0) ]]);
}
else if (v1.type == T_CLIST)
@ -1031,12 +1113,12 @@
struct f_val dummy;
if ((v2.type == T_PAIR) || (v2.type == T_QUAD))
RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]);
RESULT_(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]);
/* IP->Quad implicit conversion */
else if (val_is_ip4(&v2))
RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
RESULT_(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST))
RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]);
RESULT_(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]);
else
runtime("Can't delete non-pair");
}
@ -1045,22 +1127,22 @@
{
/* v2.val is either EC or EC-set */
if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST))
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
RESULT_(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
else if (v2.type != T_EC)
runtime("Can't delete non-ec");
else
RESULT(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]);
RESULT_(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]);
}
else if (v1.type == T_LCLIST)
{
/* v2.val is either LC or LC-set */
if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST))
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
RESULT_(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
else if (v2.type != T_LC)
runtime("Can't delete non-lc");
else
RESULT(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]);
RESULT_(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]);
}
else
@ -1070,12 +1152,14 @@
INST(FI_CLIST_FILTER, 2, 1) { /* (Extended) Community list add or delete */
ARG_ANY(1);
ARG_ANY(2);
RESULT_TYPE(f1->type);
if (v1.type == T_PATH)
{
u32 key = 0;
if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT))
RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, v2.val.t, key, 1) ]]);
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, v2.val.t, key, 1) ]]);
else
runtime("Can't filter integer");
}
@ -1086,7 +1170,7 @@
struct f_val dummy;
if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST))
RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
RESULT_(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter pair");
}
@ -1095,7 +1179,7 @@
{
/* v2.val is either EC or EC-set */
if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST))
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
RESULT_(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter ec");
}
@ -1104,7 +1188,7 @@
{
/* v2.val is either LC or LC-set */
if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST))
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
RESULT_(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter lc");
}

View File

@ -17,6 +17,8 @@
#include "conf/conf.h"
#include "filter/filter.h"
#include "filter/data.h"
#include "lib/buffer.h"
#include "lib/flowspec.h"
/* Flags for instructions */
enum f_instruction_flags {
@ -29,7 +31,9 @@ enum f_instruction_flags {
#define f_new_inst(...) MACRO_CONCAT_AFTER(f_new_inst_, MACRO_FIRST(__VA_ARGS__))(__VA_ARGS__)
/* Convert the instruction back to the enum name */
const char *f_instruction_name(enum f_instruction_code fi);
const char *f_instruction_name_(enum f_instruction_code fi);
static inline const char *f_instruction_name(enum f_instruction_code fi)
{ return f_instruction_name_(fi) + 3; }
/* Filter structures for execution */
/* Line of instructions to be unconditionally executed one after another */
@ -47,6 +51,41 @@ static inline struct f_line *f_linearize(const struct f_inst *root)
void f_dump_line(const struct f_line *, uint indent);
/* Recursive iteration over filter instructions */
struct filter_iterator {
BUFFER_(const struct f_line *) lines;
};
void f_add_lines(const struct f_line_item *what, struct filter_iterator *fit);
#define FILTER_ITERATE_INIT(fit, filter, pool) \
({ \
BUFFER_INIT((fit)->lines, (pool), 32); \
BUFFER_PUSH((fit)->lines) = (filter)->root; \
})
#define FILTER_ITERATE(fit, fi) ({ \
const struct f_line *fl_; \
while (!BUFFER_EMPTY((fit)->lines)) \
{ \
BUFFER_POP((fit)->lines); \
fl_ = (fit)->lines.data[(fit)->lines.used]; \
for (uint i_ = 0; i_ < fl_->len; i_++) \
{ \
const struct f_line_item *fi = &fl_->items[i_]; \
f_add_lines(fi, (fit));
#define FILTER_ITERATE_END } } })
#define FILTER_ITERATE_CLEANUP(fit) \
({ \
mb_free((fit)->lines.data); \
memset((fit), 0, sizeof(struct filter_iterator)); \
})
struct filter *f_new_where(struct f_inst *);
static inline struct f_dynamic_attr f_new_dynamic_attr(u8 type, enum f_type f_type, uint 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 */

View File

@ -32,8 +32,9 @@
#include "lib/socket.h"
#include "lib/string.h"
#include "lib/unaligned.h"
#include "lib/net.h"
#include "lib/ip.h"
#include "lib/net.h"
#include "lib/flowspec.h"
#include "nest/route.h"
#include "nest/protocol.h"
#include "nest/iface.h"
@ -174,7 +175,7 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
#define curline fstk->estk[fstk->ecnt-1]
#if DEBUGGING
#ifdef LOCAL_DEBUG
debug("Interpreting line.");
f_dump_line(line, 1);
#endif
@ -407,7 +408,7 @@ f_eval_buf(const struct f_line *expr, struct linpool *tmp_pool, buffer *buf)
{
struct f_val val;
enum filter_return fret = f_eval(expr, tmp_pool, &val);
if (fret > F_RETURN)
if (fret <= F_RETURN)
val_format(&val, buf);
return fret;
}

View File

@ -24,7 +24,6 @@ enum filter_return {
F_ACCEPT, /* Need to preserve ordering: accepts < rejects! */
F_REJECT,
F_ERROR,
F_QUITBIRD,
};
static inline const char *filter_return_str(const enum filter_return fret) {
@ -36,7 +35,6 @@ static inline const char *filter_return_str(const enum filter_return fret) {
FRS(F_ACCEPT);
FRS(F_REJECT);
FRS(F_ERROR);
FRS(F_QUITBIRD);
#undef FRS
default: bug("This shall not happen");
}

View File

@ -24,21 +24,18 @@
#define BT_CONFIG_FILE "filter/test.conf"
struct parse_config_file_arg {
struct config **cp;
const char *filename;
};
static int
parse_config_file(const void *argv)
t_reconfig(void)
{
const struct parse_config_file_arg *arg = argv;
size_t fn_size = strlen(arg->filename) + 1;
char *filename = alloca(fn_size);
memcpy(filename, arg->filename, fn_size);
*(arg->cp) = bt_config_file_parse(filename);
return !!*(arg->cp);
if (!bt_config_file_parse(BT_CONFIG_FILE))
return 0;
struct symbol *s;
WALK_LIST(s, config->symbols)
if ((s->class == SYM_FUNCTION) || (s->class == SYM_FILTER))
bt_assert_msg((s->flags & SYM_FLAG_SAME), "Symbol %s same check", s->name);
return 1;
}
static int
@ -49,12 +46,6 @@ run_function(const void *arg)
if (t->cmp)
return t->result == f_same(t->fn, t->cmp);
if (!f_same(t->fn, t->fn)) {
bt_result = bt_suite_result = 0;
bt_log_suite_case_result(0, "The function doesn't compare to itself as the same");
return 0;
}
linpool *tmp = lp_new_default(&root_pool);
enum filter_return fret = f_eval(t->fn, tmp, NULL);
rfree(tmp);
@ -82,22 +73,19 @@ main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_bird_init();
bt_assert_hook = bt_assert_filter;
struct config *c = NULL;
struct parse_config_file_arg pcfa = { .cp = &c, .filename = BT_CONFIG_FILE };
bt_test_suite_base(parse_config_file, "conf", (const void *) &pcfa, 0, 0, "parse config file");
bt_test_suite_base(parse_config_file, "reconf", (const void *) &pcfa, 0, 0, "reconfigure with the same file");
/* Initial test.conf parsing, must be done here */
if (!bt_config_file_parse(BT_CONFIG_FILE))
abort();
bt_test_suite(t_reconfig, "Testing reconfiguration");
struct f_bt_test_suite *t;
WALK_LIST(t, config->tests)
bt_test_suite_base(run_function, t->fn_name, t, BT_FORKING, BT_TIMEOUT, "%s", t->dsc);
bt_bird_cleanup();
if (c)
{
struct f_bt_test_suite *t;
WALK_LIST(t, c->tests)
bt_test_suite_base(run_function, t->fn_name, t, BT_FORKING, BT_TIMEOUT, "%s", t->dsc);
}
return bt_exit_value();
}

View File

@ -63,8 +63,8 @@ bool b;
bt_assert(! false && ! false && true);
bt_assert(1 < 2 && 1 != 3);
bt_assert(true && true && ! false);
bt_assert(true || 1+"a");
bt_assert(!(false && 1+"a"));
# bt_assert(true || 1+"a");
# bt_assert(!(false && 1+"a"));
bt_assert(!(true && false));
}
@ -380,6 +380,9 @@ function t_enum()
{
bt_assert(format(RTS_DUMMY) = "(enum 30)0");
bt_assert(format(RTS_STATIC) = "(enum 30)1");
bt_assert(format(NET_IP4) = "(enum 36)1");
bt_assert(format(NET_VPN6) = "(enum 36)4");
bt_assert(RTS_STATIC ~ [RTS_STATIC, RTS_DEVICE]);
bt_assert(RTS_BGP !~ [RTS_STATIC, RTS_DEVICE]);
}
@ -458,7 +461,8 @@ function t_prefix_set()
prefix set pxs;
{
pxs = [ 1.2.0.0/16, 1.4.0.0/16+, 44.66.88.64/30{24,28}, 12.34.56.0/24{8,16} ];
bt_assert(format(pxs) = "[1.2.0.0/112{::0.1.0.0}, 1.4.0.0/112{::0.1.255.255}, 12.34.0.0/112{::1.255.0.0}, 44.66.88.64/124{::1f0}]");
bt_assert(format(pxs) = "[1.2.0.0/16{0.1.0.0}, 1.4.0.0/16{0.1.255.255}, 12.34.0.0/16{1.255.0.0}, 44.66.88.64/28{0.0.1.240}]");
bt_assert(1.2.0.0/16 ~ pxs);
bt_assert(1.4.0.0/16 ~ pxs);
bt_assert(1.4.0.0/18 ~ pxs);
@ -642,7 +646,6 @@ int set set12;
bt_assert(delete(p2, 3) = prepend(prepend(prepend(prepend(+empty+, 1), 2), 4), 5));
bt_assert(filter(p2, [1..3]) = prepend(prepend(prepend(+empty+, 1), 2), 3));
pm1 = [= 1 2 * 3 4 5 =];
p2 = prepend( + empty +, 5 );
p2 = prepend( p2, 4 );
p2 = prepend( p2, 3 );
@ -650,9 +653,17 @@ int set set12;
p2 = prepend( p2, 2 );
p2 = prepend( p2, 1 );
bt_assert(p2 ~ pm1);
bt_assert(p2 !~ [= 1 2 3 4 5 =]);
bt_assert(p2 ~ [= 1 2 * 4 5 =]);
bt_assert(p2 ~ [= 1 2 * 3 4 5 =]);
bt_assert(p2 ~ [= 1 2 3+ 4 5 =]);
bt_assert(p2 ~ [= 1 2 3+ 4+ 5 =]);
bt_assert(p2 !~ [= 1 2 3+ 5+ 4 5 =]);
bt_assert(p2 !~ [= 1 2 3 3 5+ 4 5 =]);
bt_assert(delete(p2, 3) = prepend(prepend(prepend(prepend(+empty+, 5), 4), 2), 1));
bt_assert(delete(p2, [4..5]) = prepend(prepend(prepend(prepend(+empty+, 3), 3), 2), 1));
bt_assert(format([= 1 2+ 3 =]) = "[= 1 2 + 3 =]");
}
bt_test_suite(t_path, "Testing paths");
@ -1038,6 +1049,7 @@ rd x;
bt_assert(x != 2:12345:20000);
bt_assert(!(x > 12345:200010));
bt_assert(format(0:1:2) = "1:2");
bt_assert(format(10.0.0.1:1000) = "10.0.0.1:1000");
bt_assert(format(100000:20000) = "100000:20000");
bt_assert(format(2:100000:20000) = "100000:20000");
@ -1247,7 +1259,7 @@ int j;
filter roa_filter
{
if net ~ [ 10.0.0.0/8{16,24}, 2000::/3{16,96} ] then {
if net ~ [ 10.0.0.0/8{16,24} ] || net ~ [ 2000::/3{16,96} ] then {
accept;
}
reject;
@ -1271,10 +1283,9 @@ protocol static
route 2001:0db8:85a3:8a2e::/64 max 96 as 1000;
}
function test_roa_check()
function t_roa_check()
prefix pfx;
{
# cannot be tested in __startup(), sorry
bt_assert(roa_check(r4, 10.10.0.0/16, 1000) = ROA_UNKNOWN);
bt_assert(roa_check(r4, 10.0.0.0/8, 1000) = ROA_UNKNOWN);
bt_assert(roa_check(r4, 10.110.0.0/16, 1000) = ROA_VALID);
@ -1329,39 +1340,9 @@ prefix pfx;
bt_assert(pfx.asn = 1234);
}
bt_test_suite(test_roa_check, "Testing ROA");
bt_test_suite(t_roa_check, "Testing ROA");
/*
* Testing Mixed Net Types
* -----------------------
*/
function t_mixed_prefix()
prefix set pxs;
prefix set pxt;
{
pxs = [ 98.45.0.0/16, 128.128.0.0/12+, 2200::/42-, ::ffff:d000:0/100{98,102}];
bt_assert(format(pxs) = "[::/0, ::/2{c000::}, 98.45.0.0/112{::0.1.0.0}, 128.128.0.0/108{::0.31.255.255}, 208.0.0.0/100{::124.0.0.0}, 2200::/42{ffff:ffff:ffc0::}]");
bt_assert(::fe00:0:0/88 !~ pxs);
bt_assert(::fffe:0:0/95 !~ pxs);
bt_assert(::ffff:d800:0/101 ~ pxs);
bt_assert(216.0.0.0/5 ~ pxs);
bt_assert(212.0.0.0/6 ~ pxs);
bt_assert(212.0.0.0/7 !~ pxs);
bt_assert(::ffff:8080:8080/121 ~ pxs);
bt_assert(::/0 ~ pxs);
bt_assert(0.0.0.0/0 !~ pxs);
bt_assert(128.135.64.17/32 ~ pxs);
# pxt = [ 0:1:2 10.1.10.0/24, 0:5:10000 10.1.10.0/24 ];
# print pxt;
bt_assert(format(NET_IP4) = "(enum 36)1"); ## if (net.type = NET_IP4) ...
bt_assert(format(NET_VPN6) = "(enum 36)4");
bt_assert(format(0:1:2) = "1:2");
}
bt_test_suite(t_mixed_prefix, "Testing mixed net types");
filter vpn_filter

View File

@ -43,7 +43,6 @@ protocol static {
print scope;
if !(scope ~ [ SCOPE_HOST, SCOPE_SITE ]) then {
print "Failed in test";
quitbird;
}
preference = 15;

View File

@ -103,12 +103,7 @@ build_tree(struct f_tree *from)
struct f_tree *
f_new_tree(void)
{
struct f_tree * ret;
ret = cfg_alloc(sizeof(struct f_tree));
ret->left = ret->right = NULL;
ret->from.type = ret->to.type = T_VOID;
ret->from.val.i = ret->to.val.i = 0;
ret->data = NULL;
struct f_tree *ret = cfg_allocz(sizeof(struct f_tree));
return ret;
}
@ -175,3 +170,14 @@ tree_format(const struct f_tree *t, buffer *buf)
buffer_puts(buf, "]");
}
void
tree_walk(const struct f_tree *t, void (*hook)(const struct f_tree *, void *), void *data)
{
if (!t)
return;
tree_walk(t->left, hook, data);
hook(t, data);
tree_walk(t->right, hook, data);
}

View File

@ -77,9 +77,10 @@
/*
* In the trie code, the prefix length is internally treated as for the whole
* ip_addr, regardless whether it contains an IPv4 or IPv6 address. Therefore,
* remaining definitions make sense.
* In the trie_add_prefix(), we use ip_addr (assuming that it is the same as
* ip6_addr) to handle both IPv4 and IPv6 prefixes. In contrast to rest of the
* BIRD, IPv4 addresses are just zero-padded from right. That is why we have
* ipt_from_ip4() and ipt_to_ip4() macros below.
*/
#define ipa_mkmask(x) ip6_mkmask(x)
@ -87,26 +88,30 @@
#define ipa_pxlen(x,y) ip6_pxlen(x,y)
#define ipa_getbit(x,n) ip6_getbit(x,n)
#define ipt_from_ip4(x) _MI6(_I(x), 0, 0, 0)
#define ipt_to_ip4(x) _MI4(_I0(x))
/**
* f_new_trie - allocates and returns a new empty trie
* @lp: linear pool to allocate items from
* @node_size: node size to be used (&f_trie_node and user data)
* @data_size: user data attached to node
*/
struct f_trie *
f_new_trie(linpool *lp, uint node_size)
f_new_trie(linpool *lp, uint data_size)
{
struct f_trie * ret;
ret = lp_allocz(lp, sizeof(struct f_trie) + node_size);
ret = lp_allocz(lp, sizeof(struct f_trie) + data_size);
ret->lp = lp;
ret->node_size = node_size;
ret->ipv4 = -1;
ret->data_size = data_size;
return ret;
}
static inline struct f_trie_node *
new_node(struct f_trie *t, int plen, ip_addr paddr, ip_addr pmask, ip_addr amask)
static inline struct f_trie_node4 *
new_node4(struct f_trie *t, int plen, ip4_addr paddr, ip4_addr pmask, ip4_addr amask)
{
struct f_trie_node *n = lp_allocz(t->lp, t->node_size);
struct f_trie_node4 *n = lp_allocz(t->lp, sizeof(struct f_trie_node4) + t->data_size);
n->plen = plen;
n->addr = paddr;
n->mask = pmask;
@ -114,12 +119,51 @@ new_node(struct f_trie *t, int plen, ip_addr paddr, ip_addr pmask, ip_addr amask
return n;
}
static inline void
attach_node(struct f_trie_node *parent, struct f_trie_node *child)
static inline struct f_trie_node6 *
new_node6(struct f_trie *t, int plen, ip6_addr paddr, ip6_addr pmask, ip6_addr amask)
{
parent->c[ipa_getbit(child->addr, parent->plen) ? 1 : 0] = child;
struct f_trie_node6 *n = lp_allocz(t->lp, sizeof(struct f_trie_node6) + t->data_size);
n->plen = plen;
n->addr = paddr;
n->mask = pmask;
n->accept = amask;
return n;
}
static inline struct f_trie_node *
new_node(struct f_trie *t, int plen, ip_addr paddr, ip_addr pmask, ip_addr amask)
{
if (t->ipv4)
return (struct f_trie_node *) new_node4(t, plen, ipt_to_ip4(paddr), ipt_to_ip4(pmask), ipt_to_ip4(amask));
else
return (struct f_trie_node *) new_node6(t, plen, ipa_to_ip6(paddr), ipa_to_ip6(pmask), ipa_to_ip6(amask));
}
static inline void
attach_node4(struct f_trie_node4 *parent, struct f_trie_node4 *child)
{
parent->c[ip4_getbit(child->addr, parent->plen) ? 1 : 0] = child;
}
static inline void
attach_node6(struct f_trie_node6 *parent, struct f_trie_node6 *child)
{
parent->c[ip6_getbit(child->addr, parent->plen) ? 1 : 0] = child;
}
static inline void
attach_node(struct f_trie_node *parent, struct f_trie_node *child, int v4)
{
if (v4)
attach_node4(&parent->v4, &child->v4);
else
attach_node6(&parent->v6, &child->v6);
}
#define GET_ADDR(N,F,X) ((X) ? ipt_from_ip4((N)->v4.F) : ipa_from_ip6((N)->v6.F))
#define SET_ADDR(N,F,X,V) ({ if (X) (N)->v4.F =ipt_to_ip4(V); else (N)->v6.F =ipa_to_ip6(V); })
#define GET_CHILD(N,F,X,I) ((X) ? (struct f_trie_node *) (N)->v4.c[I] : (struct f_trie_node *) (N)->v6.c[I])
/**
* trie_add_prefix
* @t: trie to add to
@ -133,21 +177,30 @@ attach_node(struct f_trie_node *parent, struct f_trie_node *child)
*
* Returns a pointer to the allocated node. The function can return a pointer to
* an existing node if @px and @plen are the same. If px/plen == 0/0 (or ::/0),
* a pointer to the root node is returned.
* a pointer to the root node is returned. Returns NULL when called with
* mismatched IPv4/IPv6 net type.
*/
void *
trie_add_prefix(struct f_trie *t, const net_addr *net, uint l, uint h)
{
ip_addr px = net_prefix(net);
uint plen = net_pxlen(net);
ip_addr px;
int v4;
if (net->type == NET_IP4)
switch (net->type)
{
const uint delta = IP6_MAX_PREFIX_LENGTH - IP4_MAX_PREFIX_LENGTH;
plen += delta;
l += delta;
h += delta;
case NET_IP4: px = ipt_from_ip4(net4_prefix(net)); v4 = 1; break;
case NET_IP6: px = ipa_from_ip6(net6_prefix(net)); v4 = 0; break;
default: bug("invalid type");
}
if (t->ipv4 != v4)
{
if (t->ipv4 < 0)
t->ipv4 = v4;
else
return NULL;
}
if (l == 0)
@ -162,95 +215,136 @@ trie_add_prefix(struct f_trie *t, const net_addr *net, uint l, uint h)
ip_addr pmask = ipa_mkmask(plen);
ip_addr paddr = ipa_and(px, pmask);
struct f_trie_node *o = NULL;
struct f_trie_node *n = t->root;
struct f_trie_node *n = &t->root;
while (n)
{
ip_addr cmask = ipa_and(n->mask, pmask);
ip_addr naddr = GET_ADDR(n, addr, v4);
ip_addr nmask = GET_ADDR(n, mask, v4);
ip_addr accept = GET_ADDR(n, accept, v4);
ip_addr cmask = ipa_and(nmask, pmask);
uint nlen = v4 ? n->v4.plen : n->v6.plen;
if (ipa_compare(ipa_and(paddr, cmask), ipa_and(n->addr, cmask)))
{
if (ipa_compare(ipa_and(paddr, cmask), ipa_and(naddr, cmask)))
{
/* We are out of path - we have to add branching node 'b'
between node 'o' and node 'n', and attach new node 'a'
as the other child of 'b'. */
int blen = ipa_pxlen(paddr, n->addr);
int blen = ipa_pxlen(paddr, naddr);
ip_addr bmask = ipa_mkmask(blen);
ip_addr baddr = ipa_and(px, bmask);
/* Merge accept masks from children to get accept mask for node 'b' */
ip_addr baccm = ipa_and(ipa_or(amask, n->accept), bmask);
ip_addr baccm = ipa_and(ipa_or(amask, accept), bmask);
struct f_trie_node *a = new_node(t, plen, paddr, pmask, amask);
struct f_trie_node *b = new_node(t, blen, baddr, bmask, baccm);
attach_node(o, b);
attach_node(b, n);
attach_node(b, a);
attach_node(o, b, v4);
attach_node(b, n, v4);
attach_node(b, a, v4);
return a;
}
if (plen < n->plen)
if (plen < nlen)
{
/* We add new node 'a' between node 'o' and node 'n' */
amask = ipa_or(amask, ipa_and(n->accept, pmask));
amask = ipa_or(amask, ipa_and(accept, pmask));
struct f_trie_node *a = new_node(t, plen, paddr, pmask, amask);
attach_node(o, a);
attach_node(a, n);
attach_node(o, a, v4);
attach_node(a, n, v4);
return a;
}
if (plen == n->plen)
if (plen == nlen)
{
/* We already found added node in trie. Just update accept mask */
n->accept = ipa_or(n->accept, amask);
accept = ipa_or(accept, amask);
SET_ADDR(n, accept, v4, accept);
return n;
}
/* Update accept mask part M2 and go deeper */
n->accept = ipa_or(n->accept, ipa_and(amask, n->mask));
accept = ipa_or(accept, ipa_and(amask, nmask));
SET_ADDR(n, accept, v4, accept);
/* n->plen < plen and plen <= 32 (128) */
o = n;
n = n->c[ipa_getbit(paddr, n->plen) ? 1 : 0];
n = GET_CHILD(n, c, v4, ipa_getbit(paddr, nlen) ? 1 : 0);
}
/* We add new tail node 'a' after node 'o' */
struct f_trie_node *a = new_node(t, plen, paddr, pmask, amask);
attach_node(o, a);
attach_node(o, a, v4);
return a;
}
static int
trie_match_prefix(const struct f_trie *t, ip_addr px, uint plen)
trie_match_net4(const struct f_trie *t, ip4_addr px, uint plen)
{
ip_addr pmask = ipa_mkmask(plen);
ip_addr paddr = ipa_and(px, pmask);
ip4_addr pmask = ip4_mkmask(plen);
ip4_addr paddr = ip4_and(px, pmask);
if (plen == 0)
return t->zero;
int plentest = plen - 1;
const struct f_trie_node *n = t->root;
const struct f_trie_node4 *n = &t->root.v4;
while(n)
{
ip_addr cmask = ipa_and(n->mask, pmask);
while (n)
{
ip4_addr cmask = ip4_and(n->mask, pmask);
/* We are out of path */
if (ipa_compare(ipa_and(paddr, cmask), ipa_and(n->addr, cmask)))
return 0;
/* We are out of path */
if (ip4_compare(ip4_and(paddr, cmask), ip4_and(n->addr, cmask)))
return 0;
/* Check accept mask */
if (ipa_getbit(n->accept, plentest))
return 1;
/* Check accept mask */
if (ip4_getbit(n->accept, plentest))
return 1;
/* We finished trie walk and still no match */
if (plen <= n->plen)
return 0;
/* We finished trie walk and still no match */
if (plen <= n->plen)
return 0;
/* Choose children */
n = n->c[(ipa_getbit(paddr, n->plen)) ? 1 : 0];
}
/* Choose children */
n = n->c[(ip4_getbit(paddr, n->plen)) ? 1 : 0];
}
return 0;
}
static int
trie_match_net6(const struct f_trie *t, ip6_addr px, uint plen)
{
ip6_addr pmask = ip6_mkmask(plen);
ip6_addr paddr = ip6_and(px, pmask);
if (plen == 0)
return t->zero;
int plentest = plen - 1;
const struct f_trie_node6 *n = &t->root.v6;
while (n)
{
ip6_addr cmask = ip6_and(n->mask, pmask);
/* We are out of path */
if (ip6_compare(ip6_and(paddr, cmask), ip6_and(n->addr, cmask)))
return 0;
/* Check accept mask */
if (ip6_getbit(n->accept, plentest))
return 1;
/* We finished trie walk and still no match */
if (plen <= n->plen)
return 0;
/* Choose children */
n = n->c[(ip6_getbit(paddr, n->plen)) ? 1 : 0];
}
return 0;
}
@ -267,20 +361,25 @@ trie_match_prefix(const struct f_trie *t, ip_addr px, uint plen)
int
trie_match_net(const struct f_trie *t, const net_addr *n)
{
uint add = 0;
switch (n->type)
{
case NET_IP4:
case NET_VPN4:
case NET_ROA4:
return t->ipv4 ? trie_match_net4(t, net4_prefix(n), net_pxlen(n)) : 0;
switch (n->type) {
case NET_IP4:
case NET_VPN4:
case NET_ROA4:
add = IP6_MAX_PREFIX_LENGTH - IP4_MAX_PREFIX_LENGTH;
case NET_IP6:
case NET_VPN6:
case NET_ROA6:
return !t->ipv4 ? trie_match_net6(t, net6_prefix(n), net_pxlen(n)) : 0;
default:
return 0;
}
return trie_match_prefix(t, net_prefix(n), net_pxlen(n) + add);
}
static int
trie_node_same(const struct f_trie_node *t1, const struct f_trie_node *t2)
trie_node_same4(const struct f_trie_node4 *t1, const struct f_trie_node4 *t2)
{
if ((t1 == NULL) && (t2 == NULL))
return 1;
@ -289,11 +388,28 @@ trie_node_same(const struct f_trie_node *t1, const struct f_trie_node *t2)
return 0;
if ((t1->plen != t2->plen) ||
(! ipa_equal(t1->addr, t2->addr)) ||
(! ipa_equal(t1->accept, t2->accept)))
(! ip4_equal(t1->addr, t2->addr)) ||
(! ip4_equal(t1->accept, t2->accept)))
return 0;
return trie_node_same(t1->c[0], t2->c[0]) && trie_node_same(t1->c[1], t2->c[1]);
return trie_node_same4(t1->c[0], t2->c[0]) && trie_node_same4(t1->c[1], t2->c[1]);
}
static int
trie_node_same6(const struct f_trie_node6 *t1, const struct f_trie_node6 *t2)
{
if ((t1 == NULL) && (t2 == NULL))
return 1;
if ((t1 == NULL) || (t2 == NULL))
return 0;
if ((t1->plen != t2->plen) ||
(! ip6_equal(t1->addr, t2->addr)) ||
(! ip6_equal(t1->accept, t2->accept)))
return 0;
return trie_node_same6(t1->c[0], t2->c[0]) && trie_node_same6(t1->c[1], t2->c[1]);
}
/**
@ -306,20 +422,39 @@ trie_node_same(const struct f_trie_node *t1, const struct f_trie_node *t2)
int
trie_same(const struct f_trie *t1, const struct f_trie *t2)
{
return (t1->zero == t2->zero) && trie_node_same(t1->root, t2->root);
if ((t1->zero != t2->zero) || (t1->ipv4 != t2->ipv4))
return 0;
if (t1->ipv4)
return trie_node_same4(&t1->root.v4, &t2->root.v4);
else
return trie_node_same6(&t1->root.v6, &t2->root.v6);
}
static void
trie_node_format(const struct f_trie_node *t, buffer *buf)
trie_node_format4(const struct f_trie_node4 *t, buffer *buf)
{
if (t == NULL)
return;
if (ipa_nonzero(t->accept))
buffer_print(buf, "%I/%d{%I}, ", t->addr, t->plen, t->accept);
if (ip4_nonzero(t->accept))
buffer_print(buf, "%I4/%d{%I4}, ", t->addr, t->plen, t->accept);
trie_node_format(t->c[0], buf);
trie_node_format(t->c[1], buf);
trie_node_format4(t->c[0], buf);
trie_node_format4(t->c[1], buf);
}
static void
trie_node_format6(const struct f_trie_node6 *t, buffer *buf)
{
if (t == NULL)
return;
if (ip6_nonzero(t->accept))
buffer_print(buf, "%I6/%d{%I6}, ", t->addr, t->plen, t->accept);
trie_node_format6(t->c[0], buf);
trie_node_format6(t->c[1], buf);
}
/**
@ -335,8 +470,12 @@ trie_format(const struct f_trie *t, buffer *buf)
buffer_puts(buf, "[");
if (t->zero)
buffer_print(buf, "%I/%d, ", IPA_NONE, 0);
trie_node_format(t->root, buf);
buffer_print(buf, "%I/%d, ", t->ipv4 ? IPA_NONE4 : IPA_NONE6, 0);
if (t->ipv4)
trie_node_format4(&t->root.v4, buf);
else
trie_node_format6(&t->root.v6, buf);
if (buf->pos == buf->end)
return;

View File

@ -103,7 +103,7 @@ t_match_net(void)
{
list prefixes; /* of structs f_extended_prefix */
init_list(&prefixes);
struct f_trie *trie = f_new_trie(config->mem, sizeof(struct f_trie_node));
struct f_trie *trie = f_new_trie(config->mem, 0);
generate_random_ipv6_prefixes(&prefixes);
struct f_prefix_node *n;
@ -143,8 +143,8 @@ t_trie_same(void)
int round;
for (round = 0; round < TESTS_NUM*4; round++)
{
struct f_trie * trie1 = f_new_trie(config->mem, sizeof(struct f_trie_node));
struct f_trie * trie2 = f_new_trie(config->mem, sizeof(struct f_trie_node));
struct f_trie * trie1 = f_new_trie(config->mem, 0);
struct f_trie * trie2 = f_new_trie(config->mem, 0);
list prefixes; /* a list of f_extended_prefix structures */
init_list(&prefixes);

View File

@ -1,7 +1,7 @@
src := bitops.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
src := bitmap.c bitops.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
obj := $(src-o-files)
$(all-daemon)
tests_src := heap_test.c buffer_test.c event_test.c flowspec_test.c bitops_test.c patmatch_test.c fletcher16_test.c slist_test.c checksum_test.c lists_test.c mac_test.c ip_test.c hash_test.c printf_test.c
tests_src := bitmap_test.c heap_test.c buffer_test.c event_test.c flowspec_test.c bitops_test.c patmatch_test.c fletcher16_test.c slist_test.c checksum_test.c lists_test.c mac_test.c ip_test.c hash_test.c printf_test.c
tests_targets := $(tests_targets) $(tests-target-files)
tests_objs := $(tests_objs) $(src-o-files)

View File

@ -72,6 +72,7 @@ static inline int u64_cmp(u64 i1, u64 i2)
#define NORET __attribute__((noreturn))
#define UNUSED __attribute__((unused))
#define PACKED __attribute__((packed))
#define NONNULL(...) __attribute__((nonnull((__VA_ARGS__))))
#ifndef HAVE_THREAD_LOCAL
#define _Thread_local
@ -162,12 +163,23 @@ void debug(const char *msg, ...); /* Printf to debug output */
#define DBG(x, y...) do { } while(0)
#endif
#define ASSERT_DIE(x) do { if (!(x)) bug("Assertion '%s' failed at %s:%d", #x, __FILE__, __LINE__); } while(0)
#define EXPENSIVE_CHECK(x) /* intentionally left blank */
#ifdef DEBUGGING
#define ASSERT(x) do { if (!(x)) bug("Assertion '%s' failed at %s:%d", #x, __FILE__, __LINE__); } while(0)
#define ASSERT(x) ASSERT_DIE(x)
#define ASSUME(x) ASSERT_DIE(x)
#ifdef ENABLE_EXPENSIVE_CHECKS
#undef EXPENSIVE_CHECK
#define EXPENSIVE_CHECK(x) ASSERT_DIE(x)
#endif
#else
#define ASSERT(x) do { if (!(x)) log(L_BUG "Assertion '%s' failed at %s:%d", #x, __FILE__, __LINE__); } while(0)
#define ASSUME(x) /* intentionally left blank */
#endif
#ifdef DEBUGGING
asm(
".pushsection \".debug_gdb_scripts\", \"MS\",@progbits,1\n"

197
lib/bitmap.c Normal file
View File

@ -0,0 +1,197 @@
/*
* BIRD Library -- Bitmaps
*
* (c) 2019 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2019 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include <stdlib.h>
#include "nest/bird.h"
#include "lib/bitmap.h"
#include "lib/bitops.h"
#include "lib/resource.h"
/*
* Basic bitmap
*/
void
bmap_init(struct bmap *b, pool *p, uint size)
{
b->size = BIRD_ALIGN(size, 4);
b->data = mb_allocz(p, b->size);
}
void
bmap_reset(struct bmap *b, uint size)
{
b->size = BIRD_ALIGN(size, 4);
memset(b->data, 0, b->size);
}
void
bmap_grow(struct bmap *b, uint need)
{
uint size = b->size * 2;
while (size < need)
size *= 2;
uint old_size = b->size;
b->size = size;
b->data = mb_realloc(b->data, b->size);
ASSERT(size >= old_size);
memset(b->data + (old_size / 4), 0, size - old_size);
}
void
bmap_free(struct bmap *b)
{
mb_free(b->data);
b->size = 0;
b->data = NULL;
}
/*
* Hierarchical bitmap
*/
#define B256_SIZE(b) BIRD_ALIGN(b, 32)
#define B256_STEP(b) (BIRD_ALIGN(b, 8192) >> 8)
void
hmap_init(struct hmap *b, pool *p, uint size)
{
b->size[0] = B256_SIZE(size);
b->size[1] = B256_STEP(b->size[0]);
b->size[2] = B256_STEP(b->size[1]);
b->size[3] = sizeof(b->root);
b->data[0] = mb_allocz(p, b->size[0]);
b->data[1] = mb_allocz(p, b->size[1]);
b->data[2] = mb_allocz(p, b->size[2]);
b->data[3] = b->root;
memset(b->root, 0, sizeof(b->root));
}
static void
hmap_grow(struct hmap *b, uint need)
{
uint size = b->size[0] * 2;
while (size < need)
size *= 2;
for (uint i = 0; i < 3; i++)
{
uint old_size = b->size[i];
b->size[i] = size;
b->data[i] = mb_realloc(b->data[i], b->size[i]);
ASSERT(size >= old_size);
memset(b->data[i] + (old_size / 4), 0, size - old_size);
size = B256_STEP(size);
}
}
void
hmap_free(struct hmap *b)
{
mb_free(b->data[0]);
mb_free(b->data[1]);
mb_free(b->data[2]);
memset(b, 0, sizeof(struct hmap));
}
static inline int
b256_and(u32 *p)
{
for (int i = 0; i < 8; i++)
if (~p[i])
return 0;
return 1;
}
void
hmap_set(struct hmap *b, uint n)
{
if (n >= hmap_max(b))
hmap_grow(b, n/8 + 1);
for (int i = 0; i < 4; i++)
{
BIT32_SET(b->data[i], n);
n = n >> 8;
/* Continue if all bits in 256-bit block are set */
if (! b256_and(b->data[i] + 8*n))
break;
}
}
void
hmap_clear(struct hmap *b, uint n)
{
if (n >= hmap_max(b))
return;
for (int i = 0; i < 4; i++)
{
BIT32_CLR(b->data[i], n);
n = n >> 8;
}
}
static inline int
b256_first_zero(u32 *p)
{
for (int i = 0; i < 8; i++)
if (~p[i])
return 32*i + u32_ctz(~p[i]);
return 256;
}
u32
hmap_first_zero(struct hmap *b)
{
u32 n = 0;
for (int i = 3; i >= 0; i--)
{
if (32*n >= b->size[i])
return hmap_max(b);
u32 *p = b->data[i] + 8*n;
n = (n << 8) + b256_first_zero(p);
}
return n;
}
void
hmap_check(struct hmap *b)
{
for (int i = 0; i < 2; i++)
{
int max = b->size[i] / 32;
for (int j = 0; j < max; j++)
{
int x = b256_and(b->data[i] + 8*j);
int y = !!BIT32_TEST(b->data[i+1], j);
if (x != y)
bug("Inconsistent data on %d:%d (%d vs %d)", i, j, x, y);
}
}
}

63
lib/bitmap.h Normal file
View File

@ -0,0 +1,63 @@
/*
* BIRD Library -- Bitmaps
*
* (c) 2019 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2019 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_BITMAP_H_
#define _BIRD_BITMAP_H_
struct bmap
{
u32 size;
u32 *data;
};
void bmap_init(struct bmap *b, pool *p, uint size);
void bmap_reset(struct bmap *b, uint size);
void bmap_grow(struct bmap *b, uint need);
void bmap_free(struct bmap *b);
static inline uint bmap_max(struct bmap *b)
{ return 8 * b->size; }
static inline int bmap_test(struct bmap *b, uint n)
{ return (n < bmap_max(b)) && BIT32_TEST(b->data, n); }
static inline void bmap_set(struct bmap *b, uint n)
{
if (n >= bmap_max(b)) bmap_grow(b, n/8 + 1);
BIT32_SET(b->data, n);
}
static inline void bmap_clear(struct bmap *b, uint n)
{
if (n >= bmap_max(b)) return;
BIT32_CLR(b->data, n);
}
struct hmap
{
u32 size[4];
u32 *data[4];
u32 root[8];
};
static inline uint hmap_max(struct hmap *b)
{ return 8 * b->size[0]; }
static inline int hmap_test(struct hmap *b, uint n)
{ return (n < hmap_max(b)) && BIT32_TEST(b->data[0], n); }
void hmap_init(struct hmap *b, pool *p, uint size);
void hmap_free(struct hmap *b);
void hmap_set(struct hmap *b, uint n);
void hmap_clear(struct hmap *b, uint n);
u32 hmap_first_zero(struct hmap *b);
void hmap_check(struct hmap *b);
#endif

186
lib/bitmap_test.c Normal file
View File

@ -0,0 +1,186 @@
/*
* BIRD Library -- Bitmap Tests
*
* (c) 2019 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2019 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "sysdep/config.h"
#include "lib/bitmap.h"
#define MAX_NUM (1 << 20)
#define MAX_SET (1 << 19)
#define MAX_CLR (1 << 17)
#define STEP_NUM 1000
#define STEP_SET 1000
#define STEP_CLR 500
static int
t_bmap_set_clear_random(void)
{
struct bmap b;
resource_init();
bmap_init(&b, &root_pool, 1024);
char expected[MAX_NUM] = {};
uint i, n;
for (i = 0; i < MAX_SET; i++)
{
do n = bt_random() % MAX_NUM;
while (expected[n]);
bmap_set(&b, n);
expected[n] = 1;
}
for (i = 0; i < MAX_CLR; i++)
{
do n = bt_random() % MAX_NUM;
while (!expected[n]);
bmap_clear(&b, n);
expected[n] = 0;
}
for (i = 0; i < MAX_NUM; i++)
if (bmap_test(&b, i) != expected[i])
bt_abort_msg("Bitmap mismatch on %d (should be %d %d)", i, bmap_test(&b, i), expected[i]);
return 1;
}
static int
t_hmap_set_clear_random(void)
{
struct hmap b;
resource_init();
hmap_init(&b, &root_pool, 1024);
char expected[MAX_NUM] = {};
uint i, n;
for (i = 0; i < MAX_SET; i++)
{
do n = bt_random() % MAX_NUM;
while (expected[n]);
hmap_set(&b, n);
expected[n] = 1;
}
hmap_check(&b);
for (i = 0; i < MAX_CLR; i++)
{
do n = bt_random() % MAX_NUM;
while (!expected[n]);
hmap_clear(&b, n);
expected[n] = 0;
}
hmap_check(&b);
for (i = 0; i < MAX_NUM; i++)
if (hmap_test(&b, i) != expected[i])
bt_abort_msg("Bitmap mismatch on %d (should be %d %d)", i, hmap_test(&b, i), expected[i]);
for (i = 0; 1; i++)
{
n = hmap_first_zero(&b);
bt_assert(n >= i);
bt_assert(n <= MAX_NUM);
for (; i < n; i++)
bt_assert(expected[i]);
if (n == MAX_NUM)
break;
bt_assert(!expected[i]);
hmap_set(&b, n);
}
hmap_check(&b);
return 1;
}
static int
t_hmap_set_clear_fill(void)
{
struct hmap b;
resource_init();
hmap_init(&b, &root_pool, 1024);
char expected[MAX_NUM] = {};
uint i, j, n, max = 0;
for (i = 0; i < STEP_NUM; i++)
{
uint last = 0;
uint step_set = bt_random() % STEP_SET;
uint step_clr = bt_random() % STEP_CLR;
for (j = 0; j < step_set; j++)
{
n = hmap_first_zero(&b);
bt_assert(n > last || !last);
bt_assert(n < MAX_NUM);
if (!last)
last = n;
for (; last < n; last++)
bt_assert(expected[last]);
bt_assert(!expected[n]);
hmap_set(&b, n);
expected[n] = 1;
max = MAX(max, n);
}
for (j = 0; j < step_clr; j++)
{
uint k = 0;
do n = bt_random() % max;
while (!expected[n] && (k++ < 8));
if (!expected[n])
continue;
hmap_clear(&b, n);
expected[n] = 0;
}
}
for (i = 0; i < MAX_NUM; i++)
if (hmap_test(&b, i) != expected[i])
bt_abort_msg("Bitmap mismatch on %d (should be %d %d)", i, hmap_test(&b, i), expected[i]);
hmap_check(&b);
return 1;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_bmap_set_clear_random, "BMap - random sequence of sets / clears");
bt_test_suite(t_hmap_set_clear_random, "HMap - random sequence of sets / clears");
bt_test_suite(t_hmap_set_clear_fill, "HMap - linear sets and random clears");
return bt_exit_value();
}

View File

@ -29,6 +29,9 @@ static inline u32 u32_hash(u32 v) { return v * 2902958171u; }
static inline u8 u32_popcount(u32 v) { return __builtin_popcount(v); }
static inline int u32_clz(u32 v) { return __builtin_clz(v); }
static inline int u32_ctz(u32 v) { return __builtin_ctz(v); }
static inline int uint_is_pow2(uint n) { return n && !(n & (n-1)); }
#endif

View File

@ -50,6 +50,8 @@
#define BUFFER_FLUSH(v) ({ (v).used = 0; })
#define BUFFER_EMPTY(v) ({ (v).used == 0; })
#define BUFFER_WALK(v,n) \
for (BUFFER_TYPE(v) *_n = (v).data, n; _n < ((v).data + (v).used) && (n = *_n, 1); _n++)

View File

@ -23,6 +23,7 @@
#include "lib/event.h"
event_list global_event_list;
event_list global_work_list;
inline void
ev_postpone(event *e)
@ -114,6 +115,22 @@ ev_schedule(event *e)
ev_enqueue(&global_event_list, e);
}
/**
* ev_schedule_work - schedule a work-event.
* @e: an event
*
* This function schedules an event by enqueueing it to a system-wide work-event
* list which is run by the platform dependent code whenever appropriate. This
* is designated for work-events instead of regular events. They are executed
* less often in order to not clog I/O loop.
*/
void
ev_schedule_work(event *e)
{
if (!ev_active(e))
add_tail(&global_work_list, &e->n);
}
void io_log_event(void *hook, void *data);
/**
@ -136,10 +153,47 @@ ev_run_list(event_list *l)
event *e = SKIP_BACK(event, n, n);
/* This is ugly hack, we want to log just events executed from the main I/O loop */
if (l == &global_event_list)
if ((l == &global_event_list) || (l == &global_work_list))
io_log_event(e->hook, e->data);
ev_run(e);
}
return !EMPTY_LIST(*l);
}
int
ev_run_list_limited(event_list *l, uint limit)
{
node *n;
list tmp_list;
init_list(&tmp_list);
add_tail_list(&tmp_list, l);
init_list(l);
WALK_LIST_FIRST(n, tmp_list)
{
event *e = SKIP_BACK(event, n, n);
if (!limit)
break;
/* This is ugly hack, we want to log just events executed from the main I/O loop */
if ((l == &global_event_list) || (l == &global_work_list))
io_log_event(e->hook, e->data);
ev_run(e);
limit--;
}
if (!EMPTY_LIST(tmp_list))
{
/* Attach new items after the unprocessed old items */
add_tail_list(&tmp_list, l);
init_list(l);
add_tail_list(l, &tmp_list);
}
return !EMPTY_LIST(*l);
}

View File

@ -21,14 +21,17 @@ typedef struct event {
typedef list event_list;
extern event_list global_event_list;
extern event_list global_work_list;
event *ev_new(pool *);
void ev_run(event *);
#define ev_init_list(el) init_list(el)
void ev_enqueue(event_list *, event *);
void ev_schedule(event *);
void ev_schedule_work(event *);
void ev_postpone(event *);
int ev_run_list(event_list *);
int ev_run_list_limited(event_list *, uint);
static inline int
ev_active(event *e)

View File

@ -24,24 +24,33 @@ straightforward_fletcher16_compute(const char *data)
sum2 = (sum2 + sum1) % 255;
}
return (sum2 << 8) | sum1;
sum2 = (sum2 + sum1) % 255;
sum2 = (sum2 + sum1) % 255;
return (sum1 << 8) | sum2;
}
static u16
straightforward_fletcher16_checksum(const char *data)
{
u16 csum;
u8 c0,c1,f0,f1;
u16 c0,c1,x,y;
csum = straightforward_fletcher16_compute(data);
f0 = csum & 0xff;
f1 = (csum >> 8) & 0xff;
c0 = 0xff - ((f0 + f1) % 0xff);
c1 = 0xff - ((f0 + c0) % 0xff);
c0 = (csum >> 8) & 0xff;
c1 = csum & 0xff;
return (c1 << 8) | c0;
x = (255 + c0 - c1) % 255;
y = (510 - 2*c0 + c1) % 255;
if (!x) x = 255;
if (!y) y = 255;
return (x << 8) | y;
}
const u8 zero16[2] = {};
static int
test_fletcher16(void *out_, const void *in_, const void *expected_out_)
{
@ -53,7 +62,8 @@ test_fletcher16(void *out_, const void *in_, const void *expected_out_)
fletcher16_init(&ctxt);
fletcher16_update(&ctxt, in, strlen(in));
put_u16(out, fletcher16_compute(&ctxt));
fletcher16_update(&ctxt, zero16, 2);
*out = fletcher16_compute(&ctxt);
return *out == *expected_out;
}
@ -70,7 +80,8 @@ test_fletcher16_checksum(void *out_, const void *in_, const void *expected_out_)
fletcher16_init(&ctxt);
fletcher16_update(&ctxt, in, len);
put_u16(out, fletcher16_final(&ctxt, len, len));
fletcher16_update(&ctxt, zero16, 2);
*out = fletcher16_final(&ctxt, len+2, len);
return *out == *expected_out;
}
@ -81,7 +92,7 @@ t_fletcher16_compute(void)
struct bt_pair test_vectors[] = {
{
.in = "\001\002",
.out = & (const u16) { 0x0403 },
.out = & ((const u16) { straightforward_fletcher16_compute("\001\002") }),
},
{
.in = "",

View File

@ -112,7 +112,6 @@ get_value_length(const byte *op)
}
/*
* Flowspec iterators
*/
@ -244,6 +243,69 @@ flow6_next_part(const byte *pos, const byte *end)
return flow_next_part(pos, end, 1);
}
static const byte *
flow_get_part(const byte *data, uint dlen, uint type, int ipv6)
{
const byte *part;
for (part = flow_first_part(data);
part && (part[0] <= type);
part = flow_next_part(part, data+dlen, ipv6))
if (part[0] == type)
return part;
return NULL;
}
const byte *
flow4_get_part(const net_addr_flow4 *f, uint type)
{
return flow_get_part(f->data, f->length - sizeof(net_addr_flow4), type, 0);
}
const byte *
flow6_get_part(const net_addr_flow6 *f, uint type)
{
return flow_get_part(f->data, f->length - sizeof(net_addr_flow6), type, 1);
}
/*
* Flowspec accessors
*/
static inline ip4_addr
flow_read_ip4(const byte *px, uint pxlen)
{
ip4_addr ip = IP4_NONE;
memcpy(&ip, px, BYTES(pxlen));
return ip4_ntoh(ip);
}
ip4_addr
flow_read_ip4_part(const byte *part)
{
return flow_read_ip4(part + 2, part[1]);
}
static inline ip6_addr
flow_read_ip6(const byte *px, uint pxlen, uint pxoffset)
{
uint floor_offset = BYTES(pxoffset - (pxoffset % 8));
uint ceil_len = BYTES(pxlen);
ip6_addr ip = IP6_NONE;
memcpy(((byte *) &ip) + floor_offset, px, ceil_len - floor_offset);
return ip6_ntoh(ip);
}
ip6_addr
flow_read_ip6_part(const byte *part)
{
return flow_read_ip6(part + 3, part[1], part[2]);
}
/*
* Flowspec validation
@ -374,7 +436,6 @@ flow_validate(const byte *nlri, uint len, int ipv6)
enum flow_type type = 0;
const byte *pos = nlri;
const byte *end = nlri + len;
int met_dst_pfx = 0;
while (pos < end)
{
@ -386,8 +447,6 @@ flow_validate(const byte *nlri, uint len, int ipv6)
switch (type)
{
case FLOW_TYPE_DST_PREFIX:
met_dst_pfx = 1;
/* Fall through */
case FLOW_TYPE_SRC_PREFIX:
{
uint pxlen = *pos++;
@ -494,9 +553,6 @@ flow_validate(const byte *nlri, uint len, int ipv6)
if (pos != end)
return FLOW_ST_NOT_COMPLETE;
if (!ipv6 && !met_dst_pfx)
return FLOW_ST_DEST_PREFIX_REQUIRED;
return FLOW_ST_VALID;
}
@ -779,26 +835,6 @@ flow_builder_set_type(struct flow_builder *fb, enum flow_type type)
fb->this_type = type;
}
static ip4_addr
flow_read_ip4(const byte *px, uint pxlen)
{
ip4_addr ip = IP4_NONE;
memcpy(&ip, px, BYTES(pxlen));
return ip4_ntoh(ip);
}
static ip6_addr
flow_read_ip6(const byte *px, uint pxlen, uint pxoffset)
{
uint floor_offset = BYTES(pxoffset - (pxoffset % 8));
uint ceil_len = BYTES(pxlen);
ip6_addr ip = IP6_NONE;
memcpy(((byte *) &ip) + floor_offset, px, ceil_len - floor_offset);
return ip6_ntoh(ip);
}
static void
builder_write_parts(struct flow_builder *fb, byte *buf)
{
@ -831,9 +867,9 @@ flow_builder4_finalize(struct flow_builder *fb, linpool *lpool)
if (fb->parts[FLOW_TYPE_DST_PREFIX].length)
{
byte *p = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset + 1;
pxlen = *p++;
prefix = flow_read_ip4(p, pxlen);
byte *part = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset;
prefix = flow_read_ip4_part(part);
pxlen = flow_read_pxlen(part);
}
*f = NET_ADDR_FLOW4(prefix, pxlen, data_len);
@ -861,10 +897,9 @@ flow_builder6_finalize(struct flow_builder *fb, linpool *lpool)
if (fb->parts[FLOW_TYPE_DST_PREFIX].length)
{
byte *p = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset + 1;
pxlen = *p++;
uint pxoffset = *p++;
prefix = flow_read_ip6(p, pxlen, pxoffset);
byte *part = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset;
prefix = flow_read_ip6_part(part);
pxlen = flow_read_pxlen(part);
}
*n = NET_ADDR_FLOW6(prefix, pxlen, data_len);
@ -947,18 +982,18 @@ fragment_val_str(u8 val)
static void
net_format_flow_ip(buffer *b, const byte *part, int ipv6)
{
uint pxlen = *(part+1);
uint pxlen = part[1];
if (ipv6)
{
uint pxoffset = *(part+2);
uint pxoffset = part[2];
if (pxoffset)
buffer_print(b, "%I6/%u offset %u; ", flow_read_ip6(part+3,pxlen,pxoffset), pxlen, pxoffset);
buffer_print(b, "%I6/%u offset %u; ", flow_read_ip6_part(part), pxlen, pxoffset);
else
buffer_print(b, "%I6/%u; ", flow_read_ip6(part+3,pxlen,0), pxlen);
buffer_print(b, "%I6/%u; ", flow_read_ip6_part(part), pxlen);
}
else
{
buffer_print(b, "%I4/%u; ", flow_read_ip4(part+2,pxlen), pxlen);
buffer_print(b, "%I4/%u; ", flow_read_ip4_part(part), pxlen);
}
}

View File

@ -83,6 +83,17 @@ const byte *flow4_first_part(const net_addr_flow4 *f);
const byte *flow6_first_part(const net_addr_flow6 *f);
const byte *flow4_next_part(const byte *pos, const byte *end);
const byte *flow6_next_part(const byte *pos, const byte *end);
const byte *flow4_get_part(const net_addr_flow4 *f, uint type);
const byte *flow6_get_part(const net_addr_flow6 *f, uint type);
/*
* Flowspec accessors
*/
ip4_addr flow_read_ip4_part(const byte *part);
ip6_addr flow_read_ip6_part(const byte *part);
static inline int flow_read_pxlen(const byte *part) { return part[1]; }
/*

View File

@ -137,7 +137,7 @@ static int
t_iterators6(void)
{
net_addr_flow6 *f;
NET_ADDR_FLOW6_(f, ip6_build(0,0,0x12345678,0x9a000000), 64, ((byte[]) {
NET_ADDR_FLOW6_(f, ip6_build(0,0,0x12345678,0x9a000000), 0x68, ((byte[]) {
26, /* Length */
FLOW_TYPE_DST_PREFIX, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a,
FLOW_TYPE_SRC_PREFIX, 0x08, 0x0, 0xc0,
@ -166,6 +166,56 @@ t_iterators6(void)
return 1;
}
static int
t_accessors4(void)
{
net_addr_flow4 *f;
NET_ADDR_FLOW4_(f, ip4_build(5,6,7,0), 24, ((byte[]) {
25, /* Length */
FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
}));
const byte *p1_dst_px = &f->data[1];
const ip4_addr p1_dst_addr = ip4_build(5,6,7,0);
const byte *p2_src_px = &f->data[6];
const ip4_addr p2_src_addr = ip4_build(10,11,12,13);
bt_assert(ip4_equal(flow_read_ip4_part(p1_dst_px), p1_dst_addr));
bt_assert(ip4_equal(flow_read_ip4_part(p2_src_px), p2_src_addr));
return 1;
}
static int
t_accessors6(void)
{
net_addr_flow6 *f;
NET_ADDR_FLOW6_(f, ip6_build(0,0,0x12345678,0x9a000000), 0x68, ((byte[]) {
26, /* Length */
FLOW_TYPE_DST_PREFIX, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a,
FLOW_TYPE_SRC_PREFIX, 0x08, 0x0, 0xc0,
FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
FLOW_TYPE_LABEL, 0x80, 0x55,
}));
const byte *p1_dst_px = &f->data[1];
const ip6_addr p1_dst_addr = ip6_build(0,0,0x12345678,0x9a000000);
const byte *p2_src_px = &f->data[9];
const ip6_addr p2_src_addr = ip6_build(0xc0000000, 0, 0, 0);
bt_assert(ip6_equal(flow_read_ip6_part(p1_dst_px), p1_dst_addr));
bt_assert(ip6_equal(flow_read_ip6_part(p2_src_px), p2_src_addr));
return 1;
}
static int
t_validation4(void)
{
@ -179,11 +229,9 @@ t_validation4(void)
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
};
/* Isn't included destination prefix */
/* Empty NLRI */
res = flow4_validate(nlri1, 0);
bt_assert(res == FLOW_ST_DEST_PREFIX_REQUIRED);
res = flow4_validate(&nlri1[5], sizeof(nlri1)-5);
bt_assert(res == FLOW_ST_DEST_PREFIX_REQUIRED);
bt_assert(res == FLOW_ST_VALID);
/* Valid / Not Complete testing */
uint valid_sizes[] = {5, 11, 14, 22, 25, 0};
@ -628,6 +676,8 @@ main(int argc, char *argv[])
bt_test_suite(t_first_part, "Searching first part in net_addr_flow");
bt_test_suite(t_iterators4, "Testing iterators (IPv4)");
bt_test_suite(t_iterators6, "Testing iterators (IPv6)");
bt_test_suite(t_accessors4, "Testing accessors (IPv4)");
bt_test_suite(t_accessors6, "Testing accessors (IPv6)");
bt_test_suite(t_validation4, "Testing validation (IPv4)");
bt_test_suite(t_validation6, "Testing validation (IPv6)");
bt_test_suite(t_builder4, "Inserting components into existing Flow Specification (IPv4)");

View File

@ -264,6 +264,9 @@ ip6_pton(const char *a, ip6_addr *o)
int i, j, k, l, hfil;
const char *start;
if (!a[0]) /* Empty string check */
return 0;
if (a[0] == ':') /* Leading :: */
{
if (a[1] != ':')
@ -333,6 +336,8 @@ ip6_pton(const char *a, ip6_addr *o)
for (; i>=hfil; i--)
words[i] = 0;
}
else if (i != 8) /* Incomplete address */
return 0;
/* Convert the address to ip6_addr format */
for (i=0; i<4; i++)

View File

@ -48,6 +48,13 @@
#define UDP_HEADER_LENGTH 8
/* IANA Address Family Numbers */
/* https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml */
/* Would use AF_ prefix, but that collides with POSIX address family numbers */
#define AFI_IPV4 1
#define AFI_IPV6 2
#ifdef DEBUGGING
typedef struct ip4_addr {

View File

@ -13,25 +13,38 @@
#define IP4_MAX_LEN 16
static int
test_ipa_pton(void *out_, const void *in_, const void *expected_out_)
test_ip4_pton(void *out_, const void *in_, const void *expected_out_)
{
ip_addr *out = out_;
const char *in = in_;
const ip_addr *expected_out = expected_out_;
ip4_addr ip4;
if (expected_out)
{
bt_assert(ip4_pton(in, &ip4));
*out = ipa_from_ip4(ip4);
return ipa_equal(*out, *expected_out);
}
else
return !ip4_pton(in, &ip4);
}
static int
test_ip6_pton(void *out_, const void *in_, const void *expected_out_)
{
ip_addr *out = out_;
const char *in = in_;
const ip_addr *expected_out = expected_out_;
if (ipa_is_ip4(*expected_out))
{
ip4_addr ip4;
bt_assert(ip4_pton(in, &ip4));
*out = ipa_from_ip4(ip4);
}
else
if (expected_out)
{
bt_assert(ip6_pton(in, out));
/* ip_addr == ip6_addr */
return ipa_equal(*out, *expected_out);
}
return ipa_equal(*out, *expected_out);
else
return !ip6_pton(in, out);
}
static int
@ -52,7 +65,7 @@ t_ip4_pton(void)
},
};
return bt_assert_batch(test_vectors, test_ipa_pton, bt_fmt_str, bt_fmt_ipa);
return bt_assert_batch(test_vectors, test_ip4_pton, bt_fmt_str, bt_fmt_ipa);
}
static int
@ -87,9 +100,17 @@ t_ip6_pton(void)
.in = "2605:2700:0:3::4713:93e3",
.out = & ipa_build6(0x26052700, 0x00000003, 0x00000000, 0x471393E3),
},
{
.in = "2605:2700:0:3:4713:93e3",
.out = NULL,
},
{
.in = "2",
.out = NULL,
},
};
return bt_assert_batch(test_vectors, test_ipa_pton, bt_fmt_str, bt_fmt_ipa);
return bt_assert_batch(test_vectors, test_ip6_pton, bt_fmt_str, bt_fmt_ipa);
}
static int

View File

@ -29,6 +29,42 @@
#include "nest/bird.h"
#include "lib/lists.h"
LIST_INLINE int
check_list(list *l, node *n)
{
if (!l)
{
ASSERT_DIE(n);
ASSERT_DIE(n->prev);
do { n = n->prev; } while (n->prev);
l = SKIP_BACK(list, head_node, n);
}
int seen = 0;
ASSERT_DIE(l->null == NULL);
ASSERT_DIE(l->head != NULL);
ASSERT_DIE(l->tail != NULL);
node *prev = &l->head_node, *cur = l->head, *next = l->head->next;
while (next)
{
if (cur == n)
seen++;
ASSERT_DIE(cur->prev == prev);
prev = cur;
cur = next;
next = next->next;
}
ASSERT_DIE(cur == &(l->tail_node));
ASSERT_DIE(!n || (seen == 1));
return 1;
}
/**
* add_tail - append a node to a list
* @l: linked list
@ -39,6 +75,10 @@
LIST_INLINE void
add_tail(list *l, node *n)
{
EXPENSIVE_CHECK(check_list(l, NULL));
ASSUME(n->prev == NULL);
ASSUME(n->next == NULL);
node *z = l->tail;
n->next = &l->tail_node;
@ -57,6 +97,10 @@ add_tail(list *l, node *n)
LIST_INLINE void
add_head(list *l, node *n)
{
EXPENSIVE_CHECK(check_list(l, NULL));
ASSUME(n->prev == NULL);
ASSUME(n->next == NULL);
node *z = l->head;
n->next = z;
@ -76,6 +120,10 @@ add_head(list *l, node *n)
LIST_INLINE void
insert_node(node *n, node *after)
{
EXPENSIVE_CHECK(check_list(l, after));
ASSUME(n->prev == NULL);
ASSUME(n->next == NULL);
node *z = after->next;
n->next = z;
@ -93,6 +141,8 @@ insert_node(node *n, node *after)
LIST_INLINE void
rem_node(node *n)
{
EXPENSIVE_CHECK(check_list(NULL, n));
node *z = n->prev;
node *x = n->next;
@ -103,24 +153,20 @@ rem_node(node *n)
}
/**
* replace_node - replace a node in a list with another one
* @old: node to be removed
* @new: node to be inserted
* update_node - update node after calling realloc on it
* @n: node to be updated
*
* Replaces node @old in the list it's linked in with node @new. Node
* @old may be a copy of the original node, which is not accessed
* through the list. The function could be called with @old == @new,
* which just fixes neighbors' pointers in the case that the node
* was reallocated.
* Fixes neighbor pointers.
*/
LIST_INLINE void
replace_node(node *old, node *new)
update_node(node *n)
{
old->next->prev = new;
old->prev->next = new;
ASSUME(n->next->prev == n->prev->next);
new->prev = old->prev;
new->next = old->next;
n->next->prev = n;
n->prev->next = n;
EXPENSIVE_CHECK(check_list(NULL, n));
}
/**
@ -149,6 +195,9 @@ init_list(list *l)
LIST_INLINE void
add_tail_list(list *to, list *l)
{
EXPENSIVE_CHECK(check_list(to, NULL));
EXPENSIVE_CHECK(check_list(l, NULL));
node *p = to->tail;
node *q = l->head;
@ -157,6 +206,8 @@ add_tail_list(list *to, list *l)
q = l->tail;
q->next = &to->tail_node;
to->tail = q;
EXPENSIVE_CHECK(check_list(to, NULL));
}
LIST_INLINE uint
@ -165,6 +216,8 @@ list_length(list *l)
uint len = 0;
node *n;
EXPENSIVE_CHECK(check_list(l, NULL));
WALK_LIST(n, *l)
len++;

View File

@ -222,26 +222,29 @@ t_remove_node(void)
}
static int
t_replace_node(void)
t_update_node(void)
{
node head, inside, tail;
init_list_();
fill_list();
replace_node(&nodes[0], &head);
head = nodes[0];
update_node(&head);
bt_assert(l.head == &head);
bt_assert(head.prev == NODE &l.head);
bt_assert(head.next == &nodes[1]);
bt_assert(nodes[1].prev == &head);
replace_node(&nodes[MAX_NUM/2], &inside);
inside = nodes[MAX_NUM/2];
update_node(&inside);
bt_assert(nodes[MAX_NUM/2-1].next == &inside);
bt_assert(nodes[MAX_NUM/2+1].prev == &inside);
bt_assert(inside.prev == &nodes[MAX_NUM/2-1]);
bt_assert(inside.next == &nodes[MAX_NUM/2+1]);
replace_node(&nodes[MAX_NUM-1], &tail);
tail = nodes[MAX_NUM-1];
update_node(&tail);
bt_assert(l.tail == &tail);
bt_assert(tail.prev == &nodes[MAX_NUM-2]);
bt_assert(tail.next == NODE &l.null);
@ -280,7 +283,7 @@ main(int argc, char *argv[])
bt_test_suite(t_add_head, "Adding nodes to head of list");
bt_test_suite(t_insert_node, "Inserting nodes to list");
bt_test_suite(t_remove_node, "Removing nodes from list");
bt_test_suite(t_replace_node, "Replacing nodes in list");
bt_test_suite(t_update_node, "Updating nodes in list");
bt_test_suite(t_add_tail_list, "At the tail of a list adding the another list");
return bt_exit_value();

View File

@ -174,10 +174,10 @@ extern const u16 net_max_text_length[];
((net_addr_roa6) { NET_ROA6, pxlen, sizeof(net_addr_roa6), prefix, max_pxlen, asn })
#define NET_ADDR_FLOW4(prefix,pxlen,dlen) \
((net_addr_flow4) { NET_FLOW4, pxlen, sizeof(net_addr_ip4) + dlen, prefix })
((net_addr_flow4) { NET_FLOW4, pxlen, sizeof(net_addr_flow4) + dlen, prefix })
#define NET_ADDR_FLOW6(prefix,pxlen,dlen) \
((net_addr_flow6) { NET_FLOW6, pxlen, sizeof(net_addr_ip6) + dlen, prefix })
((net_addr_flow6) { NET_FLOW6, pxlen, sizeof(net_addr_flow6) + dlen, prefix })
#define NET_ADDR_IP6_SADR(dst_prefix,dst_pxlen,src_prefix,src_pxlen) \
((net_addr_ip6_sadr) { NET_IP6_SADR, dst_pxlen, sizeof(net_addr_ip6_sadr), dst_prefix, src_pxlen, src_prefix })

View File

@ -34,13 +34,7 @@ static int skip_atoi(const char **s)
#define SPECIAL 32 /* 0x */
#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
#define do_div(n,base) ({ \
int __res; \
__res = ((unsigned long) n) % (unsigned) base; \
n = ((unsigned long) n) / (unsigned) base; \
__res; })
static char * number(char * str, long num, int base, int size, int precision,
static char * number(char * str, u64 num, uint base, int size, int precision,
int type, int remains)
{
char c,sign,tmp[66];
@ -58,7 +52,7 @@ static char * number(char * str, long num, int base, int size, int precision,
c = (type & ZEROPAD) ? '0' : ' ';
sign = 0;
if (type & SIGN) {
if (num < 0) {
if (num > (u64) INT64_MAX) {
sign = '-';
num = -num;
size--;
@ -79,8 +73,11 @@ static char * number(char * str, long num, int base, int size, int precision,
i = 0;
if (num == 0)
tmp[i++]='0';
else while (num != 0)
tmp[i++] = digits[do_div(num,base)];
else while (num != 0) {
uint res = num % base;
num = num / base;
tmp[i++] = digits[res];
}
if (i > precision)
precision = i;
size -= precision;
@ -128,16 +125,17 @@ static char * number(char * str, long num, int base, int size, int precision,
* value printed as eight :-separated octets), |%t| for time values (btime) with
* specified subsecond precision, and |%m| resp. |%M| for error messages (uses
* strerror() to translate @errno code to message text). On the other hand, it
* doesn't support floating point numbers.
* doesn't support floating point numbers. The bvsnprintf() supports |%h| and
* |%l| qualifiers, but |%l| is used for s64/u64 instead of long/ulong.
*
* Result: number of characters of the output string or -1 if
* the buffer space was insufficient.
*/
int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
{
int len;
unsigned long num;
int i, base;
int len, i;
u64 num;
uint base;
u32 x;
u64 X;
btime t;
@ -152,7 +150,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
int field_width; /* width of output field */
int precision; /* min. # of digits for integers; max
number of chars for from string */
int qualifier; /* 'h', 'l', or 'L' for integer fields */
int qualifier; /* 'h' or 'l' for integer fields */
for (start=str=buf ; *fmt ; ++fmt, size-=(str-start), start=str) {
if (*fmt != '%') {
@ -286,16 +284,15 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
field_width = 2*sizeof(void *);
flags |= ZEROPAD;
}
str = number(str,
(unsigned long) va_arg(args, void *), 16,
field_width, precision, flags, size);
str = number(str, (uintptr_t) va_arg(args, void *), 16,
field_width, precision, flags, size);
if (!str)
return -1;
continue;
case 'n':
if (qualifier == 'l') {
long * ip = va_arg(args, long *);
s64 * ip = va_arg(args, s64 *);
*ip = (str - buf);
} else {
int * ip = va_arg(args, int *);
@ -393,7 +390,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
/* Print seconds */
flags |= SIGN;
str = number(str, t1, 10, field_width, 0, flags, size);
str = number(str, (u64) t1, 10, field_width, 0, flags, size);
if (!str)
return -1;
@ -411,7 +408,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
/* Print sub-seconds */
*str++ = '.';
str = number(str, t2, 10, precision, 0, ZEROPAD, size - 1);
str = number(str, (u64) t2, 10, precision, 0, ZEROPAD, size - 1);
if (!str)
return -1;
}
@ -446,16 +443,22 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
--fmt;
continue;
}
if (qualifier == 'l')
num = va_arg(args, unsigned long);
else if (qualifier == 'h') {
num = (unsigned short) va_arg(args, int);
if (flags & SIGN)
num = (short) num;
} else if (flags & SIGN)
num = va_arg(args, int);
else
num = va_arg(args, uint);
if (flags & SIGN) {
/* Conversions valid per ISO C99 6.3.1.3 (2) */
if (qualifier == 'l')
num = (u64) va_arg(args, s64);
else if (qualifier == 'h')
num = (u64) (short) va_arg(args, int);
else
num = (u64) va_arg(args, int);
} else {
if (qualifier == 'l')
num = va_arg(args, u64);
else if (qualifier == 'h')
num = (unsigned short) va_arg(args, int);
else
num = va_arg(args, uint);
}
str = number(str, num, base, field_width, precision, flags, size);
if (!str)
return -1;

View File

@ -45,7 +45,7 @@ t_simple(void)
else
BSPRINTF(16, "00000fee1a15600d", buf, "%p", (void *) 0xfee1a15600d);
long ln = 0;
s64 ln = 0;
BSPRINTF(10, "TeStStRiNg", buf, "TeStS%lntRiNg", &ln);
bt_assert_msg(ln == 5, "fmt=\"TeStS%%lntRiNg\", &ln makes ln=%ld, want 5", ln);
@ -54,7 +54,19 @@ t_simple(void)
BSPRINTF(2, "+1", buf, "%+d", 1);
BSPRINTF(2, " 1", buf, "% d", 1);
BSPRINTF(2, "-1", buf, "%d", -1);
BSPRINTF(11, "-2147483648", buf, "%d", -2147483648);
BSPRINTF(11, "-2147483648", buf, "%d", INT32_MIN);
BSPRINTF(10, "2147483647", buf, "%d", INT32_MAX);
BSPRINTF(1, "0", buf, "%u", 0x0);
BSPRINTF(10, "4294967295", buf, "%u", 0xFFFFFFFF);
BSPRINTF(4, "-100", buf, "%ld", (s64) -100);
BSPRINTF(3, "100", buf, "%ld", (s64) 100);
BSPRINTF(20, "-9223372036854775808", buf, "%ld", INT64_MIN);
BSPRINTF(19, "9223372036854775807", buf, "%ld", INT64_MAX);
BSPRINTF(3, "0 8", buf, "%lu %lu", U64(0), U64(8));
BSPRINTF(20, "18446744073709551615", buf, "%lu", UINT64_MAX);
return 1;
}

View File

@ -340,6 +340,7 @@ mb_alloc(pool *p, unsigned size)
struct mblock *b = xmalloc(sizeof(struct mblock) + size);
b->r.class = &mb_class;
b->r.n = (node) {};
add_tail(&p->inside, &b->r.n);
b->size = size;
return b->data;
@ -387,7 +388,7 @@ mb_realloc(void *m, unsigned size)
struct mblock *b = SKIP_BACK(struct mblock, data, m);
b = xrealloc(b, sizeof(struct mblock) + size);
replace_node(&b->r.n, &b->r.n);
update_node(&b->r.n);
b->size = size;
return b->data;
}

View File

@ -83,6 +83,7 @@ typedef struct slab slab;
slab *sl_new(pool *, unsigned size);
void *sl_alloc(slab *);
void *sl_allocz(slab *);
void sl_free(slab *, void *);
/*

View File

@ -88,6 +88,14 @@ sl_alloc(slab *s)
return o->data;
}
void *
sl_allocz(slab *s)
{
void *obj = sl_alloc(s);
memset(obj, 0, s->size);
return obj;
}
void
sl_free(slab *s, void *oo)
{
@ -216,8 +224,11 @@ sl_new_head(slab *s)
struct sl_obj *no;
uint n = s->objs_per_slab;
h->first_free = o;
h->num_full = 0;
*h = (struct sl_head) {
.first_free = o,
.num_full = 0,
};
while (n--)
{
o->slab = h;
@ -275,6 +286,22 @@ no_partial:
goto okay;
}
/**
* sl_allocz - allocate an object from Slab and zero it
* @s: slab
*
* sl_allocz() allocates space for a single object from the
* Slab and returns a pointer to the object after zeroing out
* the object memory.
*/
void *
sl_allocz(slab *s)
{
void *obj = sl_alloc(s);
memset(obj, 0, s->data_size);
return obj;
}
/**
* sl_free - return a free object back to a Slab
* @s: slab

View File

@ -76,7 +76,7 @@ typedef struct birdsock {
int rcv_ttl; /* TTL of last received datagram */
node n;
void *rbuf_alloc, *tbuf_alloc;
char *password; /* Password for MD5 authentication */
const char *password; /* Password for MD5 authentication */
const char *err; /* Error message */
struct ssh_sock *ssh; /* Used in SK_SSH */
} sock;
@ -106,7 +106,7 @@ int sk_leave_group(sock *s, ip_addr maddr); /* Leave multicast group on sk iface
int sk_setup_broadcast(sock *s);
int sk_set_ttl(sock *s, int ttl); /* Set transmit TTL for given socket */
int sk_set_min_ttl(sock *s, int ttl); /* Set minimal accepted TTL for given socket */
int sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, struct iface *ifa, char *passwd, int setkey);
int sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int setkey);
int sk_set_ipv6_checksum(sock *s, int offset);
int sk_set_icmp6_filter(sock *s, int p1, int p2);
void sk_log_error(sock *s, const char *p);

View File

@ -72,6 +72,15 @@ bstrcmp(const char *s1, const char *s2)
return !s2 - !s1;
}
static inline void *
bmemcpy(void *dest, const void *src, size_t n)
{
if (n)
return memcpy(dest, src, n);
else
return dest;
}
#define ROUTER_ID_64_LENGTH 23
#endif

View File

@ -253,9 +253,9 @@ timer_init(void)
* type &btime.
*/
btime
tm_parse_time(char *x)
tm_parse_time(const char *x)
{
struct tm tm;
struct tm tm = {};
int usec, n1, n2, n3, r;
r = sscanf(x, "%d-%d-%d%n %d:%d:%d%n.%d%n",

View File

@ -106,7 +106,7 @@ void timer_init(void);
struct timeformat {
char *fmt1, *fmt2;
const char *fmt1, *fmt2;
btime limit;
};
@ -120,7 +120,7 @@ struct timeformat {
#define TM_DATETIME_BUFFER_SIZE 32 /* Buffer size required by tm_format_time() */
btime tm_parse_time(char *x);
btime tm_parse_time(const char *x);
void tm_format_time(char *x, struct timeformat *fmt, btime t);
int tm_format_real_time(char *x, size_t max, const char *fmt, btime t);

View File

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

View File

@ -0,0 +1,11 @@
FROM centos:8
RUN yum -y upgrade
RUN yum -y install \
autoconf \
flex \
bison \
pkgconfig \
'readline-devel' \
'pkgconfig(ncurses)' \
gcc \
make

View File

@ -0,0 +1,12 @@
FROM debian:buster-slim
ENV DEBIAN_FRONTEND noninteractive
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
RUN apt-get -y update
RUN apt-get -y upgrade
RUN apt-get -y install \
autoconf \
build-essential \
flex \
bison \
ncurses-dev \
libreadline-dev

View File

@ -0,0 +1,12 @@
FROM i386/debian:stretch-slim
ENV DEBIAN_FRONTEND noninteractive
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
RUN apt-get -y update
RUN apt-get -y upgrade
RUN apt-get -y install \
autoconf \
build-essential \
flex \
bison \
ncurses-dev \
libreadline-dev

View File

@ -1,7 +1,8 @@
FROM debian:wheezy-slim
ENV DEBIAN_FRONTEND noninteractive
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
RUN apt-get -y update
RUN echo 'deb http://archive.debian.org/debian/ wheezy main' > /etc/apt/sources.list
RUN echo 'deb http://archive.debian.org/debian-security/ wheezy/updates main' >> /etc/apt/sources.list
RUN apt-get -y update -o Acquire::Check-Valid-Until=false
RUN apt-get -y upgrade
RUN apt-get -y install \
autoconf \

View File

@ -1,7 +1,8 @@
FROM i386/debian:wheezy-slim
ENV DEBIAN_FRONTEND noninteractive
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
RUN apt-get -y update
RUN echo 'deb http://archive.debian.org/debian/ wheezy main' > /etc/apt/sources.list
RUN echo 'deb http://archive.debian.org/debian-security/ wheezy/updates main' >> /etc/apt/sources.list
RUN apt-get -y update -o Acquire::Check-Valid-Until=false
RUN apt-get -y upgrade
RUN apt-get -y install \
autoconf \

View File

@ -1,6 +1,7 @@
FROM fedora:25
RUN dnf -y upgrade
RUN dnf -y install \
make \
autoconf \
flex \
bison \

View File

@ -1,6 +1,7 @@
FROM fedora:26
RUN dnf -y upgrade
RUN dnf -y install \
make \
autoconf \
flex \
bison \

View File

@ -0,0 +1,11 @@
FROM fedora:27
RUN dnf -y upgrade
RUN dnf -y install \
make \
autoconf \
flex \
bison \
pkgconfig \
'readline-devel' \
'pkgconfig(ncurses)' \
gcc

View File

@ -0,0 +1,11 @@
FROM fedora:28
RUN dnf -y upgrade
RUN dnf -y install \
make \
autoconf \
flex \
bison \
pkgconfig \
'readline-devel' \
'pkgconfig(ncurses)' \
gcc

View File

@ -0,0 +1,11 @@
FROM fedora:29
RUN dnf -y upgrade
RUN dnf -y install \
make \
autoconf \
flex \
bison \
pkgconfig \
'readline-devel' \
'pkgconfig(ncurses)' \
gcc

View File

@ -0,0 +1,11 @@
FROM fedora:30
RUN dnf -y upgrade
RUN dnf -y install \
make \
autoconf \
flex \
bison \
pkgconfig \
'readline-devel' \
'pkgconfig(ncurses)' \
gcc

View File

@ -0,0 +1,11 @@
FROM fedora:31
RUN dnf -y upgrade
RUN dnf -y install \
make \
autoconf \
flex \
bison \
pkgconfig \
'readline-devel' \
'pkgconfig(ncurses)' \
gcc

View File

@ -1,4 +1,4 @@
FROM opensuse:42.3
FROM opensuse/leap:15.0
RUN zypper -n up
RUN zypper -n install \
autoconf \

View File

@ -0,0 +1,11 @@
FROM opensuse/leap:15.1
RUN zypper -n up
RUN zypper -n install \
autoconf \
flex \
bison \
pkgconfig \
readline-devel \
ncurses-devel \
gcc \
gmake

View File

@ -0,0 +1,12 @@
FROM ubuntu:18.04
ENV DEBIAN_FRONTEND noninteractive
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
RUN apt-get -y update
RUN apt-get -y upgrade
RUN apt-get -y install \
autoconf \
build-essential \
flex \
bison \
ncurses-dev \
libreadline-dev

View File

@ -0,0 +1,12 @@
FROM ubuntu:19.10
ENV DEBIAN_FRONTEND noninteractive
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
RUN apt-get -y update
RUN apt-get -y upgrade
RUN apt-get -y install \
autoconf \
build-essential \
flex \
bison \
ncurses-dev \
libreadline-dev

View File

@ -25,7 +25,7 @@
#define BAD(DSC, VAL) ({ err_dsc = DSC; err_val = VAL; goto bad; })
int
as_path_valid(byte *data, uint len, int bs, int confed, char *err, uint elen)
as_path_valid(byte *data, uint len, int bs, int sets, int confed, char *err, uint elen)
{
byte *pos = data;
char *err_dsc = NULL;
@ -46,13 +46,21 @@ as_path_valid(byte *data, uint len, int bs, int confed, char *err, uint elen)
switch (type)
{
case AS_PATH_SET:
if (!sets)
BAD("AS_SET segment", type);
break;
case AS_PATH_SEQUENCE:
break;
case AS_PATH_CONFED_SEQUENCE:
case AS_PATH_CONFED_SET:
if (!confed)
BAD("AS_CONFED* segment", type);
BAD("AS_CONFED_SEQUENCE segment", type);
break;
case AS_PATH_CONFED_SET:
if (!sets || !confed)
BAD("AS_CONFED_SET segment", type);
break;
default:
@ -719,7 +727,7 @@ parse_path(const struct adata *path, struct pm_pos *pp)
}
static int
pm_match(struct pm_pos *pos, u32 asn, u32 asn2)
pm_match_val(const struct pm_pos *pos, u32 asn, u32 asn2)
{
u32 gas;
if (! pos->set)
@ -741,7 +749,7 @@ pm_match(struct pm_pos *pos, u32 asn, u32 asn2)
}
static int
pm_match_set(struct pm_pos *pos, const struct f_tree *set)
pm_match_set(const struct pm_pos *pos, const struct f_tree *set)
{
struct f_val asn = { .type = T_INT };
@ -765,25 +773,34 @@ pm_match_set(struct pm_pos *pos, const struct f_tree *set)
return 0;
}
static void
pm_mark(struct pm_pos *pos, int i, int plen, int *nl, int *nh)
static inline int
pm_match(const struct pm_pos *pos, const struct f_path_mask_item *mask, u32 asn, u32 asn2)
{
int j;
return ((mask->kind == PM_QUESTION) ||
((mask->kind != PM_ASN_SET) ?
pm_match_val(pos, asn, asn2) :
pm_match_set(pos, mask->set)));
}
if (pos[i].set)
pos[i].mark = 1;
static void
pm_mark(struct pm_pos *pos, int *i, int plen, int *nl, int *nh)
{
int j = *i;
if (pos[j].set)
do { pos[j].mark = 1; j++; }
while ((j < plen) && pos[j].set);
else
j++;
for (j = i + 1; (j < plen) && pos[j].set && (! pos[j].mark); j++)
pos[j].mark = 1;
pos[j].mark = 1;
/* We are going downwards, therefore every mark is
new low and just the first mark is new high */
/* Update low, high based on first and last marked pos */
int l = pos[*i].set ? *i : j;
*nl = i + (pos[i].set ? 0 : 1);
if (*nh < 0)
*nh = j;
*nl = (*nl < 0) ? l : MIN(*nl, l);
*nh = MAX(*nh, j);
*i = j;
}
/* AS path matching is nontrivial. Because AS path can
@ -813,7 +830,7 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
{
struct pm_pos pos[2048 + 1];
int plen = parse_path(path, pos);
int l, h, i, nh, nl;
int l, h, i, nh, nl, last, loop;
u32 val = 0;
u32 val2 = 0;
@ -823,7 +840,7 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
pos[plen].set = 0;
pos[plen].mark = 0;
l = h = 0;
l = h = loop = 0;
pos[0].mark = 1;
for (uint m=0; m < mask->len; m++)
@ -839,6 +856,10 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
h = plen;
break;
case PM_LOOP:
loop = 1;
break;
case PM_ASN: /* Define single ASN as ASN..ASN - very narrow interval */
val2 = val = mask->item[m].asn;
goto step;
@ -852,15 +873,22 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
case PM_ASN_SET:
step:
nh = nl = -1;
last = plen;
for (i = h; i >= l; i--)
if (pos[i].mark)
{
pos[i].mark = 0;
if ((mask->item[m].kind == PM_QUESTION) ||
((mask->item[m].kind != PM_ASN_SET) ?
pm_match(pos + i, val, val2) :
pm_match_set(pos + i, mask->item[m].set)))
pm_mark(pos, i, plen, &nl, &nh);
int j = i;
match:
if (pm_match(pos + j, &mask->item[m], val, val2))
{
pm_mark(pos, &j, plen, &nl, &nh);
if (loop && (j < last))
goto match;
}
last = i;
}
if (nh < 0)
@ -868,6 +896,7 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
h = nh;
l = nl;
loop = 0;
break;
}
}

View File

@ -30,7 +30,7 @@
struct f_tree;
int as_path_valid(byte *data, uint len, int bs, int confed, char *err, uint elen);
int as_path_valid(byte *data, uint len, int bs, int sets, int confed, char *err, uint elen);
int as_path_16to32(byte *dst, const byte *src, uint len);
int as_path_32to16(byte *dst, const byte *src, uint len);
int as_path_contains_as4(const struct adata *path);
@ -61,6 +61,7 @@ static inline struct adata *as_path_prepend(struct linpool *pool, const struct a
#define PM_ASN_EXPR 3
#define PM_ASN_RANGE 4
#define PM_ASN_SET 5
#define PM_LOOP 6
struct f_path_mask_item {
union {
@ -111,7 +112,7 @@ static inline struct adata *
aggregator_to_old(struct linpool *pool, const struct adata *a)
{
struct adata *d = lp_alloc_adata(pool, 8);
put_u32(d->data, 0xFFFF);
put_u32(d->data, AS_TRANS);
memcpy(d->data + 4, a->data + 4, 4);
return d;
}

View File

@ -9,9 +9,20 @@
#include "lib/lists.h"
#include "lib/resource.h"
#include "conf/conf.h"
struct bfd_session;
struct bfd_options {
u32 min_rx_int;
u32 min_tx_int;
u32 idle_tx_int;
u8 multiplier;
u8 passive;
u8 passive_set;
u8 mode;
};
struct bfd_request {
resource r;
node n;
@ -20,6 +31,7 @@ struct bfd_request {
ip_addr local;
struct iface *iface;
struct iface *vrf;
struct bfd_options opts;
void (*hook)(struct bfd_request *);
void *data;
@ -32,6 +44,7 @@ struct bfd_request {
u8 down;
};
#define BGP_BFD_GRACEFUL 2 /* BFD down triggers graceful restart */
#define BFD_STATE_ADMIN_DOWN 0
#define BFD_STATE_DOWN 1
@ -39,15 +52,20 @@ struct bfd_request {
#define BFD_STATE_UP 3
static inline struct bfd_options * bfd_new_options(void)
{ return cfg_allocz(sizeof(struct bfd_options)); }
#ifdef CONFIG_BFD
struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, struct iface *vrf, void (*hook)(struct bfd_request *), void *data);
struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, struct iface *vrf, void (*hook)(struct bfd_request *), void *data, const struct bfd_options *opts);
void bfd_update_request(struct bfd_request *req, const struct bfd_options *opts);
static inline void cf_check_bfd(int use UNUSED) { }
#else
static inline struct bfd_request * bfd_request_session(pool *p UNUSED, ip_addr addr UNUSED, ip_addr local UNUSED, struct iface *iface UNUSED, struct iface *vrf UNUSED, void (*hook)(struct bfd_request *) UNUSED, void *data UNUSED) { return NULL; }
static inline struct bfd_request * bfd_request_session(pool *p UNUSED, ip_addr addr UNUSED, ip_addr local UNUSED, struct iface *iface UNUSED, struct iface *vrf UNUSED, void (*hook)(struct bfd_request *) UNUSED, void *data UNUSED, const struct bfd_options *opts UNUSED) { return NULL; }
static inline void bfd_update_request(struct bfd_request *req UNUSED, const struct bfd_options *opts UNUSED) { };
static inline void cf_check_bfd(int use) { if (use) cf_error("BFD not available"); }

View File

@ -143,6 +143,7 @@ cli_printf(cli *c, int code, char *msg, ...)
{
size = bsprintf(buf, "%04d ", cd);
errcode = 8000;
cd = 0; /* Final message - no more continuation lines */
}
c->last_reply = cd;

View File

@ -58,6 +58,9 @@ void cli_printf(cli *, int, char *, ...);
#define cli_msg(x...) cli_printf(this_cli, x)
void cli_set_log_echo(cli *, uint mask, uint size);
static inline void cli_separator(cli *c)
{ if (c->last_reply) cli_printf(c, -c->last_reply, ""); };
/* Functions provided to sysdep layer */
cli *cli_new(void *);

View File

@ -27,6 +27,7 @@ cmd_show_status(void)
cli_msg(-1000, "BIRD " BIRD_VERSION);
tm_format_time(tim, &config->tf_base, current_time());
cli_msg(-1011, "Router ID is %R", config->router_id);
cli_msg(-1011, "Hostname is %s", config->hostname);
cli_msg(-1011, "Current server time is %s", tim);
tm_format_time(tim, &config->tf_base, boot_time);
cli_msg(-1011, "Last reboot on %s", tim);

View File

@ -25,6 +25,7 @@ static struct iface_patt_node *this_ipn;
static list *this_p_list;
static struct password_item *this_p_item;
static int password_id;
static struct bfd_options *this_bfd_opts;
static void
iface_patt_check(void)
@ -51,6 +52,28 @@ get_passwords(void)
return rv;
}
static inline void
init_bfd_opts(struct bfd_options **opts)
{
cf_check_bfd(1);
if (! *opts)
*opts = bfd_new_options();
}
static inline void
open_bfd_opts(struct bfd_options **opts)
{
init_bfd_opts(opts);
this_bfd_opts = *opts;
}
static inline void
close_bfd_opts(void)
{
this_bfd_opts = NULL;
}
static void
proto_postconfig(void)
{
@ -64,17 +87,19 @@ proto_postconfig(void)
CF_DECLS
CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
CF_KEYWORDS(ROUTER, ID, HOSTNAME, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, DEFAULT, TABLE, STATES, ROUTES, FILTERS)
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS)
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED, RPKI)
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, CHANNELS, INTERFACES)
CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512)
CF_KEYWORDS(PRIMARY, STATS, COUNT, BY, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE)
CF_KEYWORDS(PRIMARY, STATS, COUNT, BY, FOR, COMMANDS, PREEXPORT, NOEXPORT, EXPORTED, GENERATE)
CF_KEYWORDS(BGP, PASSWORDS, DESCRIPTION, SORTED)
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
CF_KEYWORDS(CHECK, LINK)
/* For r_args_channel */
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
@ -84,6 +109,7 @@ CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIREC
CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
%type <i32> idval
%type <f> imexport
@ -97,7 +123,8 @@ CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
%type <cl> limit_spec
%type <net> r_args_for_val
%type <net_ptr> r_args_for
%type <t> r_args_channel
%type <t> channel_sym
%type <c> channel_arg
CF_GRAMMAR
@ -124,6 +151,10 @@ idval:
}
;
conf: hostname_override ;
hostname_override: HOSTNAME text ';' { new_config->hostname = $2; } ;
conf: gr_opts ;
gr_opts: GRACEFUL RESTART WAIT expr ';' { new_config->gr_wait = $4; } ;
@ -221,7 +252,7 @@ channel_start: net_type
$$ = this_channel = channel_config_get(NULL, net_label[$1], $1, this_proto);
};
channel_item:
channel_item_:
TABLE rtable {
if (this_channel->net_type && ($2->addr_type != this_channel->net_type))
cf_error("Incompatible table type");
@ -234,6 +265,13 @@ channel_item:
| EXPORT LIMIT limit_spec { this_channel->out_limit = $3; }
| PREFERENCE expr { this_channel->preference = $2; check_u16($2); }
| IMPORT KEEP FILTERED bool { this_channel->in_keep_filtered = $4; }
| RPKI RELOAD bool { this_channel->rpki_reload = $3; }
;
/* To avoid grammar collision in Pipe protocol */
channel_item:
channel_item_
| DEBUG debug_mask { this_channel->debug = $2; }
;
channel_opts:
@ -284,6 +322,7 @@ conf: debug_default ;
debug_default:
DEBUG PROTOCOLS debug_mask { new_config->proto_default_debug = $3; }
| DEBUG CHANNELS debug_mask { new_config->channel_default_debug = $3; }
| DEBUG COMMANDS expr { new_config->cli_debug = $3; }
;
@ -453,11 +492,11 @@ password_item:
password_item_begin:
PASSWORD text {
if (!this_p_list) {
this_p_list = cfg_alloc(sizeof(list));
this_p_list = cfg_allocz(sizeof(list));
init_list(this_p_list);
password_id = 1;
}
this_p_item = cfg_alloc(sizeof (struct password_item));
this_p_item = cfg_allocz(sizeof(struct password_item));
this_p_item->password = $2;
this_p_item->length = strlen($2);
this_p_item->genfrom = 0;
@ -478,7 +517,7 @@ password_item_params:
| ACCEPT TO time ';' password_item_params { this_p_item->accto = $3; }
| FROM time ';' password_item_params { this_p_item->genfrom = this_p_item->accfrom = $2; }
| TO time ';' password_item_params { this_p_item->gento = this_p_item->accto = $2; }
| ID expr ';' password_item_params { this_p_item->id = $2; if ($2 <= 0) cf_error("Password ID has to be greated than zero."); }
| ID expr ';' password_item_params { this_p_item->id = $2; if ($2 > 255) cf_error("Password ID must be in range 0-255"); }
| ALGORITHM password_algorithm ';' password_item_params { this_p_item->alg = $2; }
;
@ -495,6 +534,28 @@ password_algorithm:
| HMAC SHA512 { $$ = ALG_HMAC_SHA512; }
;
/* BFD options */
bfd_item:
INTERVAL expr_us { this_bfd_opts->min_rx_int = this_bfd_opts->min_tx_int = $2; }
| MIN RX INTERVAL expr_us { this_bfd_opts->min_rx_int = $4; }
| MIN TX INTERVAL expr_us { this_bfd_opts->min_tx_int = $4; }
| IDLE TX INTERVAL expr_us { this_bfd_opts->idle_tx_int = $4; }
| MULTIPLIER expr { this_bfd_opts->multiplier = $2; }
| PASSIVE bool { this_bfd_opts->passive = $2; this_bfd_opts->passive_set = 1; }
| GRACEFUL { this_bfd_opts->mode = BGP_BFD_GRACEFUL; }
;
bfd_items:
/* empty */
| bfd_items bfd_item ';'
;
bfd_opts:
'{' bfd_items '}'
;
/* Core commands */
CF_CLI_HELP(SHOW, ..., [[Show status information]])
@ -556,26 +617,14 @@ r_args:
rt_show_add_table($$, t->table);
$$->tables_defined_by = RSD_TDB_ALL;
}
| r_args IMPORT TABLE CF_SYM_KNOWN '.' r_args_channel {
cf_assert_symbol($4, SYM_PROTO);
$$ = $1;
struct proto_config *cf = $4->proto;
if (!cf->proto) cf_error("%s is not a protocol", $4->name);
struct channel *c = proto_find_channel_by_name(cf->proto, $6);
if (!c) cf_error("Channel %s.%s not found", $4->name, $6);
if (!c->in_table) cf_error("No import table in channel %s.%s", $4->name, $6);
rt_show_add_table($$, c->in_table);
| r_args IMPORT TABLE channel_arg {
if (!$4->in_table) cf_error("No import table in channel %s.%s", $4->proto->name, $4->name);
rt_show_add_table($$, $4->in_table);
$$->tables_defined_by = RSD_TDB_DIRECT;
}
| r_args EXPORT TABLE CF_SYM_KNOWN '.' r_args_channel {
cf_assert_symbol($4, SYM_PROTO);
$$ = $1;
struct proto_config *cf = $4->proto;
if (!cf->proto) cf_error("%s is not a protocol", $4->name);
struct channel *c = proto_find_channel_by_name(cf->proto, $6);
if (!c) cf_error("Channel %s.%s not found", $4->name, $6);
if (!c->out_table) cf_error("No export table in channel %s.%s", $4->name, $6);
rt_show_add_table($$, c->out_table);
| r_args EXPORT TABLE channel_arg {
if (!$4->out_table) cf_error("No export table in channel %s.%s", $4->proto->name, $4->name);
rt_show_add_table($$, $4->out_table);
$$->tables_defined_by = RSD_TDB_DIRECT;
}
| r_args FILTER filter {
@ -610,15 +659,11 @@ r_args:
$$->export_protocol = c->proto;
$$->tables_defined_by = RSD_TDB_INDIRECT;
}
| r_args export_mode CF_SYM_KNOWN '.' r_args_channel {
cf_assert_symbol($3, SYM_PROTO);
struct proto_config *c = (struct proto_config *) $3->proto;
| r_args export_mode channel_arg {
$$ = $1;
if ($$->export_mode) cf_error("Export specified twice");
if (!c->proto) cf_error("%s is not a protocol", $3->name);
$$->export_mode = $2;
$$->export_channel = proto_find_channel_by_name(c->proto, $5);
if (!$$->export_channel) cf_error("Export channel not found");
$$->export_channel = $3;
$$->tables_defined_by = RSD_TDB_INDIRECT;
}
| r_args PROTOCOL CF_SYM_KNOWN {
@ -683,10 +728,11 @@ export_mode:
PREEXPORT { $$ = RSEM_PREEXPORT; }
| EXPORT { $$ = RSEM_EXPORT; }
| NOEXPORT { $$ = RSEM_NOEXPORT; }
| EXPORTED { $$ = RSEM_EXPORTED; }
;
/* This is ugly hack */
r_args_channel:
channel_sym:
IPV4 { $$ = "ipv4"; }
| IPV4_MC { $$ = "ipv4-mc"; }
| IPV4_MPLS { $$ = "ipv4-mpls"; }
@ -709,6 +755,16 @@ r_args_channel:
| SEC { $$ = "sec"; }
;
channel_arg:
CF_SYM_KNOWN '.' channel_sym {
cf_assert_symbol($1, SYM_PROTO);
struct proto *p = $1->proto->proto;
if (!p) cf_error("%s is not a protocol", $1->name);
$$ = proto_find_channel_by_name(p, $3);
if (!$$) cf_error("Channel %s.%s not found", $1->name, $3);
}
;
CF_CLI_HELP(SHOW SYMBOLS, ..., [[Show all known symbolic names]])
CF_CLI(SHOW SYMBOLS, sym_args, [table|filter|function|protocol|template|<symbol>], [[Show all known symbolic names]])
{ cmd_show_symbols($3); } ;
@ -783,8 +839,13 @@ CF_CLI(RELOAD OUT, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protoc
{ proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_OUT); } ;
CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging via BIRD logs]])
CF_CLI(DEBUG, proto_patt debug_mask, (<protocol> | \"<pattern>\" | all) (all | off | { states|routes|filters|interfaces|events|packets [, ...] }), [[Control protocol debugging via BIRD logs]])
{ proto_apply_cmd($2, proto_cmd_debug, 1, $3); } ;
CF_CLI(DEBUG, debug_args, (<protocol> | <channel> | \"<pattern>\" | all) (all | off | { states|routes|filters|interfaces|events|packets [, ...] }), [[Control protocol debugging via BIRD logs]])
{ /* Done in debug_args */ };
debug_args:
proto_patt debug_mask { proto_apply_cmd($1, proto_cmd_debug, 1, $2); }
| channel_arg debug_mask { channel_cmd_debug($1, $2); }
;
CF_CLI_HELP(MRTDUMP, ..., [[Control protocol debugging via MRTdump files]])
CF_CLI(MRTDUMP, proto_patt mrtdump_mask, (<protocol> | \"<pattern>\" | all) (all | off | { states|messages [, ...] }), [[Control protocol debugging via MRTdump format]])

View File

@ -172,12 +172,12 @@ static inline void
ifa_notify_change(unsigned c, struct ifa *a)
{
if (c & IF_CHANGE_DOWN)
neigh_ifa_update(a);
neigh_ifa_down(a);
ifa_notify_change_(c, a);
if (c & IF_CHANGE_UP)
neigh_ifa_update(a);
neigh_ifa_up(a);
}
static inline void
@ -444,7 +444,7 @@ if_find_by_index(unsigned idx)
* if no such structure exists.
*/
struct iface *
if_find_by_name(char *name)
if_find_by_name(const char *name)
{
struct iface *i;
@ -455,7 +455,7 @@ if_find_by_name(char *name)
}
struct iface *
if_get_by_name(char *name)
if_get_by_name(const char *name)
{
struct iface *i;
@ -725,7 +725,7 @@ iface_patt_match(struct iface_patt *ifp, struct iface *i, struct ifa *a)
WALK_LIST(p, ifp->ipn_list)
{
char *t = p->pattern;
const char *t = p->pattern;
int pos = p->positive;
if (t)

View File

@ -115,15 +115,15 @@ void if_end_update(void);
void if_flush_ifaces(struct proto *p);
void if_feed_baby(struct proto *);
struct iface *if_find_by_index(unsigned);
struct iface *if_find_by_name(char *);
struct iface *if_get_by_name(char *);
struct iface *if_find_by_name(const char *);
struct iface *if_get_by_name(const char *);
void if_recalc_all_preferred_addresses(void);
/* The Neighbor Cache */
typedef struct neighbor {
node n; /* Node in global neighbor list */
node n; /* Node in neighbor hash table chain */
node if_n; /* Node in per-interface neighbor list */
ip_addr addr; /* Address of the neighbor */
struct ifa *ifa; /* Ifa on related iface */
@ -150,7 +150,8 @@ void neigh_prune(void);
void neigh_if_up(struct iface *);
void neigh_if_down(struct iface *);
void neigh_if_link(struct iface *);
void neigh_ifa_update(struct ifa *);
void neigh_ifa_up(struct ifa *a);
void neigh_ifa_down(struct ifa *a);
void neigh_init(struct pool *);
/*
@ -160,7 +161,7 @@ void neigh_init(struct pool *);
struct iface_patt_node {
node n;
int positive;
byte *pattern;
const byte *pattern;
net_addr prefix;
};

View File

@ -66,10 +66,32 @@ neigh_hash(struct proto *p, ip_addr a, struct iface *i)
return (p->hash_key ^ ipa_hash(a) ^ ptr_hash(i)) >> NEIGH_HASH_OFFSET;
}
static inline int
ifa_better(struct ifa *a, struct ifa *b)
{
return a && (!b || (a->prefix.pxlen > b->prefix.pxlen));
}
static inline int
scope_better(int sa, int sb)
{
/* Order per preference: -1 unknown, 0 for remote, 1 for local */
sa = (sa < 0) ? sa : !sa;
sb = (sb < 0) ? sb : !sb;
return sa > sb;
}
static inline int
scope_remote(int sa, int sb)
{
return (sa > SCOPE_HOST) && (sb > SCOPE_HOST);
}
static int
if_connected(ip_addr a, struct iface *i, struct ifa **ap, uint flags)
{
struct ifa *b;
struct ifa *b, *addr = NULL;
/* Handle iface pseudo-neighbors */
if (flags & NEF_IFACE)
@ -89,12 +111,12 @@ if_connected(ip_addr a, struct iface *i, struct ifa **ap, uint flags)
{
if (b->flags & IA_PEER)
{
if (ipa_equal(a, b->opposite))
return *ap = b, b->scope;
if (ipa_equal(a, b->opposite) && ifa_better(b, addr))
addr = b;
}
else
{
if (ipa_in_netX(a, &b->prefix))
if (ipa_in_netX(a, &b->prefix) && ifa_better(b, addr))
{
/* Do not allow IPv4 network and broadcast addresses */
if (ipa_is_ip4(a) &&
@ -103,11 +125,15 @@ if_connected(ip_addr a, struct iface *i, struct ifa **ap, uint flags)
ipa_equal(a, b->brd))) /* Broadcast */
return *ap = NULL, -1;
return *ap = b, b->scope;
addr = b;
}
}
}
/* Return found address */
if (addr)
return *ap = addr, addr->scope;
/* Handle ONLINK flag */
if (flags & NEF_ONLINK)
return *ap = NULL, ipa_classify(a) & IADDR_SCOPE_MASK;
@ -125,10 +151,10 @@ if_connected_any(ip_addr a, struct iface *vrf, uint vrf_set, struct iface **ifac
*iface = NULL;
*addr = NULL;
/* Get first match, but prefer SCOPE_HOST to other matches */
/* Prefer SCOPE_HOST or longer prefix */
WALK_LIST(i, iface_list)
if ((!vrf_set || vrf == i->master) && ((s = if_connected(a, i, &b, flags)) >= 0))
if ((scope < 0) || ((scope > SCOPE_HOST) && (s == SCOPE_HOST)))
if (scope_better(s, scope) || (scope_remote(s, scope) && ifa_better(b, *addr)))
{
*iface = i;
*addr = b;
@ -138,6 +164,33 @@ if_connected_any(ip_addr a, struct iface *vrf, uint vrf_set, struct iface **ifac
return scope;
}
/* Is ifa @a subnet of any ifa on iface @ib ? */
static inline int
ifa_intersect(struct ifa *a, struct iface *ib)
{
struct ifa *b;
WALK_LIST(b, ib->addrs)
if (net_in_netX(&a->prefix, &b->prefix))
return 1;
return 0;
}
/* Is any ifa of iface @ia subnet of any ifa on iface @ib ? */
static inline int
if_intersect(struct iface *ia, struct iface *ib)
{
struct ifa *a, *b;
WALK_LIST(a, ia->addrs)
WALK_LIST(b, ib->addrs)
if (net_in_netX(&a->prefix, &b->prefix))
return 1;
return 0;
}
/**
* neigh_find - find or create a neighbor entry
* @p: protocol which asks for the entry
@ -200,9 +253,7 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
if ((scope < 0) && !(flags & NEF_STICKY))
return NULL;
n = sl_alloc(neigh_slab);
memset(n, 0, sizeof(neighbor));
n = sl_allocz(neigh_slab);
add_tail(&neigh_hash_table[h], &n->n);
add_tail((scope >= 0) ? &iface->neighbors : &sticky_neigh_list, &n->if_n);
n->addr = a;
@ -323,9 +374,20 @@ neigh_update(neighbor *n, struct iface *iface)
scope = if_connected(n->addr, iface, &ifa, n->flags);
/* When neighbor is going down, try to respawn it on other ifaces */
if ((scope < 0) && (n->scope >= 0) && !n->ifreq && (n->flags & NEF_STICKY))
scope = if_connected_any(n->addr, p->vrf, p->vrf_set, &iface, &ifa, n->flags);
/* Update about already assigned iface, or some other iface */
if (iface == n->iface)
{
/* When neighbor is going down, try to respawn it on other ifaces */
if ((scope < 0) && (n->scope >= 0) && !n->ifreq && (n->flags & NEF_STICKY))
scope = if_connected_any(n->addr, p->vrf, p->vrf_set, &iface, &ifa, n->flags);
}
else
{
/* Continue only if the new variant is better than the existing one */
if (! (scope_better(scope, n->scope) ||
(scope_remote(scope, n->scope) && ifa_better(ifa, n->ifa))))
return;
}
/* No change or minor change - ignore or notify */
if ((scope == n->scope) && (iface == n->iface))
@ -367,9 +429,16 @@ neigh_update(neighbor *n, struct iface *iface)
void
neigh_if_up(struct iface *i)
{
struct iface *ii;
neighbor *n;
node *x, *y;
/* Update neighbors that might be better off with the new iface */
WALK_LIST(ii, iface_list)
if (!EMPTY_LIST(ii->neighbors) && (ii != i) && if_intersect(i, ii))
WALK_LIST2_DELSAFE(n, x, y, ii->neighbors, if_n)
neigh_update(n, i);
WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n)
neigh_update(n, i);
}
@ -420,7 +489,25 @@ neigh_if_link(struct iface *i)
* and causes all unreachable neighbors to be flushed.
*/
void
neigh_ifa_update(struct ifa *a)
neigh_ifa_up(struct ifa *a)
{
struct iface *i = a->iface, *ii;
neighbor *n;
node *x, *y;
/* Update neighbors that might be better off with the new ifa */
WALK_LIST(ii, iface_list)
if (!EMPTY_LIST(ii->neighbors) && ifa_intersect(a, ii))
WALK_LIST2_DELSAFE(n, x, y, ii->neighbors, if_n)
neigh_update(n, i);
/* Wake up all sticky neighbors that are reachable now */
WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n)
neigh_update(n, i);
}
void
neigh_ifa_down(struct ifa *a)
{
struct iface *i = a->iface;
neighbor *n;
@ -428,11 +515,8 @@ neigh_ifa_update(struct ifa *a)
/* Update all neighbors whose scope has changed */
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
neigh_update(n, i);
/* Wake up all sticky neighbors that are reachable now */
WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n)
neigh_update(n, i);
if (n->ifa == a)
neigh_update(n, i);
}
static inline void

View File

@ -12,7 +12,7 @@
struct password_item {
node n;
char *password; /* Key data, null terminated */
const char *password; /* Key data, null terminated */
uint length; /* Key length, without null */
uint id; /* Key ID */
uint alg; /* MAC algorithm */

View File

@ -20,6 +20,7 @@
#include "nest/iface.h"
#include "nest/cli.h"
#include "filter/filter.h"
#include "filter/f-inst.h"
pool *proto_pool;
list proto_list;
@ -27,7 +28,8 @@ list proto_list;
static list protocol_list;
struct protocol *class_to_protocol[PROTOCOL__MAX];
#define PD(pr, msg, args...) do { if (pr->debug & D_STATES) { log(L_TRACE "%s: " msg, pr->name , ## args); } } while(0)
#define CD(c, msg, args...) ({ if (c->debug & D_STATES) log(L_TRACE "%s.%s: " msg, c->proto->name, c->name ?: "?", ## args); })
#define PD(p, msg, args...) ({ if (p->debug & D_STATES) log(L_TRACE "%s: " msg, p->name, ## args); })
static timer *proto_shutdown_timer;
static timer *gr_wait_timer;
@ -42,9 +44,11 @@ static u32 graceful_restart_locks;
static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
static char *c_states[] = { "DOWN", "START", "UP", "FLUSHING" };
static char *e_states[] = { "DOWN", "FEEDING", "READY" };
extern struct protocol proto_unix_iface;
static void channel_request_reload(struct channel *c);
static void proto_shutdown_loop(timer *);
static void proto_rethink_goal(struct proto *p);
static char *proto_state_name(struct proto *p);
@ -58,6 +62,18 @@ static inline int proto_is_done(struct proto *p)
static inline int channel_is_active(struct channel *c)
{ return (c->channel_state == CS_START) || (c->channel_state == CS_UP); }
static inline int channel_reloadable(struct channel *c)
{ return c->proto->reload_routes && c->reloadable; }
static inline void
channel_log_state_change(struct channel *c)
{
if (c->export_state)
CD(c, "State changed to %s/%s", c_states[c->channel_state], e_states[c->export_state]);
else
CD(c, "State changed to %s", c_states[c->channel_state]);
}
static void
proto_log_state_change(struct proto *p)
{
@ -159,30 +175,33 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
c->net_type = cf->net_type;
c->ra_mode = cf->ra_mode;
c->preference = cf->preference;
c->debug = cf->debug;
c->merge_limit = cf->merge_limit;
c->in_keep_filtered = cf->in_keep_filtered;
c->rpki_reload = cf->rpki_reload;
c->channel_state = CS_DOWN;
c->export_state = ES_DOWN;
c->last_state_change = current_time();
c->last_tx_filter_change = current_time();
c->reloadable = 1;
init_list(&c->roa_subscriptions);
CALL(c->channel->init, c, cf);
add_tail(&p->channels, &c->n);
PD(p, "Channel %s connected to table %s", c->name, c->table->name);
CD(c, "Connected to table %s", c->table->name);
return c;
}
void
proto_remove_channel(struct proto *p, struct channel *c)
proto_remove_channel(struct proto *p UNUSED, struct channel *c)
{
ASSERT(c->channel_state == CS_DOWN);
PD(p, "Channel %s removed", c->name);
CD(c, "Removed", c->name);
rem_node(&c->n);
mb_free(c);
@ -233,7 +252,7 @@ channel_schedule_feed(struct channel *c, int initial)
c->export_state = ES_FEEDING;
c->refeeding = !initial;
ev_schedule(c->feed_event);
ev_schedule_work(c->feed_event);
}
static void
@ -244,26 +263,187 @@ channel_feed_loop(void *ptr)
if (c->export_state != ES_FEEDING)
return;
/* Start feeding */
if (!c->feed_active)
{
if (c->proto->feed_begin)
c->proto->feed_begin(c, !c->refeeding);
c->refeed_pending = 0;
}
// DBG("Feeding protocol %s continued\n", p->name);
if (!rt_feed_channel(c))
{
ev_schedule(c->feed_event);
ev_schedule_work(c->feed_event);
return;
}
/* Reset export limit if the feed ended with acceptable number of exported routes */
struct channel_limit *l = &c->out_limit;
if (c->refeeding &&
(l->state == PLS_BLOCKED) &&
(c->refeed_count <= l->limit) &&
(c->stats.exp_routes <= l->limit))
{
log(L_INFO "Protocol %s resets route export limit (%u)", c->proto->name, l->limit);
channel_reset_limit(&c->out_limit);
/* Continue in feed - it will process routing table again from beginning */
c->refeed_count = 0;
ev_schedule_work(c->feed_event);
return;
}
// DBG("Feeding protocol %s finished\n", p->name);
c->export_state = ES_READY;
// proto_log_state_change(p);
channel_log_state_change(c);
if (c->proto->feed_end)
c->proto->feed_end(c);
/* Restart feeding */
if (c->refeed_pending)
channel_request_feeding(c);
}
static void
channel_roa_in_changed(struct rt_subscription *s)
{
struct channel *c = s->data;
int active = c->reload_event && ev_active(c->reload_event);
CD(c, "Reload triggered by RPKI change%s", active ? " - already active" : "");
if (!active)
channel_request_reload(c);
else
c->reload_pending = 1;
}
static void
channel_roa_out_changed(struct rt_subscription *s)
{
struct channel *c = s->data;
int active = (c->export_state == ES_FEEDING);
CD(c, "Feeding triggered by RPKI change%s", active ? " - already active" : "");
if (!active)
channel_request_feeding(c);
else
c->refeed_pending = 1;
}
/* Temporary code, subscriptions should be changed to resources */
struct roa_subscription {
struct rt_subscription s;
node roa_node;
};
static int
channel_roa_is_subscribed(struct channel *c, rtable *tab, int dir)
{
void (*hook)(struct rt_subscription *) =
dir ? channel_roa_in_changed : channel_roa_out_changed;
struct roa_subscription *s;
node *n;
WALK_LIST2(s, n, c->roa_subscriptions, roa_node)
if ((s->s.tab == tab) && (s->s.hook == hook))
return 1;
return 0;
}
static void
channel_roa_subscribe(struct channel *c, rtable *tab, int dir)
{
if (channel_roa_is_subscribed(c, tab, dir))
return;
struct roa_subscription *s = mb_allocz(c->proto->pool, sizeof(struct roa_subscription));
s->s.hook = dir ? channel_roa_in_changed : channel_roa_out_changed;
s->s.data = c;
rt_subscribe(tab, &s->s);
add_tail(&c->roa_subscriptions, &s->roa_node);
}
static void
channel_roa_unsubscribe(struct roa_subscription *s)
{
rt_unsubscribe(&s->s);
rem_node(&s->roa_node);
mb_free(s);
}
static void
channel_roa_subscribe_filter(struct channel *c, int dir)
{
const struct filter *f = dir ? c->in_filter : c->out_filter;
struct rtable *tab;
int valid = 1, found = 0;
if ((f == FILTER_ACCEPT) || (f == FILTER_REJECT))
return;
/* No automatic reload for non-reloadable channels */
if (dir && !channel_reloadable(c))
valid = 0;
#ifdef CONFIG_BGP
/* No automatic reload for BGP channels without in_table / out_table */
if (c->channel == &channel_bgp)
valid = dir ? !!c->in_table : !!c->out_table;
#endif
struct filter_iterator fit;
FILTER_ITERATE_INIT(&fit, f, c->proto->pool);
FILTER_ITERATE(&fit, fi)
{
switch (fi->fi_code)
{
case FI_ROA_CHECK_IMPLICIT:
tab = fi->i_FI_ROA_CHECK_IMPLICIT.rtc->table;
if (valid) channel_roa_subscribe(c, tab, dir);
found = 1;
break;
case FI_ROA_CHECK_EXPLICIT:
tab = fi->i_FI_ROA_CHECK_EXPLICIT.rtc->table;
if (valid) channel_roa_subscribe(c, tab, dir);
found = 1;
break;
default:
break;
}
}
FILTER_ITERATE_END;
FILTER_ITERATE_CLEANUP(&fit);
if (!valid && found)
log(L_WARN "%s.%s: Automatic RPKI reload not active for %s",
c->proto->name, c->name ?: "?", dir ? "import" : "export");
}
static void
channel_roa_unsubscribe_all(struct channel *c)
{
struct roa_subscription *s;
node *n, *x;
WALK_LIST2_DELSAFE(s, n, x, c->roa_subscriptions, roa_node)
channel_roa_unsubscribe(s);
}
static void
channel_start_export(struct channel *c)
{
@ -282,6 +462,7 @@ channel_stop_export(struct channel *c)
c->export_state = ES_DOWN;
c->stats.exp_routes = 0;
bmap_reset(&c->export_map, 1024);
}
@ -292,7 +473,7 @@ channel_schedule_reload(struct channel *c)
ASSERT(c->channel_state == CS_UP);
rt_reload_channel_abort(c);
ev_schedule(c->reload_event);
ev_schedule_work(c->reload_event);
}
static void
@ -300,11 +481,19 @@ channel_reload_loop(void *ptr)
{
struct channel *c = ptr;
/* Start reload */
if (!c->reload_active)
c->reload_pending = 0;
if (!rt_reload_channel(c))
{
ev_schedule(c->reload_event);
ev_schedule_work(c->reload_event);
return;
}
/* Restart reload */
if (c->reload_pending)
channel_request_reload(c);
}
static void
@ -360,6 +549,9 @@ channel_do_start(struct channel *c)
c->feed_event = ev_new_init(c->proto->pool, channel_feed_loop, c);
bmap_init(&c->export_map, c->proto->pool, 1024);
memset(&c->stats, 0, sizeof(struct proto_stats));
channel_reset_limit(&c->rx_limit);
channel_reset_limit(&c->in_limit);
channel_reset_limit(&c->out_limit);
@ -367,6 +559,17 @@ channel_do_start(struct channel *c)
CALL(c->channel->start, c);
}
static void
channel_do_up(struct channel *c)
{
/* Register RPKI/ROA subscriptions */
if (c->rpki_reload)
{
channel_roa_subscribe_filter(c, 1);
channel_roa_subscribe_filter(c, 0);
}
}
static void
channel_do_flush(struct channel *c)
{
@ -377,6 +580,14 @@ channel_do_flush(struct channel *c)
channel_graceful_restart_unlock(c);
CALL(c->channel->shutdown, c);
/* This have to be done in here, as channel pool is freed before channel_do_down() */
bmap_free(&c->export_map);
c->in_table = NULL;
c->reload_event = NULL;
c->out_table = NULL;
channel_roa_unsubscribe_all(c);
}
static void
@ -391,6 +602,7 @@ channel_do_down(struct channel *c)
if ((c->stats.imp_routes + c->stats.filt_routes) != 0)
log(L_ERR "%s: Channel %s is down but still has some routes", c->proto->name, c->name);
// bmap_free(&c->export_map);
memset(&c->stats, 0, sizeof(struct proto_stats));
c->in_table = NULL;
@ -445,6 +657,7 @@ channel_set_state(struct channel *c, uint state)
if (!c->gr_wait && c->proto->rt_notify)
channel_start_export(c);
channel_do_up(c);
break;
case CS_FLUSHING:
@ -471,7 +684,8 @@ channel_set_state(struct channel *c, uint state)
default:
ASSERT(0);
}
// XXXX proto_log_state_change(c);
channel_log_state_change(c);
}
/**
@ -489,6 +703,8 @@ channel_request_feeding(struct channel *c)
{
ASSERT(c->channel_state == CS_UP);
CD(c, "Feeding requested");
/* Do nothing if we are still waiting for feeding */
if (c->export_state == ES_DOWN)
return;
@ -503,19 +719,11 @@ channel_request_feeding(struct channel *c)
rt_feed_channel_abort(c);
}
channel_reset_limit(&c->out_limit);
/* Hack: reset exp_routes during refeed, and do not decrease it later */
c->stats.exp_routes = 0;
/* Track number of exported routes during refeed */
c->refeed_count = 0;
channel_schedule_feed(c, 0); /* Sets ES_FEEDING */
// proto_log_state_change(c);
}
static inline int
channel_reloadable(struct channel *c)
{
return c->proto->reload_routes && c->reloadable;
channel_log_state_change(c);
}
static void
@ -524,6 +732,8 @@ channel_request_reload(struct channel *c)
ASSERT(c->channel_state == CS_UP);
ASSERT(channel_reloadable(c));
CD(c, "Reload requested");
c->proto->reload_routes(c);
/*
@ -569,6 +779,8 @@ channel_config_new(const struct channel_class *cc, const char *name, uint net_ty
cf->net_type = net_type;
cf->ra_mode = RA_OPTIMAL;
cf->preference = proto->protocol->preference;
cf->debug = new_config->channel_default_debug;
cf->rpki_reload = 1;
add_tail(&proto->channels, &cf->n);
@ -601,6 +813,7 @@ channel_copy_config(struct channel_config *src, struct proto_config *proto)
struct channel_config *dst = cfg_alloc(src->channel->config_size);
memcpy(dst, src, src->channel->config_size);
memset(&dst->n, 0, sizeof(node));
add_tail(&proto->channels, &dst->n);
CALL(src->channel->copy_config, dst, src);
@ -620,6 +833,7 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
/* Note that filter_same() requires arguments in (new, old) order */
int import_changed = !filter_same(cf->in_filter, c->in_filter);
int export_changed = !filter_same(cf->out_filter, c->out_filter);
int rpki_reload_changed = (cf->rpki_reload != c->rpki_reload);
if (c->preference != cf->preference)
import_changed = 1;
@ -637,20 +851,31 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
// c->ra_mode = cf->ra_mode;
c->merge_limit = cf->merge_limit;
c->preference = cf->preference;
c->debug = cf->debug;
c->in_keep_filtered = cf->in_keep_filtered;
c->rpki_reload = cf->rpki_reload;
channel_verify_limits(c);
if (export_changed)
c->last_tx_filter_change = current_time();
/* Execute channel-specific reconfigure hook */
if (c->channel->reconfigure && !c->channel->reconfigure(c, cf, &import_changed, &export_changed))
return 0;
/* If the channel is not open, it has no routes and we cannot reload it anyways */
if (c->channel_state != CS_UP)
return 1;
goto done;
/* Update RPKI/ROA subscriptions */
if (import_changed || export_changed || rpki_reload_changed)
{
channel_roa_unsubscribe_all(c);
if (c->rpki_reload)
{
channel_roa_subscribe_filter(c, 1);
channel_roa_subscribe_filter(c, 0);
}
}
if (reconfigure_type == RECONFIG_SOFT)
{
@ -660,7 +885,7 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
if (export_changed)
log(L_INFO "Channel %s.%s changed export", c->proto->name, c->name);
return 1;
goto done;
}
/* Route reload may be not supported */
@ -676,6 +901,8 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
if (export_changed)
channel_request_feeding(c);
done:
CD(c, "Reconfigured");
return 1;
}
@ -868,7 +1095,7 @@ proto_copy_config(struct proto_config *dest, struct proto_config *src)
struct channel_config *cc;
node old_node;
int old_class;
char *old_name;
const char *old_name;
if (dest->protocol != src->protocol)
cf_error("Can't copy configuration from a different protocol type");
@ -1836,6 +2063,16 @@ channel_show_info(struct channel *c)
channel_show_stats(c);
}
void
channel_cmd_debug(struct channel *c, uint mask)
{
if (cli_access_restricted())
return;
c->debug = mask;
cli_msg(0, "");
}
void
proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt)
{
@ -1975,10 +2212,17 @@ proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED)
cli_msg(-15, "%s: reloading", p->name);
}
extern void pipe_update_debug(struct proto *P);
void
proto_cmd_debug(struct proto *p, uintptr_t mask, int cnt UNUSED)
{
p->debug = mask;
#ifdef CONFIG_PIPE
if (p->proto == &proto_pipe)
pipe_update_debug(p);
#endif
}
void
@ -1988,7 +2232,7 @@ proto_cmd_mrtdump(struct proto *p, uintptr_t mask, int cnt UNUSED)
}
static void
proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
proto_apply_cmd_symbol(const struct symbol *s, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
{
if (s->class != SYM_PROTO)
{
@ -2001,7 +2245,7 @@ proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uintptr_t,
}
static void
proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
proto_apply_cmd_patt(const char *patt, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
{
struct proto *p;
int cnt = 0;
@ -2059,3 +2303,47 @@ proto_get_named(struct symbol *sym, struct protocol *pr)
return p;
}
struct proto *
proto_iterate_named(struct symbol *sym, struct protocol *proto, struct proto *old)
{
if (sym)
{
/* Just the first pass */
if (old)
{
cli_msg(0, "");
return NULL;
}
if (sym->class != SYM_PROTO)
cf_error("%s: Not a protocol", sym->name);
struct proto *p = sym->proto->proto;
if (!p || (p->proto != proto))
cf_error("%s: Not a %s protocol", sym->name, proto->name);
return p;
}
else
{
for (struct proto *p = !old ? HEAD(proto_list) : NODE_NEXT(old);
NODE_VALID(p);
p = NODE_NEXT(p))
{
if ((p->proto == proto) && (p->proto_state != PS_DOWN))
{
cli_separator(this_cli);
return p;
}
}
/* Not found anything during first pass */
if (!old)
cf_error("There is no %s protocol running", proto->name);
/* No more items */
cli_msg(0, "");
return NULL;
}
}

View File

@ -80,7 +80,7 @@ struct protocol {
void (*cleanup)(struct proto *); /* Called after shutdown when protocol became hungry/down */
void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */
void (*get_route_info)(struct rte *, byte *buf); /* Get route information (for `show route' command) */
int (*get_attr)(struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */
int (*get_attr)(const struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */
void (*show_proto_info)(struct proto *); /* Show protocol info (for `show protocols all' command) */
void (*copy_config)(struct proto_config *, struct proto_config *); /* Copy config from given protocol instance */
};
@ -115,8 +115,8 @@ struct proto_config {
struct protocol *protocol; /* Protocol */
struct proto *proto; /* Instance we've created */
struct proto_config *parent; /* Parent proto_config for dynamic protocols */
char *name;
char *dsc;
const char *name;
const char *dsc;
int class; /* SYM_PROTO or SYM_TEMPLATE */
u8 net_type; /* Protocol network type (NET_*), 0 for undefined */
u8 disabled; /* Protocol enabled/disabled by default */
@ -171,7 +171,7 @@ struct proto {
struct rte_src *main_source; /* Primary route source */
struct iface *vrf; /* Related VRF instance, NULL if global */
char *name; /* Name of this instance (== cf->name) */
const char *name; /* Name of this instance (== cf->name) */
u32 debug; /* Debugging flags */
u32 mrtdump; /* MRTDump flags */
uint active_channels; /* Number of active channels */
@ -245,7 +245,7 @@ struct proto {
};
struct proto_spec {
void *ptr;
const void *ptr;
int patt;
};
@ -281,6 +281,7 @@ void channel_graceful_restart_unlock(struct channel *c);
void channel_show_limit(struct channel_limit *l, const char *dsc);
void channel_show_info(struct channel *c);
void channel_cmd_debug(struct channel *c, uint mask);
void proto_cmd_show(struct proto *, uintptr_t, int);
void proto_cmd_disable(struct proto *, uintptr_t, int);
@ -292,6 +293,10 @@ void proto_cmd_mrtdump(struct proto *, uintptr_t, int);
void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int), int restricted, uintptr_t arg);
struct proto *proto_get_named(struct symbol *, struct protocol *);
struct proto *proto_iterate_named(struct symbol *sym, struct protocol *proto, struct proto *old);
#define PROTO_WALK_CMD(sym,pr,p) for(struct proto *p = NULL; p = proto_iterate_named(sym, pr, p); )
#define CMD_RELOAD 0
#define CMD_RELOAD_IN 1
@ -492,8 +497,10 @@ struct channel_config {
u8 net_type; /* Routing table network type (NET_*), 0 for undefined */
u8 ra_mode; /* Mode of received route advertisements (RA_*) */
u16 preference; /* Default route preference */
u32 debug; /* Debugging flags (D_*) */
u8 merge_limit; /* Maximal number of nexthops for RA_MERGED */
u8 in_keep_filtered; /* Routes rejected in import filter are kept */
u8 rpki_reload; /* RPKI changes trigger channel reload */
};
struct channel {
@ -507,6 +514,7 @@ struct channel {
struct rtable *table;
const struct filter *in_filter; /* Input filter */
const struct filter *out_filter; /* Output filter */
struct bmap export_map; /* Keeps track which routes passed export filter */
struct channel_limit rx_limit; /* Receive limit (for in_keep_filtered) */
struct channel_limit in_limit; /* Input limit */
struct channel_limit out_limit; /* Output limit */
@ -514,10 +522,12 @@ struct channel {
struct event *feed_event; /* Event responsible for feeding */
struct fib_iterator feed_fit; /* Routing table iterator used during feeding */
struct proto_stats stats; /* Per-channel protocol statistics */
u32 refeed_count; /* Number of routes exported during refeed regardless of out_limit */
u8 net_type; /* Routing table network type (NET_*), 0 for undefined */
u8 ra_mode; /* Mode of received route advertisements (RA_*) */
u16 preference; /* Default route preference */
u32 debug; /* Debugging flags (D_*) */
u8 merge_limit; /* Maximal number of nexthops for RA_MERGED */
u8 in_keep_filtered; /* Routes rejected in import filter are kept */
u8 disabled;
@ -533,7 +543,6 @@ struct channel {
u8 gr_wait; /* Route export to channel is postponed until graceful restart */
btime last_state_change; /* Time of last state transition */
btime last_tx_filter_change;
struct rtable *in_table; /* Internal table for received routes */
struct event *reload_event; /* Event responsible for reloading from in_table */
@ -541,7 +550,13 @@ struct channel {
struct rte *reload_next_rte; /* Route iterator in in_table used during reloading */
u8 reload_active; /* Iterator reload_fit is linked */
u8 reload_pending; /* Reloading and another reload is scheduled */
u8 refeed_pending; /* Refeeding and another refeed is scheduled */
u8 rpki_reload; /* RPKI changes trigger channel reload */
struct rtable *out_table; /* Internal table for exported routes */
list roa_subscriptions; /* List of active ROA table subscriptions based on filters roa_check() */
};

View File

@ -10,6 +10,7 @@
#define _BIRD_ROUTE_H_
#include "lib/lists.h"
#include "lib/bitmap.h"
#include "lib/resource.h"
#include "lib/net.h"
@ -18,6 +19,7 @@ struct protocol;
struct proto;
struct rte_src;
struct symbol;
struct timer;
struct filter;
struct cli;
@ -36,7 +38,6 @@ struct cli;
struct fib_node {
struct fib_node *next; /* Next in hash chain */
struct fib_iterator *readers; /* List of readers of this node */
byte flags; /* User-defined, will be removed */
net_addr addr[0];
};
@ -84,6 +85,8 @@ void fit_init(struct fib_iterator *, struct fib *); /* Internal functions, don't
struct fib_node *fit_get(struct fib *, struct fib_iterator *);
void fit_put(struct fib_iterator *, struct fib_node *);
void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos);
void fit_put_end(struct fib_iterator *i);
void fit_copy(struct fib *f, struct fib_iterator *dst, struct fib_iterator *src);
#define FIB_WALK(fib, type, z) do { \
@ -118,8 +121,12 @@ void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uin
#define FIB_ITERATE_PUT_NEXT(it, fib) fit_put_next(fib, it, fn_, hpos_)
#define FIB_ITERATE_PUT_END(it) fit_put_end(it)
#define FIB_ITERATE_UNLINK(it, fib) fit_get(fib, it)
#define FIB_ITERATE_COPY(dst, src, fib) fit_copy(fib, dst, src)
/*
* Master Routing Tables. Generally speaking, each of them contains a FIB
@ -141,6 +148,8 @@ struct rtable_config {
int gc_max_ops; /* Maximum number of operations before GC is run */
int gc_min_time; /* Minimum time between two consecutive GC runs */
byte sorted; /* Routes of network are sorted according to rte_better() */
btime min_settle_time; /* Minimum settle time for notifications */
btime max_settle_time; /* Maximum settle time for notifications */
};
typedef struct rtable {
@ -152,6 +161,7 @@ typedef struct rtable {
int pipe_busy; /* Pipe loop detection */
int use_count; /* Number of protocols using this table */
u32 rt_count; /* Number of routes in the table */
struct hmap id_map;
struct hostcache *hostcache;
struct rtable_config *config; /* Configuration of this table */
struct config *deleted; /* Table doesn't exist in current configuration,
@ -159,6 +169,8 @@ typedef struct rtable {
* obstacle from this routing table.
*/
struct event *rt_event; /* Routing table event */
btime last_rt_change; /* Last time when route changed */
btime base_settle_time; /* Start time of rtable settling interval */
btime gc_time; /* Time of last GC */
int gc_counter; /* Number of operations since last GC */
byte prune_state; /* Table prune state, 1 -> scheduled, 2-> running */
@ -166,8 +178,18 @@ typedef struct rtable {
byte nhu_state; /* Next Hop Update state */
struct fib_iterator prune_fit; /* Rtable prune FIB iterator */
struct fib_iterator nhu_fit; /* Next Hop Update FIB iterator */
list subscribers; /* Subscribers for notifications */
struct timer *settle_timer; /* Settle time for notifications */
} rtable;
struct rt_subscription {
node n;
rtable *tab;
void (*hook)(struct rt_subscription *b);
void *data;
};
#define NHU_CLEAN 0
#define NHU_SCHEDULED 1
#define NHU_RUNNING 2
@ -210,6 +232,7 @@ typedef struct rte {
net *net; /* Network this RTE belongs to */
struct channel *sender; /* Channel used to send the route to the routing table */
struct rta *attrs; /* Attributes of this route */
u32 id; /* Table specific route id */
byte flags; /* Flags (REF_...) */
byte pflags; /* Protocol-specific flags */
word pref; /* Route preference */
@ -286,6 +309,8 @@ void rt_preconfig(struct config *);
void rt_commit(struct config *new, struct config *old);
void rt_lock_table(rtable *);
void rt_unlock_table(rtable *);
void rt_subscribe(rtable *tab, struct rt_subscription *s);
void rt_unsubscribe(struct rt_subscription *s);
void rt_setup(pool *, rtable *, struct rtable_config *);
static inline net *net_find(rtable *tab, const net_addr *addr) { return (net *) fib_find(&tab->fib, addr); }
static inline net *net_find_valid(rtable *tab, const net_addr *addr)
@ -345,6 +370,7 @@ struct rt_show_data {
struct proto *export_protocol;
struct channel *export_channel;
struct config *running_on_config;
struct krt_proto *kernel;
int export_mode, primary_only, filtered, stats, show_for;
int table_open; /* Iteration (fit) is open */
@ -369,6 +395,7 @@ struct rt_show_data_rtable * rt_show_add_table(struct rt_show_data *d, rtable *t
#define RSEM_PREEXPORT 1 /* Routes ready for export, before filtering */
#define RSEM_EXPORT 2 /* Routes accepted by export filter */
#define RSEM_NOEXPORT 3 /* Routes rejected by export filter */
#define RSEM_EXPORTED 4 /* Routes marked in export map */
/*
* Route Attributes
@ -448,17 +475,13 @@ typedef struct rta {
#define RTD_PROHIBIT 4 /* Administratively prohibited */
#define RTD_MAX 5
/* Flags for net->n.flags, used by kernel syncer */
#define KRF_INSTALLED 0x80 /* This route should be installed in the kernel */
#define KRF_SYNC_ERROR 0x40 /* Error during kernel table synchronization */
#define RTAF_CACHED 1 /* This is a cached rta */
#define IGP_METRIC_UNKNOWN 0x80000000 /* Default igp_metric used when no other
protocol-specific metric is availabe */
const char * rta_dest_names[RTD_MAX];
extern const char * rta_dest_names[RTD_MAX];
static inline const char *rta_dest_name(uint n)
{ return (n < RTD_MAX) ? rta_dest_names[n] : "???"; }
@ -571,7 +594,7 @@ void ea_merge(ea_list *from, ea_list *to); /* Merge sub-lists to allocated buffe
int ea_same(ea_list *x, ea_list *y); /* Test whether two ea_lists are identical */
uint ea_hash(ea_list *e); /* Calculate 16-bit hash value */
ea_list *ea_append(ea_list *to, ea_list *what);
void ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max);
void ea_format_bitfield(const struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max);
#define ea_normalize(ea) do { \
if (ea->next) { \
@ -652,6 +675,7 @@ void rta_dump(rta *);
void rta_dump_all(void);
void rta_show(struct cli *, rta *);
u32 rt_get_igp_metric(rte *rt);
struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep);
void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls);

View File

@ -135,7 +135,7 @@ rt_get_source(struct proto *p, u32 id)
if (src)
return src;
src = sl_alloc(rte_src_slab);
src = sl_allocz(rte_src_slab);
src->proto = p;
src->private_id = id;
src->global_id = idm_alloc(&src_ids);
@ -202,7 +202,7 @@ nexthop__same(struct nexthop *x, struct nexthop *y)
}
static int
nexthop_compare_node(const struct nexthop *x, const struct nexthop *y)
nexthop_compare_node(const struct nexthop *x, const struct nexthop *y)
{
int r;
@ -278,18 +278,22 @@ nexthop_merge(struct nexthop *x, struct nexthop *y, int rx, int ry, int max, lin
while ((x || y) && max--)
{
int cmp = nexthop_compare_node(x, y);
if (cmp < 0)
{
ASSUME(x);
*n = rx ? x : nexthop_copy_node(x, lp);
x = x->next;
}
else if (cmp > 0)
{
ASSUME(y);
*n = ry ? y : nexthop_copy_node(y, lp);
y = y->next;
}
else
{
ASSUME(x && y);
*n = rx ? x : (ry ? y : nexthop_copy_node(x, lp));
x = x->next;
y = y->next;
@ -362,7 +366,7 @@ nexthop_copy(struct nexthop *o)
for (; o; o = o->next)
{
struct nexthop *n = sl_alloc(nexthop_slab(o));
struct nexthop *n = sl_allocz(nexthop_slab(o));
n->gw = o->gw;
n->iface = o->iface;
n->next = NULL;
@ -786,7 +790,7 @@ ea_free(ea_list *o)
}
static int
get_generic_attr(eattr *a, byte **buf, int buflen UNUSED)
get_generic_attr(const eattr *a, byte **buf, int buflen UNUSED)
{
if (a->id == EA_GEN_IGP_METRIC)
{
@ -798,7 +802,7 @@ get_generic_attr(eattr *a, byte **buf, int buflen UNUSED)
}
void
ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max)
ea_format_bitfield(const struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max)
{
byte *bound = buf + bufsize - 32;
u32 data = a->u.data;
@ -894,7 +898,7 @@ ea_show_lc_set(struct cli *c, const struct adata *ad, byte *pos, byte *buf, byte
* get_attr() hook, it's consulted first.
*/
void
ea_show(struct cli *c, eattr *e)
ea_show(struct cli *c, const eattr *e)
{
struct protocol *p;
int status = GA_UNKNOWN;

View File

@ -327,7 +327,6 @@ fib_get(struct fib *f, const net_addr *a)
struct fib_node *e = fib_user_to_node(f, b);
e->readers = NULL;
e->flags = 0;
fib_insert(f, a, e);
memset(b, 0, f->node_offset);
@ -585,6 +584,40 @@ found:
fit_put(i, n);
}
void
fit_put_end(struct fib_iterator *i)
{
i->prev = i->next = NULL;
i->node = NULL;
i->hash = ~0 - 1;
}
void
fit_copy(struct fib *f, struct fib_iterator *dst, struct fib_iterator *src)
{
struct fib_iterator *nxt = src->next;
fit_get(f, dst);
if (!src->prev)
{
/* We are at the end */
fit_put_end(dst);
return;
}
src->next = dst;
dst->prev = src;
dst->next = nxt;
if (nxt)
nxt->prev = dst;
dst->node = src->node;
dst->hash = src->hash;
}
#ifdef DEBUGGING
/**

View File

@ -15,6 +15,7 @@
#include "nest/cli.h"
#include "nest/iface.h"
#include "filter/filter.h"
#include "sysdep/unix/krt.h"
static void
rt_show_table(struct cli *c, struct rt_show_data *d)
@ -28,14 +29,20 @@ rt_show_table(struct cli *c, struct rt_show_data *d)
d->last_table = d->tab;
}
static inline struct krt_proto *
rt_show_get_kernel(struct rt_show_data *d)
{
struct proto_config *krt = d->tab->table->config->krt_attached;
return krt ? (struct krt_proto *) krt->proto : NULL;
}
static void
rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d)
rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary)
{
byte from[IPA_MAX_TEXT_LENGTH+8];
byte tm[TM_DATETIME_BUFFER_SIZE], info[256];
rta *a = e->attrs;
int primary = (e->net->routes == e);
int sync_error = (e->net->n.flags & KRF_SYNC_ERROR);
int sync_error = d->kernel ? krt_get_sync_error(d->kernel, e) : 0;
void (*get_route_info)(struct rte *, byte *buf);
struct nexthop *nh;
@ -97,6 +104,11 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
rte *e, *ee;
byte ia[NET_MAX_TEXT_LENGTH+1];
struct channel *ec = d->tab->export_channel;
/* The Clang static analyzer complains that ec may be NULL.
* It should be ensured to be not NULL by rt_show_prepare_tables() */
ASSUME(!d->export_mode || ec);
int first = 1;
int pass = 0;
@ -121,9 +133,17 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
if (ec && (ec->export_state == ES_DOWN))
goto skip;
/* Special case for merged export */
if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
if (d->export_mode == RSEM_EXPORTED)
{
if (!bmap_test(&ec->export_map, ee->id))
goto skip;
// if (ec->ra_mode != RA_ANY)
// pass = 1;
}
else if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
{
/* Special case for merged export */
rte *rt_free;
e = rt_export_merged(ec, n, &rt_free, c->show_pool, 1);
pass = 1;
@ -167,7 +187,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
goto skip;
if (d->stats < 2)
rt_show_rte(c, ia, e, d);
rt_show_rte(c, ia, e, d, (e->net->routes == ee));
d->show_counter++;
ia[0] = 0;
@ -223,6 +243,7 @@ rt_show_cont(struct cli *c)
FIB_ITERATE_INIT(&d->fit, &d->tab->table->fib);
d->table_open = 1;
d->table_counter++;
d->kernel = rt_show_get_kernel(d);
d->show_counter_last = d->show_counter;
d->rt_counter_last = d->rt_counter;
@ -253,6 +274,7 @@ rt_show_cont(struct cli *c)
d->net_counter - d->net_counter_last, d->tab->table->name);
}
d->kernel = NULL;
d->table_open = 0;
d->tab = NODE_NEXT(d->tab);
@ -396,6 +418,7 @@ rt_show(struct rt_show_data *d)
WALK_LIST(tab, d->tables)
{
d->tab = tab;
d->kernel = rt_show_get_kernel(d);
if (d->show_for)
n = net_route(tab->table, d->addr);

File diff suppressed because it is too large Load Diff

View File

@ -113,7 +113,7 @@ babel_get_source(struct babel_proto *p, struct babel_entry *e, u64 router_id)
if (s)
return s;
s = sl_alloc(p->source_slab);
s = sl_allocz(p->source_slab);
s->router_id = router_id;
s->expires = current_time() + BABEL_GARBAGE_INTERVAL;
s->seqno = 0;
@ -159,8 +159,7 @@ babel_get_route(struct babel_proto *p, struct babel_entry *e, struct babel_neigh
if (r)
return r;
r = sl_alloc(p->route_slab);
memset(r, 0, sizeof(*r));
r = sl_allocz(p->route_slab);
r->e = e;
r->neigh = nbr;
@ -323,7 +322,7 @@ babel_add_seqno_request(struct babel_proto *p, struct babel_entry *e,
}
/* No entries found */
sr = sl_alloc(p->seqno_slab);
sr = sl_allocz(p->seqno_slab);
found:
sr->router_id = router_id;
@ -640,6 +639,14 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
.nh.iface = r->neigh->ifa->iface,
};
/*
* If we cannot find a reachable neighbour, set the entry to be onlink. This
* makes it possible to, e.g., assign /32 addresses on a mesh interface and
* have routing work.
*/
if (!neigh_find(&p->p, r->next_hop, r->neigh->ifa->iface, 0))
a0.nh.flags = RNF_ONLINK;
rta *a = rta_lookup(&a0);
rte *rte = rte_get_temp(a);
rte->u.babel.seqno = r->seqno;
@ -1852,7 +1859,7 @@ babel_get_route_info(rte *rte, byte *buf)
}
static int
babel_get_attr(eattr *a, byte *buf, int buflen UNUSED)
babel_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
{
switch (a->id)
{
@ -1874,7 +1881,7 @@ babel_get_attr(eattr *a, byte *buf, int buflen UNUSED)
}
void
babel_show_interfaces(struct proto *P, char *iff)
babel_show_interfaces(struct proto *P, const char *iff)
{
struct babel_proto *p = (void *) P;
struct babel_iface *ifa = NULL;
@ -1883,7 +1890,6 @@ babel_show_interfaces(struct proto *P, char *iff)
if (p->p.proto_state != PS_UP)
{
cli_msg(-1023, "%s: is not up", p->p.name);
cli_msg(0, "");
return;
}
@ -1907,12 +1913,10 @@ babel_show_interfaces(struct proto *P, char *iff)
ifa->cf->rxcost, nbrs, MAX(timer, 0),
ifa->next_hop_ip4, ifa->next_hop_ip6);
}
cli_msg(0, "");
}
void
babel_show_neighbors(struct proto *P, char *iff)
babel_show_neighbors(struct proto *P, const char *iff)
{
struct babel_proto *p = (void *) P;
struct babel_iface *ifa = NULL;
@ -1922,7 +1926,6 @@ babel_show_neighbors(struct proto *P, char *iff)
if (p->p.proto_state != PS_UP)
{
cli_msg(-1024, "%s: is not up", p->p.name);
cli_msg(0, "");
return;
}
@ -1947,8 +1950,6 @@ babel_show_neighbors(struct proto *P, char *iff)
n->addr, ifa->iface->name, n->cost, rts, hellos, MAX(timer, 0));
}
}
cli_msg(0, "");
}
static void
@ -1990,7 +1991,6 @@ babel_show_entries(struct proto *P)
if (p->p.proto_state != PS_UP)
{
cli_msg(-1025, "%s: is not up", p->p.name);
cli_msg(0, "");
return;
}
@ -2000,8 +2000,6 @@ babel_show_entries(struct proto *P)
babel_show_entries_(p, &p->ip4_rtable);
babel_show_entries_(p, &p->ip6_rtable);
cli_msg(0, "");
}
static void
@ -2033,7 +2031,6 @@ babel_show_routes(struct proto *P)
if (p->p.proto_state != PS_UP)
{
cli_msg(-1025, "%s: is not up", p->p.name);
cli_msg(0, "");
return;
}
@ -2043,8 +2040,6 @@ babel_show_routes(struct proto *P)
babel_show_routes_(p, &p->ip4_rtable);
babel_show_routes_(p, &p->ip6_rtable);
cli_msg(0, "");
}

View File

@ -368,8 +368,8 @@ void babel_handle_update(union babel_msg *msg, struct babel_iface *ifa);
void babel_handle_route_request(union babel_msg *msg, struct babel_iface *ifa);
void babel_handle_seqno_request(union babel_msg *msg, struct babel_iface *ifa);
void babel_show_interfaces(struct proto *P, char *iff);
void babel_show_neighbors(struct proto *P, char *iff);
void babel_show_interfaces(struct proto *P, const char *iff);
void babel_show_neighbors(struct proto *P, const char *iff);
void babel_show_entries(struct proto *P);
void babel_show_routes(struct proto *P);

View File

@ -130,16 +130,16 @@ dynamic_attr: BABEL_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_BAB
CF_CLI_HELP(SHOW BABEL, ..., [[Show information about Babel protocol]]);
CF_CLI(SHOW BABEL INTERFACES, optproto opttext, [<name>] [\"<interface>\"], [[Show information about Babel interfaces]])
{ babel_show_interfaces(proto_get_named($4, &proto_babel), $5); };
{ PROTO_WALK_CMD($4, &proto_babel, p) babel_show_interfaces(p, $5); };
CF_CLI(SHOW BABEL NEIGHBORS, optproto opttext, [<name>] [\"<interface>\"], [[Show information about Babel neighbors]])
{ babel_show_neighbors(proto_get_named($4, &proto_babel), $5); };
{ PROTO_WALK_CMD($4, &proto_babel, p) babel_show_neighbors(p, $5); };
CF_CLI(SHOW BABEL ENTRIES, optproto opttext, [<name>], [[Show information about Babel prefix entries]])
{ babel_show_entries(proto_get_named($4, &proto_babel)); };
{ PROTO_WALK_CMD($4, &proto_babel, p) babel_show_entries(p); };
CF_CLI(SHOW BABEL ROUTES, optproto opttext, [<name>], [[Show information about Babel route entries]])
{ babel_show_routes(proto_get_named($4, &proto_babel)); };
{ PROTO_WALK_CMD($4, &proto_babel, p) babel_show_routes(p); };
CF_CODE

View File

@ -1144,7 +1144,6 @@ babel_read_tlv(struct babel_tlv *hdr,
return PARSE_ERROR;
state->current_tlv_endpos = tlv_data[hdr->type].min_length;
memset(msg, 0, sizeof(*msg));
int res = tlv_data[hdr->type].read_tlv(hdr, msg, state);
if (res != PARSE_SUCCESS)
@ -1278,7 +1277,7 @@ babel_send_unicast(union babel_msg *msg, struct babel_iface *ifa, ip_addr dest)
struct babel_msg_node *msgn = sl_alloc(p->msg_slab);
list queue;
msgn->msg = *msg;
*msgn = (struct babel_msg_node) { .msg = *msg };
init_list(&queue);
add_tail(&queue, NODE msgn);
babel_write_queue(ifa, &queue);
@ -1305,7 +1304,8 @@ babel_enqueue(union babel_msg *msg, struct babel_iface *ifa)
{
struct babel_proto *p = ifa->proto;
struct babel_msg_node *msgn = sl_alloc(p->msg_slab);
msgn->msg = *msg;
*msgn = (struct babel_msg_node) { .msg = *msg };
add_tail(&ifa->msg_queue, NODE msgn);
babel_kick_queue(ifa);
}
@ -1386,7 +1386,7 @@ babel_process_packet(struct babel_pkt_header *pkt, int len,
break;
}
msg = sl_alloc(p->msg_slab);
msg = sl_allocz(p->msg_slab);
res = babel_read_tlv(tlv, &msg->msg, &state);
if (res == PARSE_SUCCESS)
{

View File

@ -27,13 +27,13 @@
* related to the session and two timers (TX timer for periodic packets and hold
* timer for session timeout). These sessions are allocated from @session_slab
* and are accessible by two hash tables, @session_hash_id (by session ID) and
* @session_hash_ip (by IP addresses of neighbors). Slab and both hashes are in
* the main protocol structure &bfd_proto. The protocol logic related to BFD
* sessions is implemented in internal functions bfd_session_*(), which are
* expected to be called from the context of BFD thread, and external functions
* bfd_add_session(), bfd_remove_session() and bfd_reconfigure_session(), which
* form an interface to the BFD core for the rest and are expected to be called
* from the context of main thread.
* @session_hash_ip (by IP addresses of neighbors and associated interfaces).
* Slab and both hashes are in the main protocol structure &bfd_proto. The
* protocol logic related to BFD sessions is implemented in internal functions
* bfd_session_*(), which are expected to be called from the context of BFD
* thread, and external functions bfd_add_session(), bfd_remove_session() and
* bfd_reconfigure_session(), which form an interface to the BFD core for the
* rest and are expected to be called from the context of main thread.
*
* Each BFD session has an associated BFD interface, represented by structure
* &bfd_iface. A BFD interface contains a socket used for TX (the one for RX is
@ -108,10 +108,10 @@
#define HASH_ID_EQ(a,b) a == b
#define HASH_ID_FN(k) k
#define HASH_IP_KEY(n) n->addr
#define HASH_IP_KEY(n) n->addr, n->ifindex
#define HASH_IP_NEXT(n) n->next_ip
#define HASH_IP_EQ(a,b) ipa_equal(a,b)
#define HASH_IP_FN(k) ipa_hash(k)
#define HASH_IP_EQ(a1,n1,a2,n2) ipa_equal(a1, a2) && n1 == n2
#define HASH_IP_FN(a,n) ipa_hash(a) ^ u32_hash(n)
static list bfd_proto_list;
static list bfd_wait_list;
@ -128,6 +128,18 @@ static inline void bfd_notify_kick(struct bfd_proto *p);
* BFD sessions
*/
static inline struct bfd_session_config
bfd_merge_options(const struct bfd_iface_config *cf, const struct bfd_options *opts)
{
return (struct bfd_session_config) {
.min_rx_int = opts->min_rx_int ?: cf->min_rx_int,
.min_tx_int = opts->min_tx_int ?: cf->min_tx_int,
.idle_tx_int = opts->idle_tx_int ?: cf->idle_tx_int,
.multiplier = opts->multiplier ?: cf->multiplier,
.passive = opts->passive_set ? opts->passive : cf->passive
};
}
static void
bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
{
@ -152,10 +164,10 @@ bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
bfd_unlock_sessions(p);
if (state == BFD_STATE_UP)
bfd_session_set_min_tx(s, s->ifa->cf->min_tx_int);
bfd_session_set_min_tx(s, s->cf.min_tx_int);
if (old_state == BFD_STATE_UP)
bfd_session_set_min_tx(s, s->ifa->cf->idle_tx_int);
bfd_session_set_min_tx(s, s->cf.idle_tx_int);
if (notify)
bfd_notify_kick(p);
@ -373,9 +385,9 @@ bfd_find_session_by_id(struct bfd_proto *p, u32 id)
}
struct bfd_session *
bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr)
bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr, uint ifindex)
{
return HASH_FIND(p->session_hash_ip, HASH_IP, addr);
return HASH_FIND(p->session_hash_ip, HASH_IP, addr, ifindex);
}
static void
@ -405,31 +417,31 @@ bfd_get_free_id(struct bfd_proto *p)
}
static struct bfd_session *
bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *iface)
bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *iface, struct bfd_options *opts)
{
birdloop_enter(p->loop);
struct bfd_iface *ifa = bfd_get_iface(p, local, iface);
struct bfd_session *s = sl_alloc(p->session_slab);
bzero(s, sizeof(struct bfd_session));
struct bfd_session *s = sl_allocz(p->session_slab);
s->addr = addr;
s->ifa = ifa;
s->ifindex = iface ? iface->index : 0;
s->loc_id = bfd_get_free_id(p);
HASH_INSERT(p->session_hash_id, HASH_ID, s);
HASH_INSERT(p->session_hash_ip, HASH_IP, s);
s->cf = bfd_merge_options(ifa->cf, opts);
/* Initialization of state variables - see RFC 5880 6.8.1 */
s->loc_state = BFD_STATE_DOWN;
s->rem_state = BFD_STATE_DOWN;
s->des_min_tx_int = s->des_min_tx_new = ifa->cf->idle_tx_int;
s->req_min_rx_int = s->req_min_rx_new = ifa->cf->min_rx_int;
s->des_min_tx_int = s->des_min_tx_new = s->cf.idle_tx_int;
s->req_min_rx_int = s->req_min_rx_new = s->cf.min_rx_int;
s->rem_min_rx_int = 1;
s->detect_mult = ifa->cf->multiplier;
s->passive = ifa->cf->passive;
s->detect_mult = s->cf.multiplier;
s->passive = s->cf.passive;
s->tx_csn = random_u32();
s->tx_timer = tm_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0);
@ -506,15 +518,19 @@ bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
static void
bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s)
{
if (EMPTY_LIST(s->request_list))
return;
birdloop_enter(p->loop);
struct bfd_iface_config *cf = s->ifa->cf;
struct bfd_request *req = SKIP_BACK(struct bfd_request, n, HEAD(s->request_list));
s->cf = bfd_merge_options(s->ifa->cf, &req->opts);
u32 tx = (s->loc_state == BFD_STATE_UP) ? cf->min_tx_int : cf->idle_tx_int;
u32 tx = (s->loc_state == BFD_STATE_UP) ? s->cf.min_tx_int : s->cf.idle_tx_int;
bfd_session_set_min_tx(s, tx);
bfd_session_set_min_rx(s, cf->min_rx_int);
s->detect_mult = cf->multiplier;
s->passive = cf->passive;
bfd_session_set_min_rx(s, s->cf.min_rx_int);
s->detect_mult = s->cf.multiplier;
s->passive = s->cf.passive;
bfd_session_control_tx_timer(s, 0);
@ -590,12 +606,20 @@ bfd_free_iface(struct bfd_iface *ifa)
static void
bfd_reconfigure_iface(struct bfd_proto *p, struct bfd_iface *ifa, struct bfd_config *nc)
{
struct bfd_iface_config *nic = bfd_find_iface_config(nc, ifa->iface);
ifa->changed = !!memcmp(nic, ifa->cf, sizeof(struct bfd_iface_config));
struct bfd_iface_config *new = bfd_find_iface_config(nc, ifa->iface);
struct bfd_iface_config *old = ifa->cf;
/* Check options that are handled in bfd_reconfigure_session() */
ifa->changed =
(new->min_rx_int != old->min_rx_int) ||
(new->min_tx_int != old->min_tx_int) ||
(new->idle_tx_int != old->idle_tx_int) ||
(new->multiplier != old->multiplier) ||
(new->passive != old->passive);
/* This should be probably changed to not access ifa->cf from the BFD thread */
birdloop_enter(p->loop);
ifa->cf = nic;
ifa->cf = new;
birdloop_leave(p->loop);
}
@ -624,14 +648,23 @@ bfd_request_notify(struct bfd_request *req, u8 state, u8 diag)
static int
bfd_add_request(struct bfd_proto *p, struct bfd_request *req)
{
struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
if (p->p.vrf_set && (p->p.vrf != req->vrf))
return 0;
struct bfd_session *s = bfd_find_session_by_addr(p, req->addr);
if (ipa_is_ip4(req->addr) ? !cf->accept_ipv4 : !cf->accept_ipv6)
return 0;
if (req->iface ? !cf->accept_direct : !cf->accept_multihop)
return 0;
uint ifindex = req->iface ? req->iface->index : 0;
struct bfd_session *s = bfd_find_session_by_addr(p, req->addr, ifindex);
u8 state, diag;
if (!s)
s = bfd_add_session(p, req->addr, req->local, req->iface);
s = bfd_add_session(p, req->addr, req->local, req->iface, &req->opts);
rem_node(&req->n);
add_tail(&s->request_list, &req->n);
@ -690,7 +723,8 @@ static struct resclass bfd_request_class;
struct bfd_request *
bfd_request_session(pool *p, ip_addr addr, ip_addr local,
struct iface *iface, struct iface *vrf,
void (*hook)(struct bfd_request *), void *data)
void (*hook)(struct bfd_request *), void *data,
const struct bfd_options *opts)
{
struct bfd_request *req = ralloc(p, &bfd_request_class);
@ -702,6 +736,9 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local,
req->iface = iface;
req->vrf = vrf;
if (opts)
req->opts = *opts;
bfd_submit_request(req);
req->hook = hook;
@ -710,6 +747,20 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local,
return req;
}
void
bfd_update_request(struct bfd_request *req, const struct bfd_options *opts)
{
struct bfd_session *s = req->session;
if (!memcmp(opts, &req->opts, sizeof(const struct bfd_options)))
return;
req->opts = *opts;
if (s)
bfd_reconfigure_session(s->ifa->bfd, s);
}
static void
bfd_request_free(resource *r)
{
@ -759,7 +810,7 @@ bfd_neigh_notify(struct neighbor *nb)
if ((nb->scope > 0) && !n->req)
{
ip_addr local = ipa_nonzero(n->local) ? n->local : nb->ifa->ip;
n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, p->p.vrf, NULL, NULL);
n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, p->p.vrf, NULL, NULL, NULL);
}
if ((nb->scope <= 0) && n->req)
@ -776,7 +827,7 @@ bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
if (n->multihop)
{
n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, NULL, NULL);
n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, NULL, NULL, NULL);
return;
}
@ -837,10 +888,11 @@ bfd_reconfigure_neighbors(struct bfd_proto *p, struct bfd_config *new)
nn->req = on->req;
nn->active = 1;
return;
goto next;
}
bfd_stop_neighbor(p, on);
next:;
}
WALK_LIST(nn, new->neigh_list)
@ -985,10 +1037,19 @@ bfd_start(struct proto *P)
add_tail(&bfd_proto_list, &p->bfd_node);
birdloop_enter(p->loop);
p->rx4_1 = bfd_open_rx_sk(p, 0, SK_IPV4);
p->rx4_m = bfd_open_rx_sk(p, 1, SK_IPV4);
p->rx6_1 = bfd_open_rx_sk(p, 0, SK_IPV6);
p->rx6_m = bfd_open_rx_sk(p, 1, SK_IPV6);
if (cf->accept_ipv4 && cf->accept_direct)
p->rx4_1 = bfd_open_rx_sk(p, 0, SK_IPV4);
if (cf->accept_ipv4 && cf->accept_multihop)
p->rx4_m = bfd_open_rx_sk(p, 1, SK_IPV4);
if (cf->accept_ipv6 && cf->accept_direct)
p->rx6_1 = bfd_open_rx_sk(p, 0, SK_IPV6);
if (cf->accept_ipv6 && cf->accept_multihop)
p->rx6_m = bfd_open_rx_sk(p, 1, SK_IPV6);
birdloop_leave(p->loop);
bfd_take_requests(p);
@ -1033,10 +1094,17 @@ static int
bfd_reconfigure(struct proto *P, struct proto_config *c)
{
struct bfd_proto *p = (struct bfd_proto *) P;
// struct bfd_config *old = (struct bfd_config *) (P->cf);
struct bfd_config *old = (struct bfd_config *) (P->cf);
struct bfd_config *new = (struct bfd_config *) c;
struct bfd_iface *ifa;
/* TODO: Improve accept reconfiguration */
if ((new->accept_ipv4 != old->accept_ipv4) ||
(new->accept_ipv6 != old->accept_ipv6) ||
(new->accept_direct != old->accept_direct) ||
(new->accept_multihop != old->accept_multihop))
return 0;
birdloop_mask_wakeups(p->loop);
WALK_LIST(ifa, p->iface_list)
@ -1079,7 +1147,6 @@ bfd_show_sessions(struct proto *P)
if (p->p.proto_state != PS_UP)
{
cli_msg(-1020, "%s: is not up", p->p.name);
cli_msg(0, "");
return;
}
@ -1104,8 +1171,6 @@ bfd_show_sessions(struct proto *P)
s->addr, ifname, bfd_state_names[state], tbuf, tx_int, timeout);
}
HASH_WALK_END;
cli_msg(0, "");
}

View File

@ -43,6 +43,10 @@ struct bfd_config
list patt_list; /* List of iface configs (struct bfd_iface_config) */
list neigh_list; /* List of configured neighbors (struct bfd_neighbor) */
struct bfd_iface_config *multihop; /* Multihop pseudoiface config */
u8 accept_ipv4;
u8 accept_ipv6;
u8 accept_direct;
u8 accept_multihop;
};
struct bfd_iface_config
@ -57,6 +61,15 @@ struct bfd_iface_config
list *passwords; /* Passwords for authentication */
};
struct bfd_session_config
{
u32 min_rx_int;
u32 min_tx_int;
u32 idle_tx_int;
u8 multiplier;
u8 passive;
};
struct bfd_neighbor
{
node n;
@ -126,6 +139,9 @@ struct bfd_session
u8 rem_diag;
u32 loc_id; /* Local session ID (local discriminator) */
u32 rem_id; /* Remote session ID (remote discriminator) */
struct bfd_session_config cf; /* Static configuration parameters */
u32 des_min_tx_int; /* Desired min rx interval, local option */
u32 des_min_tx_new; /* Used for des_min_tx_int change */
u32 req_min_rx_int; /* Required min tx interval, local option */
@ -137,6 +153,7 @@ struct bfd_session
u8 detect_mult; /* Announced detect_mult, local option */
u8 rem_detect_mult; /* Last received detect_mult */
uint ifindex; /* Iface index, for hashing in bfd.session_hash_ip */
btime last_tx; /* Time of last sent periodic control packet */
btime last_rx; /* Time of last received valid control packet */
@ -197,7 +214,7 @@ static inline void bfd_unlock_sessions(struct bfd_proto *p) { pthread_spin_unloc
/* bfd.c */
struct bfd_session * bfd_find_session_by_id(struct bfd_proto *p, u32 id);
struct bfd_session * bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr);
struct bfd_session * bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr, uint ifindex);
void bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old_rx_int);
void bfd_show_sessions(struct proto *P);

View File

@ -23,7 +23,7 @@ CF_DECLS
CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE,
INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL, AUTHENTICATION,
NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1)
NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1, IPV4, IPV6, DIRECT)
%type <iface> bfd_neigh_iface
%type <a> bfd_neigh_local
@ -38,10 +38,13 @@ bfd_proto_start: proto_start BFD
this_proto = proto_config_new(&proto_bfd, $1);
init_list(&BFD_CFG->patt_list);
init_list(&BFD_CFG->neigh_list);
BFD_CFG->accept_ipv4 = BFD_CFG->accept_ipv6 = 1;
BFD_CFG->accept_direct = BFD_CFG->accept_multihop = 1;
};
bfd_proto_item:
proto_item
| ACCEPT bfd_accept
| INTERFACE bfd_iface
| MULTIHOP bfd_multihop
| NEIGHBOR bfd_neighbor
@ -56,6 +59,21 @@ bfd_proto:
bfd_proto_start proto_name '{' bfd_proto_opts '}';
bfd_accept_item:
IPV4 { BFD_CFG->accept_ipv4 = 1; BFD_CFG->accept_ipv6 = 0; }
| IPV6 { BFD_CFG->accept_ipv4 = 0; BFD_CFG->accept_ipv6 = 1; }
| DIRECT { BFD_CFG->accept_direct = 1; BFD_CFG->accept_multihop = 0; }
| MULTIHOP { BFD_CFG->accept_direct = 0; BFD_CFG->accept_multihop = 1; }
;
bfd_accept:
{
BFD_CFG->accept_ipv4 = BFD_CFG->accept_ipv6 = 1;
BFD_CFG->accept_direct = BFD_CFG->accept_multihop = 1;
}
| bfd_accept bfd_accept_item
bfd_iface_start:
{
this_ipatt = cfg_allocz(sizeof(struct bfd_iface_config));
@ -164,7 +182,7 @@ bfd_neighbor: ipa bfd_neigh_iface bfd_neigh_local bfd_neigh_multihop
CF_CLI_HELP(SHOW BFD, ..., [[Show information about BFD protocol]]);
CF_CLI(SHOW BFD SESSIONS, optproto, [<name>], [[Show information about BFD sessions]])
{ bfd_show_sessions(proto_get_named($4, &proto_bfd)); };
{ PROTO_WALK_CMD($4, &proto_bfd, p) bfd_show_sessions(p); };
CF_CODE

View File

@ -366,7 +366,8 @@ bfd_rx_hook(sock *sk, uint len)
if (ps > BFD_STATE_DOWN)
DROP("invalid init state", ps);
s = bfd_find_session_by_addr(p, sk->faddr);
uint ifindex = (sk->sport == BFD_CONTROL_PORT) ? sk->lifindex : 0;
s = bfd_find_session_by_addr(p, sk->faddr, ifindex);
/* FIXME: better session matching and message */
if (!s)

View File

@ -34,7 +34,7 @@
* are probably inadequate.
*
* Loop detection based on AS_PATH causes updates to be withdrawn. RFC
* 4271 does not explicitly specifiy the behavior in that case.
* 4271 does not explicitly specify the behavior in that case.
*
* Loop detection related to route reflection (based on ORIGINATOR_ID
* and CLUSTER_LIST) causes updates to be withdrawn. RFC 4456 8
@ -72,7 +72,7 @@ struct bgp_attr_desc {
void (*export)(struct bgp_export_state *s, eattr *a);
int (*encode)(struct bgp_write_state *s, eattr *a, byte *buf, uint size);
void (*decode)(struct bgp_parse_state *s, uint code, uint flags, byte *data, uint len, ea_list **to);
void (*format)(eattr *ea, byte *buf, uint size);
void (*format)(const eattr *ea, byte *buf, uint size);
};
static const struct bgp_attr_desc bgp_attr_table[];
@ -118,7 +118,7 @@ bgp_set_attr(ea_list **attrs, struct linpool *pool, uint code, uint flags, uintp
static inline int
bgp_put_attr_hdr3(byte *buf, uint code, uint flags, uint len)
{
*buf++ = flags;
*buf++ = flags & ~BAF_EXT_LEN;
*buf++ = code;
*buf++ = len;
return 3;
@ -199,6 +199,179 @@ bgp_encode_raw(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
}
/*
* AIGP handling
*/
static int
bgp_aigp_valid(byte *data, uint len, char *err, uint elen)
{
byte *pos = data;
char *err_dsc = NULL;
uint err_val = 0;
#define BAD(DSC,VAL) ({ err_dsc = DSC; err_val = VAL; goto bad; })
while (len)
{
if (len < 3)
BAD("TLV framing error", len);
/* Process one TLV */
uint ptype = pos[0];
uint plen = get_u16(pos + 1);
if (len < plen)
BAD("TLV framing error", plen);
if (plen < 3)
BAD("Bad TLV length", plen);
if ((ptype == BGP_AIGP_METRIC) && (plen != 11))
BAD("Bad AIGP TLV length", plen);
ADVANCE(pos, len, plen);
}
#undef BAD
return 1;
bad:
if (err)
if (bsnprintf(err, elen, "%s (%u) at %d", err_dsc, err_val, (int) (pos - data)) < 0)
err[0] = 0;
return 0;
}
static const byte *
bgp_aigp_get_tlv(const struct adata *ad, uint type)
{
if (!ad)
return NULL;
uint len = ad->length;
const byte *pos = ad->data;
while (len)
{
uint ptype = pos[0];
uint plen = get_u16(pos + 1);
if (ptype == type)
return pos;
ADVANCE(pos, len, plen);
}
return NULL;
}
static const struct adata *
bgp_aigp_set_tlv(struct linpool *pool, const struct adata *ad, uint type, byte *data, uint dlen)
{
uint len = ad ? ad->length : 0;
const byte *pos = ad ? ad->data : NULL;
struct adata *res = lp_alloc_adata(pool, len + 3 + dlen);
byte *dst = res->data;
byte *tlv = NULL;
int del = 0;
while (len)
{
uint ptype = pos[0];
uint plen = get_u16(pos + 1);
/* Find position for new TLV */
if ((ptype >= type) && !tlv)
{
tlv = dst;
dst += 3 + dlen;
}
/* Skip first matching TLV, copy others */
if ((ptype == type) && !del)
del = 1;
else
{
memcpy(dst, pos, plen);
dst += plen;
}
ADVANCE(pos, len, plen);
}
if (!tlv)
{
tlv = dst;
dst += 3 + dlen;
}
/* Store the TLD */
put_u8(tlv + 0, type);
put_u16(tlv + 1, 3 + dlen);
memcpy(tlv + 3, data, dlen);
/* Update length */
res->length = dst - res->data;
return res;
}
static u64 UNUSED
bgp_aigp_get_metric(const struct adata *ad, u64 def)
{
const byte *b = bgp_aigp_get_tlv(ad, BGP_AIGP_METRIC);
return b ? get_u64(b + 3) : def;
}
static const struct adata *
bgp_aigp_set_metric(struct linpool *pool, const struct adata *ad, u64 metric)
{
byte data[8];
put_u64(data, metric);
return bgp_aigp_set_tlv(pool, ad, BGP_AIGP_METRIC, data, 8);
}
int
bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad)
{
eattr *a = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AIGP));
if (!a)
return 0;
const byte *b = bgp_aigp_get_tlv(a->u.ptr, BGP_AIGP_METRIC);
if (!b)
return 0;
u64 aigp = get_u64(b + 3);
u64 step = e->attrs->igp_metric;
if (!rte_resolvable(e) || (step >= IGP_METRIC_UNKNOWN))
step = BGP_AIGP_MAX;
if (!step)
step = 1;
*ad = a->u.ptr;
*metric = aigp + step;
if (*metric < aigp)
*metric = BGP_AIGP_MAX;
return 1;
}
static inline int
bgp_init_aigp_metric(rte *e, u64 *metric, const struct adata **ad)
{
if (e->attrs->source == RTS_BGP)
return 0;
*metric = rt_get_igp_metric(e);
*ad = NULL;
return *metric < IGP_METRIC_UNKNOWN;
}
/*
* Attribute hooks
*/
@ -223,7 +396,7 @@ bgp_decode_origin(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte
}
static void
bgp_format_origin(eattr *a, byte *buf, uint size UNUSED)
bgp_format_origin(const eattr *a, byte *buf, uint size UNUSED)
{
static const char *bgp_origin_names[] = { "IGP", "EGP", "Incomplete" };
@ -231,6 +404,15 @@ bgp_format_origin(eattr *a, byte *buf, uint size UNUSED)
}
static inline int
bgp_as_path_first_as_equal(const byte *data, uint len, u32 asn)
{
return (len >= 6) &&
((data[0] == AS_PATH_SEQUENCE) || (data[0] == AS_PATH_CONFED_SEQUENCE)) &&
(data[1] > 0) &&
(get_u32(data+2) == asn);
}
static int
bgp_encode_as_path(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
{
@ -253,17 +435,13 @@ bgp_decode_as_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte
{
struct bgp_proto *p = s->proto;
int as_length = s->as4_session ? 4 : 2;
int as_sets = p->cf->allow_as_sets;
int as_confed = p->cf->confederation && p->is_interior;
char err[128];
if (!as_path_valid(data, len, as_length, as_confed, err, sizeof(err)))
if (!as_path_valid(data, len, as_length, as_sets, as_confed, err, sizeof(err)))
WITHDRAW("Malformed AS_PATH attribute - %s", err);
/* In some circumstances check for initial AS_CONFED_SEQUENCE; RFC 5065 5.0 */
if (p->is_interior && !p->is_internal &&
((len < 2) || (data[0] != AS_PATH_CONFED_SEQUENCE)))
WITHDRAW("Malformed AS_PATH attribute - %s", "missing initial AS_CONFED_SEQUENCE");
if (!s->as4_session)
{
/* Prepare 32-bit AS_PATH (from 16-bit one) in a temporary buffer */
@ -272,6 +450,16 @@ bgp_decode_as_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte
len = as_path_16to32(data, src, len);
}
/* In some circumstances check for initial AS_CONFED_SEQUENCE; RFC 5065 5.0 */
if (p->is_interior && !p->is_internal &&
((len < 2) || (data[0] != AS_PATH_CONFED_SEQUENCE)))
WITHDRAW("Malformed AS_PATH attribute - %s", "missing initial AS_CONFED_SEQUENCE");
/* Reject routes with first AS in AS_PATH not matching neighbor AS; RFC 4271 6.3 */
if (!p->is_internal && p->cf->enforce_first_as &&
!bgp_as_path_first_as_equal(data, len, p->remote_as))
WITHDRAW("Malformed AS_PATH attribute - %s", "First AS differs from neigbor AS");
bgp_set_attr_data(to, s->pool, BA_AS_PATH, flags, data, len);
}
@ -322,7 +510,7 @@ bgp_decode_next_hop(struct bgp_parse_state *s, uint code UNUSED, uint flags UNUS
/* TODO: This function should use AF-specific hook */
static void
bgp_format_next_hop(eattr *a, byte *buf, uint size UNUSED)
bgp_format_next_hop(const eattr *a, byte *buf, uint size UNUSED)
{
ip_addr *nh = (void *) a->u.ptr->data;
uint len = a->u.ptr->length;
@ -389,6 +577,7 @@ bgp_encode_aggregator(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
/* Prepare 16-bit AGGREGATOR (from 32-bit one) in a temporary buffer */
byte *dst = alloca(6);
len = aggregator_32to16(dst, data);
data = dst;
}
return bgp_put_attr(buf, size, BA_AGGREGATOR, a->flags, data, len);
@ -412,7 +601,7 @@ bgp_decode_aggregator(struct bgp_parse_state *s, uint code UNUSED, uint flags, b
}
static void
bgp_format_aggregator(eattr *a, byte *buf, uint size UNUSED)
bgp_format_aggregator(const eattr *a, byte *buf, uint size UNUSED)
{
const byte *data = a->u.ptr->data;
@ -487,13 +676,44 @@ bgp_decode_cluster_list(struct bgp_parse_state *s, uint code UNUSED, uint flags,
}
static void
bgp_format_cluster_list(eattr *a, byte *buf, uint size)
bgp_format_cluster_list(const eattr *a, byte *buf, uint size)
{
/* Truncates cluster lists larger than buflen, probably not a problem */
int_set_format(a->u.ptr, 0, -1, buf, size);
}
int
bgp_encode_mp_reach_mrt(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
{
/*
* Limited version of MP_REACH_NLRI used for MRT table dumps (IPv6 only):
*
* 3 B MP_REACH_NLRI header
* 1 B MP_REACH_NLRI data - Length of Next Hop Network Address
* var MP_REACH_NLRI data - Network Address of Next Hop
*/
ip_addr *nh = (void *) a->u.ptr->data;
uint len = a->u.ptr->length;
ASSERT((len == 16) || (len == 32));
if (size < (3+1+len))
return -1;
bgp_put_attr_hdr3(buf, BA_MP_REACH_NLRI, BAF_OPTIONAL, 1+len);
buf[3] = len;
buf += 4;
put_ip6(buf, ipa_to_ip6(nh[0]));
if (len == 32)
put_ip6(buf+16, ipa_to_ip6(nh[1]));
return 3+1+len;
}
static inline u32
get_af3(byte *buf)
{
@ -544,13 +764,23 @@ bgp_decode_mp_unreach_nlri(struct bgp_parse_state *s, uint code UNUSED, uint fla
static void
bgp_export_ext_community(struct bgp_export_state *s, eattr *a)
{
struct adata *ad = ec_set_del_nontrans(s->pool, a->u.ptr);
if (!s->proto->is_interior)
{
struct adata *ad = ec_set_del_nontrans(s->pool, a->u.ptr);
if (ad->length == 0)
UNSET(a);
if (ad->length == 0)
UNSET(a);
ec_set_sort_x(ad);
a->u.ptr = ad;
ec_set_sort_x(ad);
a->u.ptr = ad;
}
else
{
if (a->u.ptr->length == 0)
UNSET(a);
a->u.ptr = ec_set_sort(s->pool, a->u.ptr);
}
}
static void
@ -580,6 +810,9 @@ bgp_decode_as4_aggregator(struct bgp_parse_state *s, uint code UNUSED, uint flag
static void
bgp_decode_as4_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
struct bgp_proto *p = s->proto;
int sets = p->cf->allow_as_sets;
char err[128];
if (s->as4_session)
@ -588,7 +821,7 @@ bgp_decode_as4_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byt
if (len < 6)
DISCARD(BAD_LENGTH, "AS4_PATH", len);
if (!as_path_valid(data, len, 4, 1, err, sizeof(err)))
if (!as_path_valid(data, len, 4, sets, 1, err, sizeof(err)))
DISCARD("Malformed AS4_PATH attribute - %s", err);
struct adata *a = lp_alloc_adata(s->pool, len);
@ -604,6 +837,42 @@ bgp_decode_as4_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byt
bgp_set_attr_ptr(to, s->pool, BA_AS4_PATH, flags, a);
}
static void
bgp_export_aigp(struct bgp_export_state *s, eattr *a)
{
if (!s->channel->cf->aigp)
UNSET(a);
}
static void
bgp_decode_aigp(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
char err[128];
/* Acceptability test postponed to bgp_finish_attrs() */
if ((flags ^ bgp_attr_table[BA_AIGP].flags) & (BAF_OPTIONAL | BAF_TRANSITIVE))
DISCARD("Malformed AIGP attribute - conflicting flags (%02x)", flags);
if (!bgp_aigp_valid(data, len, err, sizeof(err)))
DISCARD("Malformed AIGP attribute - %s", err);
bgp_set_attr_data(to, s->pool, BA_AIGP, flags, data, len);
}
static void
bgp_format_aigp(const eattr *a, byte *buf, uint size UNUSED)
{
const byte *b = bgp_aigp_get_tlv(a->u.ptr, BGP_AIGP_METRIC);
if (!b)
bsprintf(buf, "?");
else
bsprintf(buf, "%lu", get_u64(b + 3));
}
static void
bgp_export_large_community(struct bgp_export_state *s, eattr *a)
{
@ -671,7 +940,7 @@ bgp_decode_mpls_label_stack(struct bgp_parse_state *s, uint code UNUSED, uint fl
}
static void
bgp_format_mpls_label_stack(eattr *a, byte *buf, uint size)
bgp_format_mpls_label_stack(const eattr *a, byte *buf, uint size)
{
u32 *labels = (u32 *) a->u.ptr->data;
uint lnum = a->u.ptr->length / 4;
@ -820,6 +1089,15 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
.decode = bgp_decode_as4_aggregator,
.format = bgp_format_aggregator,
},
[BA_AIGP] = {
.name = "aigp",
.type = EAF_TYPE_OPAQUE,
.flags = BAF_OPTIONAL | BAF_DECODE_FLAGS,
.export = bgp_export_aigp,
.encode = bgp_encode_raw,
.decode = bgp_decode_aigp,
.format = bgp_format_aigp,
},
[BA_LARGE_COMMUNITY] = {
.name = "large_community",
.type = EAF_TYPE_LC_SET,
@ -1021,7 +1299,8 @@ bgp_decode_attr(struct bgp_parse_state *s, uint code, uint flags, byte *data, ui
const struct bgp_attr_desc *desc = &bgp_attr_table[code];
/* Handle conflicting flags; RFC 7606 3 (c) */
if ((flags ^ desc->flags) & (BAF_OPTIONAL | BAF_TRANSITIVE))
if (((flags ^ desc->flags) & (BAF_OPTIONAL | BAF_TRANSITIVE)) &&
!(desc->flags & BAF_DECODE_FLAGS))
WITHDRAW("Malformed %s attribute - conflicting flags (%02x)", desc->name, flags);
desc->decode(s, code, flags, data, len, to);
@ -1150,6 +1429,17 @@ withdraw:
return NULL;
}
void
bgp_finish_attrs(struct bgp_parse_state *s, rta *a)
{
/* AIGP test here instead of in bgp_decode_aigp() - we need to know channel */
if (BIT32_TEST(s->attrs_seen, BA_AIGP) && !s->channel->cf->aigp)
{
REPORT("Discarding AIGP attribute received on non-AIGP session");
bgp_unset_attr(&a->eattrs, s->pool, BA_AIGP);
}
}
/*
* Route bucket hash table
@ -1218,6 +1508,7 @@ bgp_get_bucket(struct bgp_channel *c, ea_list *new)
/* Create the bucket */
b = mb_alloc(c->pool, size);
*b = (struct bgp_bucket) { };
init_list(&b->prefixes);
b->hash = hash;
@ -1342,8 +1633,7 @@ bgp_get_prefix(struct bgp_channel *c, net_addr *net, u32 path_id)
else
px = mb_alloc(c->pool, sizeof(struct bgp_prefix) + net->length);
px->buck_node.next = NULL;
px->buck_node.prev = NULL;
*px = (struct bgp_prefix) { };
px->hash = hash;
px->path_id = path_id;
net_copy(px->net, net);
@ -1481,6 +1771,16 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
if (p->is_interior && ! bgp_find_attr(attrs0, BA_LOCAL_PREF))
bgp_set_attr_u32(&attrs, pool, BA_LOCAL_PREF, 0, p->cf->default_local_pref);
/* AIGP attribute - accumulate local metric or originate new one */
u64 metric;
if (s.local_next_hop &&
(bgp_total_aigp_metric_(e, &metric, &ad) ||
(c->cf->aigp_originate && bgp_init_aigp_metric(e, &metric, &ad))))
{
ad = bgp_aigp_set_metric(pool, ad, metric);
bgp_set_attr_ptr(&attrs, pool, BA_AIGP, 0, ad);
}
/* IBGP route reflection, RFC 4456 */
if (src && src->is_internal && p->is_internal && (src->local_as == p->local_as))
{
@ -1578,12 +1878,6 @@ bgp_get_neighbor(rte *r)
return p->cf->confederation ?: p->local_as;
}
static inline int
rte_resolvable(rte *rt)
{
return rt->attrs->dest == RTD_UNICAST;
}
static inline int
rte_stale(rte *r)
{
@ -1639,6 +1933,14 @@ bgp_rte_better(rte *new, rte *old)
if (n < o)
return 0;
/* RFC 7311 4.1 - Apply AIGP metric */
u64 n2 = bgp_total_aigp_metric(new);
u64 o2 = bgp_total_aigp_metric(old);
if (n2 < o2)
return 1;
if (n2 > o2)
return 0;
/* RFC 4271 9.1.2.2. a) Use AS path lengths */
if (new_bgp->cf->compare_path_lengths || old_bgp->cf->compare_path_lengths)
{
@ -2022,7 +2324,7 @@ bgp_process_as4_attrs(ea_list **attrs, struct linpool *pool)
}
int
bgp_get_attr(eattr *a, byte *buf, int buflen)
bgp_get_attr(const eattr *a, byte *buf, int buflen)
{
uint i = EA_ID(a->id);
const struct bgp_attr_desc *d;
@ -2062,7 +2364,12 @@ bgp_get_route_info(rte *e, byte *buf)
if (rte_stale(e))
buf += bsprintf(buf, "s");
if (e->attrs->hostentry)
u64 metric = bgp_total_aigp_metric(e);
if (metric < BGP_AIGP_MAX)
{
buf += bsprintf(buf, "/%lu", metric);
}
else if (e->attrs->igp_metric)
{
if (!rte_resolvable(e))
buf += bsprintf(buf, "/-");

Some files were not shown because too many files have changed in this diff Show More