Compare commits

...

137 commits

Author SHA1 Message Date
6269f404fc
test median smoothing 2022-12-12 13:46:36 +08:00
Toke Høiland-Jørgensen
d9763bd7a0
babel: Add route metric smoothing
The Babel RTT extension employs metric smoothing to dampen route
oscillations in the face of varying RTT values between two peers[0].

This patch implements such dampening in Bird, roughly following the
implementation in babeld (i.e., using the same exponential function
definition). The main difference is that we calculate everything in the
native Bird microsecond time unit (and increase constants accordingly), and
that we split out the smoothed metric calculation in two function variants,
one that has no side effects and one that does.

  [0] https://arxiv.org/pdf/1403.3488.pdf

Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
2022-12-12 13:46:36 +08:00
Toke Høiland-Jørgensen
94ebf8e00e
babel: Add support for the RTT extension
This adds support to the Babel protocol for the RTT extension specified in
draft-ietf-babel-rtt-extension. While this extension is not yet at the RFC
stage, it is one of the more useful extensions to Babel[0], so it seems
worth having in Bird as well.

The extension adds timestamps to Hello and IHU TLVs and uses these to
compute an RTT to each neighbour. An extra per-neighbour cost is then
computed from the RTT based on a minimum and maximum interval and cost
value specified in the configuration. The primary use case for this is
improving routing in a geographically distributed tunnel-based overlay
network.

The implementation follows the babeld implementation when picking constants
and default configuration values. It also uses the same RTT smoothing
algorithm as babeld, and follows it in adding a new 'tunnel' interface type
which enables RTT by default.

[0] https://alioth-lists.debian.net/pipermail/babel-users/2022-April/003932.html

Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
2022-12-12 13:46:35 +08:00
Toke Høiland-Jørgensen
071354da5b
lib/timer: Add current_time_now() function for immediate timestamp
Add a current_time_now() function which gets an immediate monotonic
timestamp instead of using the cached value from the event loop. This is
useful for callers that need precise times, such as the Babel RTT
measurement code.

Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
2022-12-12 13:46:35 +08:00
Toke Høiland-Jørgensen
8657e7e703
babel: Don't try to remove multicast seqno request objects from neighbour list
The Babel seqno request code keeps track of which seqno requests are
outstanding for a neighbour by putting them onto a per-neighbour list. When
reusing a seqno request, it will try to remove this node, but if the seqno
request in question was a multicast request with no neighbour attached this
will result in a crash because it tries to remove a list node that wasn't
added to any list.

Fix this by making the list remove conditional. Also add a check so that
seqno requests are only reused if the neighbour also matches, allowing
multiple outstanding requests for the same router ID.

Fixes: ebd5751cde ("Babel: Seqno requests are properly decoupled from neighbors when the underlying interface disappears")
Reported-by: Stefan Haller <stefan.haller@stha.de>
Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
2022-12-12 13:46:35 +08:00
Ondrej Zajicek
1e47b9f203 NEWS and version update 2022-12-11 17:28:14 +01:00
Ondrej Zajicek
34ebc4e1ba BSD: Workaround for direct routes on FreeBSD 13.0
FreeBSD 13.0 added some safechecks for syscalls, rejecting sockaddrs that
are too small, later versions loosen up the check.
2022-12-11 16:28:28 +01:00
Ondrej Zajicek
937ebf2536 BGP: Log unacceptable hold time as decimal number
Thanks Johannes Moos for the suggestion.
2022-12-10 18:06:52 +01:00
Ondrej Zajicek
4c19a8a984 CLI: Fix for long-lived sessions during high loads
When there is a continuos stream of CLI commands, cli_get_command()
always returns 1 (there is a new command). Anyway, the socket receive
buffer was reset only when there was no command at all, leading to a
strange behavior: after a while, the CLI receive buffer came to its end,
then read() was called with zero size buffer, it returned 0 which was
interpreted as EOF.

The patch fixes that by resetting the buffer position after each command
and moving remaining data at the beginning of buffer.

Thanks to Maria Matejka for examining the bug and for the original bugfix.
2022-12-10 17:32:42 +01:00
Ondrej Zajicek
1124f39f73 Client: Unknown command should return nonzero errorcode 2022-12-10 03:02:26 +01:00
Ondrej Zajicek
e48f898fda Doc: Document issue with import tables
The import table does not work reliably together with re-evaluation of
routes due to recursive next hops or flowspec validation. We will at
least document that here, as import tables are completely redesigned and
this issue is fixed in BIRD 3.x branch.
2022-12-09 22:43:27 +01:00
Alexander V. Chernikov
a80cd47074 Netlink on FreeBSD support
Netlink support was added to FreeBSD recently. It is not as full-featured
as its Linux counterpart yet, however the added subset is enough to make
a routing daemon work. Specifically, it supports multiple tables,
multipath, nexthops and nexthops groups. No MPLS support yet.

The attached change adds 'bsd-netlink’ sysconf target, allowing to build
both netlink & rtsock versions on FreeBSD.
2022-12-09 16:01:30 +01:00
Ondrej Zajicek
3859e4efc1 BGP: Improve handling of hold and keepalive timers
The effective keepalive time now scales relative to the negotiated
hold time, to maintain proportion between the keepalive time and the
hold time. This avoids issues when both keepalive and hold times
were configured, the hold time was negotiated to a smaller value,
but the keepalive time stayed the same.

Add new options 'min hold time' and 'min keepalive time', which reject
session attempts with too small hold time.

Improve validation of config options an their documentation.

Thanks to Alexander Zubkov and Sergei Goriunov for suggestions.
2022-12-09 05:53:24 +01:00
Ondrej Zajicek
e80156d936 Nest: Avoid spurious announcements triggered by filtered routes
When filtered routes (enabled by 'import keep filtered' option) are
updated, they trigger announcements by rte_announce(). For regular
channels (e.g. type RA_OPTIMAL or RA_ANY) such announcement is just
ignored, but in case of RA_ACCEPTED (BGP peer with 'secondary' option)
it just reannounces the old (and still valid) best route.

The patch ensures that such no-change is ignored even for these channels.
2022-12-06 19:51:50 +01:00
Ondrej Zajicek
a50d2fa65f CI: Remove docker rebuild phase
It is unnnecessary and takes too much time
2022-11-30 02:48:59 +01:00
Ondrej Zajicek
ff38ee5986 CI: Try new workers 2022-11-30 02:48:59 +01:00
Ondrej Zajicek
543c8ba097 BSD: Fix krt socket code w.r.t. rte/rta changes 2022-11-30 02:43:39 +01:00
Ondrej Zajicek
140c534fb8 Fix build variables for OpenBSD 2022-11-29 18:29:30 +01:00
Ondrej Zajicek
bbac9ca958 Conf: Make 'configure check' command restricted
While it does not directly change BIRD state, it can trigger reading
arbitrary files and eating significant memory.
2022-11-09 22:02:46 +01:00
Ondrej Zajicek
371eb49043 Conf: Free stored old config before parsing new one
BIRD keeps a previous (old) configuration for the purpose of undo. The
existing code frees it after a new configuration is successfully parsed
during reconfiguration. That causes memory usage spikes as there are
temporarily three configurations (old, current, and new). The patch
changes it to free the old one before parsing the new one (as user
already requested a new config). The disadvantage is that undo is
not available after failed reconfiguration.
2022-11-09 21:54:45 +01:00
Maria Matejka
84545a26cc Added more netlab tests for automatic run.
This commit uses bird-tools in version
f35e8bce829f4bff61ec7eb07ec9c67aa867bc9a
2022-11-08 11:19:12 +01:00
Maria Matejka
57308fb277 Page allocator: Fixed minor bugs and added commentary 2022-11-03 12:38:57 +01:00
Maria Matejka
9d03c3f56c Memory pages are not munmapped, instead we just madvise()
Memory unmapping causes slow address space fragmentation, leading in
extreme cases to failing to allocate pages at all. Removing this problem
by keeping all the pages allocated to us, yet calling madvise() to let
kernel dispose of them.

This adds a little complexity and overhead as we have to keep the
pointers to the free pages, therefore to hold e.g. 1 GB of 4K pages with
8B pointers, we have to store 2 MB of data.
2022-11-02 12:56:54 +01:00
Maria Matejka
37b6444137 Moved config-related allocations to config_pool and showing its size in memory usage 2022-11-01 16:38:24 +01:00
Alexander Zubkov
5aebce5e0c Doc: Add documentation for "show route (import|export) table" 2022-10-18 04:25:29 +02:00
Ondrej Zajicek
e471f9e0fb Filter: Fix handling of variables in anonymous filters
Define scope for anonymous filters, and also explicitly distinguish block
scopes and function/filter scopes instead of using anonymous / named
distinction.

Anonymous filters forgot to push scope, so variables for them were in
fact defined in the top scope and therefore they shared a frame. This got
broken after rework of variables, which assumed that there is a named
scope for every function/filter.
2022-10-18 03:58:19 +02:00
Ondrej Zajicek
3242529750 Netlink: Parse onlink flag even on direct routes
While onlink flag is meaningful only with explicit next hops, it can be
defined also on direct routes. Parse it also in this case to avoid
periodic updates of the same route.

Thanks to Marcin Saklak for the bugreport.
2022-10-12 17:57:26 +02:00
Ondrej Zajicek
8f79e6b93e BGP: Add option 'next hop prefer global'
Add BGP channel option 'next hop prefer global' that modifies BGP
recursive next hop resolution to use global next hop IPv6 address instead
of link-local next hop IPv6 address for immediate next hop of received
routes.
2022-10-10 05:06:19 +02:00
Ondrej Zajicek
8478de8817 Nest: Add channel config flag to distinguish new or copy
It is useful to distinguish whehter channel config returned from
channel_config_get() was allocated new, or existing from template.
Caller may want to initialize new ones.
2022-10-03 20:18:12 +02:00
Ondrej Zajicek
92a8565547 Filter: Add some minor functions for f_tree and EC
Add some supportive functions for f_tree and EC. These functions are used
by L3VPN code.
2022-10-03 20:18:12 +02:00
Ondrej Zajicek
da0b589e7b BGP: Some fixes related to VRF and MPLS interactions
- When next hop is reset to local IP, we should remove BGP label stack,
   as it is related to original next hop

 - BGP next hop or immediate next hop from one VRF should not be passed
   to another VRF, as they are different IP namespaces
2022-10-03 20:18:12 +02:00
Ondrej Zajicek
54430df953 BGP: Do not assume that all channels are struct bgp_channel
In principle, the channel list is a list of parent struct proto and can
contain general structures of type struct channel, That is useful e.g.
for adding MPLS channels to BGP.
2022-10-03 20:18:12 +02:00
Maria Matejka
605ff0a0eb RPKI: wait for retry_time if we get error immediately after connected 2022-10-03 17:09:02 +02:00
Alexander Zubkov
0f2be469f8 KRT: Fix setting default preference
Changes in commit eb937358 broke setting of channel preference for alien
routes learned during scan. The preference was set only for async routes.
Move common attribute processing part of functions krt_learn_async() and
krt_learn_async() to a separate function to have only one place for such
changes.
2022-09-27 11:33:41 +02:00
Maria Matejka
c73343de67 Revert "Reducing filter stack size to allow for lesser thread stack size"
This reverts commit 2c13759136.
2022-09-16 10:11:51 +02:00
Maria Matejka
71b3456eed Better profylaction recursive route loops
In some specific configurations, it was possible to send BIRD into an
infinite loop of recursive next hop resolution. This was caused by route
priority inversion.

To prevent priority inversions affecting other next hops, we simply
refuse to resolve any next hop if the best route for the matching prefix
is recursive or any other route with the same preference is recursive.

Next hop resolution doesn't change route priority, therefore it is
perfectly OK to resolve BGP next hops e.g. by an OSPF route, yet if the
same (or covering) prefix is also announced by iBGP, by retraction of
the OSPF route we would get a possible priority inversion.
2022-09-06 15:15:03 +02:00
Maria Matejka
d2c1036a42 Merge branch 'mq-fix-eattr-setting' into backport 2022-08-18 22:07:50 +02:00
Maria Matejka
dc28c6ed1c Simplified the protocol hookup code in Makefiles 2022-08-18 22:07:30 +02:00
Maria Matejka
16ac6c3c74 Fixed initialization of Linux kernel route attributes 2022-08-18 17:44:00 +02:00
Maria Matejka
bc4ad83dac Merge commit '082905a8' into HEAD 2022-08-03 15:04:42 +02:00
Maria Matejka
73abd91ac6 rip_rte_better() uses the IGP_METRIC_UNKNOWN instead of protocol-specific infinity 2022-08-03 15:04:28 +02:00
Ondrej Zajicek
082905a833 Merge branch 'master' into backport 2022-07-27 00:47:24 +02:00
Ondrej Zajicek
ddb1bdf281 Netlink: Restrict route replace for IPv6
Seems like the previous patch was too optimistic, as route replace is
still broken even in Linux 4.19 LTS (but fixed in Linux 5.10 LTS) for:

  ip route add 2001:db8::/32 via fe80::1 dev eth0
  ip route replace 2001:db8::/32 dev eth0

It ends with two routes instead of just the second.

The issue is limited to direct and special type (e.g. unreachable)
routes, the patch restricts route replace for cases when the new route
is a regular route (with a next hop address).
2022-07-26 18:45:20 +02:00
Ondrej Zajicek
722daa9500 Netlink: Simplify handling of IPv6 ECMP routes
When IPv6 ECMP support first appeared in Linux kernel, it used different
API than IPv4 ECMP. Individual next hops were updated and announced
separately, instead of using RTA_MULTIPATH as in IPv4. This has several
drawbacks and requires complex code to merge received notifications to
one multipath route.

When Linux came with IPv6 RTA_MULTIPATH support, the initial versions
were somewhat buggy, so we kept using the old API for updates (splitting
multipath routes to sequences of route updates), while accepting both
old-style routes and RTA_MULTIPATH routes in scans / notifications.

As IPv6 RTA_MULTIPATH support is here for a long time, this patch fully
switches Netlink to the IPv6 RTA_MULTIPATH API and removes old complex
code for handling individual next hop announces.

The required Linux version is at least 4.11 for reliable operation.

Thanks to Daniel Gröber for the original patch.
2022-07-25 00:11:40 +02:00
Ondrej Zajicek
2e484f8d29 Merge branch 'master' into backport 2022-07-24 20:08:02 +02:00
Ondrej Zajicek
534d0a4b44 KRT: Scan routing tables separetely on linux to avoid congestion
Remove compile-time sysdep option CONFIG_ALL_TABLES_AT_ONCE, replace it
with runtime ability to run either separate table scans or shared scan.

On Linux, use separate table scans by default when the netlink socket
option NETLINK_GET_STRICT_CHK is available, but retreat to shared scan
when it fails.

Running separate table scans has advantages where some routing tables are
managed independently, e.g. when multiple routing daemons are running on
the same machine, as kernel routing table modification performance is
significantly reduced when the table is modified while it is being
scanned.

Thanks Daniel Gröber for the original patch and Toke Høiland-Jørgensen
for suggestions.
2022-07-24 02:15:20 +02:00
Maria Matejka
432dfe3b9b Fixed a rarely used part of Babel: comparing two routes in table by their metric 2022-07-22 15:48:20 +02:00
Maria Matejka
4d48ede51d Revert "Export table: Delay freeing of old stored route."
This reverts commit cee0cd148c.
This change is not needed in version 2 and the surrounding code has
disappeared mostly in version 3.
2022-07-22 15:37:21 +02:00
Ondrej Zajicek
971721c9b5 BGP: Minor improvements to BGP roles
Add support for bgp_otc in filters and warning for configuration
inside confederations.
2022-07-12 15:03:17 +02:00
Eugene Bogomazov
c73b5d2d3d BGP: Implement BGP roles
Implement BGP roles as described in RFC 9234. It is  a mechanism for
route leak prevention and automatic route filtering based on common BGP
topology relationships. It defines role capability (controlled by 'local
role' option) and OTC route attribute, which is used for automatic route
filtering and leak detection.

Minor changes done by commiter.
2022-07-11 17:25:54 +02:00
Maria Matejka
b5c8fce284 Added forgotten route source locking in flowspec validation 2022-07-11 13:04:01 +02:00
Maria Matejka
2e5bfeb73a Merge remote-tracking branch 'origin/master' into backport 2022-07-11 11:08:10 +02:00
Maria Matejka
d429bc5c84 Merge commit 'beb5f78a' into backport 2022-07-11 10:41:17 +02:00
Maria Matejka
7e9cede1fd Merge version 2.0.10 into backport 2022-07-10 14:19:24 +02:00
Ondrej Zajicek (work)
cb339a3067 Filter: Implement for loops
For loops allow to iterate over elements in compound data like BGP paths
or community lists. The syntax is:

  for [ <type> ] <variable> in <expr> do <command-body>
2022-06-27 21:13:32 +02:00
Ondrej Zajicek (work)
1ac8e11bba Filter: Implement mixed declarations of local variables
Allow variable declarations mixed with code, also in nested blocks with
proper scoping, and with variable initializers. E.g:

function fn(int a)
{
  int b;
  int c = 10;

  if a > 20 then
  {
    b = 30;
    int d = c * 2;
    print a, b, c, d;
  }

  string s = "Hello";
}
2022-06-27 21:13:32 +02:00
Ondrej Zajicek (work)
a2527ee53d Filter: Improve handling of stack frames in filter bytecode
When f_line is done, we have to pop the stack frame. The old code just
removed nominal number of args/vars. Change it to use stored ventry value
modified by number of returned values. This allows to allocate variables
on a stack frame during execution of f_lines instead of just at start.

But we need to know the number of returned values for a f_line. It is 1
for term, 0 for cmd. Store that to f_line during linearization.
2022-06-27 21:13:32 +02:00
Ondrej Zajicek (work)
f31f4e6eef Filter: Simplify handling of command sequences
Command sequences in curly braces used a separate nonterminal in grammar.
Handle them as a regular command.
2022-06-27 21:13:31 +02:00
Ondrej Zajicek (work)
1e6acf34bb Filter: Fix bug in variable shadowing
When a new variable used the same name as an existing symbol in an outer
scope, then offset number was defined based on a scope of the existing
symbol ($3) instead of a scope of the new symbol (sym_). That can lead
to two variables sharing the same memory slot.
2022-06-27 21:13:31 +02:00
Ondrej Zajicek (work)
946cedfcfe Filter: Implement soft scopes
Soft scopes are anonymous scopes that most likely do not contain any
symbol, so allocating regular scope is postponed when it is really
needed.
2022-06-27 21:13:31 +02:00
Ondrej Zajicek (work)
26bc4f9904 Filter: Implement direct recursion
Direct recursion almost worked, just crashed on function signature check.
Split function parsing such that function signature is saved before
function body is processed. Recursive calls are marked so they can be
avoided during f_same() and similar code walking.

Also, include tower of hanoi solver as a test case.
2022-06-27 21:13:31 +02:00
Ondrej Zajicek (work)
fb1d8f6513 Filter: Apply constant promotion for FI_EQ / FI_NEQ
Equality comparison is defined on all values, even of different
types, but we still want to do constant promotion if possible.
2022-06-27 21:13:31 +02:00
Alexander Zubkov
b2d6d2948a Filter: Add literal for empty set
Add literal for empty set [], which works both for tree-based sets
and prefix sets by using existing constant promotion mechanism.

Minor changes by committer.
2022-06-27 21:13:31 +02:00
Ondrej Zajicek (work)
8f3c6151b4 Nest: Cleanups in as_path_filter()
Use struct f_val as a common argument for as_path_filter(), as suggested
by Alexander Zubkov. That allows to use NULL sets as valid arguments.
2022-06-27 21:13:31 +02:00
Ondrej Zajicek (work)
9b302c133f Filter: Ensure that all expressions declared return type
All instructions with a return value (i.e. expressions, ones with
non-zero outval, third argument in INST()) should declare their return
type. Check that automatically by M4 macros.

Set outval of FI_RETURN to 0. The instruction adds one value to stack,
but syntactically it is a statement, not an expression.

Add fake return type declaration to FI_CALL, otherwise the automatic
check would fail builds.
2022-06-27 21:13:31 +02:00
Ondrej Zajicek (work)
cde8094c1f Filter: Improve description of type system 2022-06-27 21:13:31 +02:00
Ondrej Zajicek (work)
93d6096c87 Filter: Implement type checks for function calls
Keep list of function parameters in f_line and use it to verify
types of arguments for function calls. Only static type checks
are implemented.
2022-06-27 21:13:31 +02:00
Ondrej Zajicek (work)
4c0c507b1f Filter: Clean up function call instruction
Pass instructions of function call arguments as vararg arguments to
FI_CALL instruction constructor and move necessary magic from parser
code to interpreter / instruction code.
2022-06-27 21:13:31 +02:00
Maria Matejka
beb5f78ada Preexport callback now takes the channel instead of protocol as argument
Passing protocol to preexport was in fact a historical relic from the
old times when channels weren't a thing. Refactoring that to match
current extensibility needs.
2022-06-27 19:04:24 +02:00
Maria Matejka
652be92a21 Merge remote-tracking branch 'origin/master' into haugesund-to-2.0 2022-05-30 15:20:21 +02:00
Maria Matejka
f196b12c62 Merge commit '9eec503b251c3388579032b300d32640403d8612' into haugesund-to-2.0 2022-05-30 15:20:05 +02:00
Maria Matejka
097f157182 Merge commit '692055e3df6cc9f0d428d3b0dd8cdd8e825eb6f4' into haugesund-to-2.0 2022-05-30 15:17:52 +02:00
Alexander Zubkov
5299fb9db0 Fixed spurious undef of route attributes 2022-05-04 15:37:23 +02:00
Maria Matejka
98fd158e28 RIP: fixed the EA_RIP_FROM attribute
The interface pointer was improperly converted to u32 and back. Fixing
this by explicitly allocating an adata structure for it. It's not so
memory efficient, we'll optimize this later.
2022-04-13 17:05:12 +02:00
Maria Matejka
d39ef961d1 BGP uses lp_save / lp_restore instead of linpool flushing
It is too cryptic to flush tmp_linpool in these cases and we don't want
anybody in the future to break this code by adding an allocation
somewhere which should persist over that flush.

Saving and restoring linpool state is safer.
2022-04-06 18:14:08 +02:00
Maria Matejka
7e86ff2076 All linpools use pages to allocate regular blocks 2022-04-06 18:14:08 +02:00
Maria Matejka
dabd7bccb3 BGP: Fixed LLGR depreferencing in bgp_rte_mergable 2022-04-06 18:14:08 +02:00
Maria Matejka
4a23ede2b0 Protocols have their own explicit init routines 2022-04-06 18:14:08 +02:00
Maria Matejka
0f68515263 Unsetting route attributes without messing with type system 2022-04-06 18:14:08 +02:00
Maria Matejka
63cf5d5d8c Eattr flags (originated and fresh) get their own struct fields 2022-04-06 18:14:08 +02:00
Maria Matejka
af8568a870 Minor fix: f_val literals should always have named struct fields 2022-04-06 18:14:08 +02:00
Maria Matejka
170b20701c Converted Slab allocator to typed lists 2022-04-06 18:14:08 +02:00
Maria Matejka
ebd807c0b8 Slab allocator can free the blocks without knowing the parent structure 2022-04-06 18:14:08 +02:00
Maria Matejka
3a6eda995e Typed lists for easier walking and stronger type checking 2022-04-06 18:14:08 +02:00
Maria Matejka
c53f547a0b Printf variant with a result allocated inside a pool / linpool 2022-03-15 11:21:46 +01:00
Maria Matejka
3c42f7af6a Slab memory allocator unit test 2022-03-14 17:37:56 +01:00
Maria Matejka
4e60b3ee72 Fixed a static assert in page allocator 2022-03-09 13:28:03 +01:00
Maria Matejka
9b6db9f9b8 Merge remote-tracking branch 'origin/master' into haugesund 2022-03-09 11:51:00 +01:00
Maria Matejka
19e727a248 Merge commit '60880b539b8886f76961125d89a265c6e1112b7a' into haugesund 2022-03-09 11:29:56 +01:00
Maria Matejka
8a4bc4fdbf BGP Flowspec validation: Removed in-route optimization for multithreading compatibility 2022-03-09 11:27:34 +01:00
Maria Matejka
24773af9e0 Merge commit 'e42eedb9' into haugesund 2022-03-09 11:02:55 +01:00
Maria Matejka
83d9920f90 Merge commit '5cff1d5f' into haugesund
Conflicts:
      proto/bgp/attrs.c
      proto/pipe/pipe.c
2022-03-09 10:56:06 +01:00
Maria Matejka
ff47cd80dd Merge commit 'd5a32563' into haugesund 2022-03-09 10:50:38 +01:00
Maria Matejka
9e60a1fbc3 Fixed resource initialization in unit tests 2022-03-09 10:30:42 +01:00
Maria Matejka
b90c9b164f Linpools with pages fixed to the final page allocator version 2022-03-09 10:30:42 +01:00
Maria Matejka
eeec9ddbf2 Merge commit '0c59f7ff' into haugesund 2022-03-09 09:13:55 +01:00
Maria Matejka
0c59f7ff01 Revert "Bound allocated pages to resource pools with page caches to avoid unnecessary syscalls"
This reverts commit 7f0e598208.
2022-03-09 09:13:31 +01:00
Maria Matejka
c20506dc07 Revert "fixup! Bound allocated pages to resource pools with page caches to avoid unnecessary syscalls"
This reverts commit bea582cbb5.
2022-03-09 09:13:21 +01:00
Maria Matejka
1c7df2c240 Revert "Multipage allocation"
This reverts commit 6cd3771378.
2022-03-09 09:13:20 +01:00
Maria Matejka
1740ff57e8 Revert "fixup! Multipage allocation"
This reverts commit a54f75f454.
2022-03-09 09:13:18 +01:00
Maria Matejka
c78247f9b9 Single-threaded version of sark-branch memory page management 2022-03-09 09:10:44 +01:00
Maria Matejka
06ece3265e Replacing BGP temporary linpools by the common temporary linpool 2022-03-02 12:13:49 +01:00
Maria Matejka
d814a8cb93 Replaced custom linpools in tests for the common tmp_linpool 2022-03-02 12:13:49 +01:00
Maria Matejka
48bf1322aa Introducing an universal temporary linpool flushed after every task 2022-03-02 12:13:49 +01:00
Maria Matejka
2e8b8bfcc4 Static list initializer 2022-03-02 12:13:49 +01:00
Maria Matejka
d071aca7aa Merge commit '2c13759136951ef0e70a3e3c2b2d3c9a387f7ed9' into haugesund 2022-03-02 10:01:44 +01:00
Maria Matejka
60880b539b Extended route trace: logging Path Identifiers 2021-11-09 17:42:36 +01:00
Maria Matejka
0b295d695a Dropping the unused rte_same hook 2021-10-13 19:09:05 +02:00
Maria Matejka
89ff49f8f0 Dropping rte-local dumper entries 2021-10-13 19:09:05 +02:00
Maria Matejka
e42eedb912 Kernel: Convert the rte-local attributes to extended attributes and flags to pflags 2021-10-13 19:09:04 +02:00
Maria Matejka
5cff1d5f02 Route: moved rte_src pointer from rta to rte
It is an auxiliary key in the routing table, not a route attribute.
2021-10-13 19:09:04 +02:00
Maria Matejka
d5a32563df Preexport: No route modification, no linpool needed 2021-10-13 19:09:04 +02:00
Maria Matejka
541881bedf RIP fixup + dropping the tmp_attrs mechanism as obsolete 2021-10-13 19:09:04 +02:00
Maria Matejka
3660f19dd5 Dropping the RTS_DUMMY temporary route storage.
Kernel route sync is done by other ways now and this code is not used
currently.
2021-10-13 19:09:04 +02:00
Maria Matejka
eb937358c0 Preference moved to RTA and set explicitly in protocols 2021-10-13 19:09:04 +02:00
Maria Matejka
cee0cd148c Export table: Delay freeing of old stored route.
This is needed to provide the protocols the full old route after filters
when export table is enabled.
2021-10-13 19:09:04 +02:00
Maria Matejka
ddd89ba12d BGP: Moved the suppressed and stale flags to pflags 2021-10-13 19:09:04 +02:00
Maria Matejka
c507fb41bb Babel: Convert the rte-local attributes to extended attributes 2021-10-13 19:09:04 +02:00
Maria Matejka
8216ec3027 There may be a symbol with NULL protocol when reconfiguring 2021-10-13 19:09:04 +02:00
Maria Matejka
5f0cb61d82 OSPF: Convert the rte-local attributes to extended attributes 2021-10-13 19:09:04 +02:00
Maria Matejka
8ac20511e1 Show route may be accidentally called on shutdown also when not all default tables are present 2021-10-13 19:09:04 +02:00
Maria Matejka
a0e4c66404 RIP: convert the rte-local attributes to extended attributes 2021-10-13 19:09:04 +02:00
Maria Matejka
6e13df70fd Extended route attributes may include also pointers 2021-10-13 19:09:04 +02:00
Maria Matejka
d471d5fc7c IGP metric getter refactoring to protocol callback
Direct protocol hooks for IGP metric inside nest/rt-table.c make the
protocol API unnecessarily complex. Instead, we use a proper callback.
2021-10-13 19:09:04 +02:00
Maria Matejka
a54f75f454 fixup! Multipage allocation 2021-10-13 19:08:35 +02:00
Maria Matejka
6cd3771378 Multipage allocation
We can also quite simply allocate bigger blocks. Anyway, we need these
blocks to be aligned to their size which needs one mmap() two times
bigger and then two munmap()s returning the unaligned parts.

The user can specify -B <N> on startup when <N> is the exponent of 2,
setting the block size to 2^N. On most systems, N is 12, anyway if you
know that your configuration is going to eat gigabytes of RAM, you are
almost forced to raise your block size as you may easily get into memory
fragmentation issues or you have to raise your maximum mapping count,
e.g. "sysctl vm.max_map_count=(number)".
2021-10-13 19:01:22 +02:00
Maria Matejka
3a31c3aad6 CLI socket accept() may also fail and should produce some message, not a coredump. 2021-10-13 19:00:36 +02:00
Maria Matejka
d322ee3d54 OSPF: explicitly stop the periodic tick on shutdown to avoid recalculation races 2021-10-13 19:00:36 +02:00
Maria Matejka
e5a8eec6d7 Linpools may use pages instead of xmalloc 2021-10-13 19:00:36 +02:00
Maria Matejka
bea582cbb5 fixup! Bound allocated pages to resource pools with page caches to avoid unnecessary syscalls 2021-10-13 18:59:45 +02:00
Maria Matejka
7f0e598208 Bound allocated pages to resource pools with page caches to avoid unnecessary syscalls 2021-09-10 18:13:50 +02:00
Maria Matejka
2c13759136 Reducing filter stack size to allow for lesser thread stack size 2021-09-10 18:11:28 +02:00
Maria Matejka
ceef6de459 OSPF: Setting a list node NULL before use 2021-09-10 17:38:22 +02:00
Maria Matejka
923a6644b2 Fixed memory poisoning in slab 2021-09-10 17:38:22 +02:00
Maria Matejka
227e2d5541 Debug output uses local buffer to avoid clashes between threads. 2021-09-10 17:37:46 +02:00
Maria Matejka
eb20251655 Filter: Additional consistency checks 2021-09-10 17:37:46 +02:00
Ondrej Zajicek (work)
47d92d8f9d Nest: Clean up main channel handling
Remove assumption that main channel is the only channel.
2021-09-10 17:32:05 +02:00
110 changed files with 3662 additions and 2006 deletions

View file

@ -5,9 +5,10 @@ variables:
DOCKER_CMD: docker --config="$HOME/.docker/$CI_JOB_ID/" DOCKER_CMD: docker --config="$HOME/.docker/$CI_JOB_ID/"
IMG_BASE: registry.nic.cz/labs/bird IMG_BASE: registry.nic.cz/labs/bird
TOOLS_DIR: /var/lib/gitlab-runner/bird-tools TOOLS_DIR: /var/lib/gitlab-runner/bird-tools
STAYRTR_BINARY: /var/lib/gitlab-runner/stayrtr-0.1-108-g8d18a41-linux-x86_64
stages: stages:
- image # - image
- build - build
- pkg - pkg
- test - test
@ -32,185 +33,12 @@ stages:
# That's Docker in Docker # That's Docker in Docker
- dind - dind
docker_debian-8-amd64: # Docker build example
variables: #docker_debian-11-amd64:
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-11-amd64:
variables:
IMG_NAME: "debian-11-amd64"
<<: *docker_build
# GPG error
#docker_debian-11-i386:
# variables: # variables:
# IMG_NAME: "debian-11-i386" # IMG_NAME: "debian-11-amd64"
# <<: *docker_build # <<: *docker_build
docker_debian-testing-amd64:
variables:
IMG_NAME: "debian-testing-amd64"
<<: *docker_build
# GPG error
#docker_debian-testing-i386:
# variables:
# IMG_NAME: "debian-testing-i386"
# <<: *docker_build
docker_fedora-25-amd64:
variables:
IMG_NAME: "fedora-25-amd64"
<<: *docker_build
docker_fedora-26-amd64:
variables:
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_fedora-32-amd64:
variables:
IMG_NAME: "fedora-32-amd64"
<<: *docker_build
docker_fedora-33-amd64:
variables:
IMG_NAME: "fedora-33-amd64"
<<: *docker_build
docker_fedora-34-amd64:
variables:
IMG_NAME: "fedora-34-amd64"
<<: *docker_build
docker_centos-7-amd64:
variables:
IMG_NAME: "centos-7-amd64"
<<: *docker_build
docker_centos-8-amd64:
variables:
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-20_04-amd64:
variables:
IMG_NAME: "ubuntu-20.04-amd64"
<<: *docker_build
docker_ubuntu-21_10-amd64:
variables:
IMG_NAME: "ubuntu-21.10-amd64"
<<: *docker_build
# GPG error
#docker_ubuntu-21_04-amd64:
# variables:
# IMG_NAME: "ubuntu-21.04-amd64"
# <<: *docker_build
docker_opensuse-15.0-amd64:
variables:
IMG_NAME: "opensuse-15.0-amd64"
<<: *docker_build
docker_opensuse-15.1-amd64:
variables:
IMG_NAME: "opensuse-15.1-amd64"
<<: *docker_build
docker_opensuse-15.2-amd64:
variables:
IMG_NAME: "opensuse-15.2-amd64"
<<: *docker_build
docker_opensuse-15.3-amd64:
variables:
IMG_NAME: "opensuse-15.3-amd64"
<<: *docker_build
# 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
- i386
#only:
#- master
#- triggers
#- tags
.freebsd-11-amd64: &freebsd-11-amd64_env
tags:
- freebsd
- amd64
.build: &build-base .build: &build-base
stage: build stage: build
@ -416,13 +244,11 @@ build-opensuse-15.3-amd64:
# needs: [build-debian-8-i386] # needs: [build-debian-8-i386]
# image: registry.nic.cz/labs/bird:debian-8-i386 # image: registry.nic.cz/labs/bird:debian-8-i386
# Dpkg error: PATH is not set
pkg-debian-9-amd64: pkg-debian-9-amd64:
<<: *pkg-deb <<: *pkg-deb
needs: [build-debian-9-amd64] needs: [build-debian-9-amd64]
image: registry.nic.cz/labs/bird:debian-9-amd64 image: registry.nic.cz/labs/bird:debian-9-amd64
# Dpkg error: PATH is not set
pkg-debian-9-i386: pkg-debian-9-i386:
<<: *pkg-deb <<: *pkg-deb
needs: [build-debian-9-i386] needs: [build-debian-9-i386]
@ -531,6 +357,7 @@ build-birdlab:
- sudo git clean -fx - sudo git clean -fx
- git pull --ff-only - git pull --ff-only
- mv $DIR/bird $DIR/birdc netlab/common - mv $DIR/bird $DIR/birdc netlab/common
- ln -s $STAYRTR_BINARY netlab/common/stayrtr
.test: &test-base .test: &test-base
stage: test stage: test
@ -541,7 +368,7 @@ build-birdlab:
script: script:
- cd $TOOLS_DIR/netlab - cd $TOOLS_DIR/netlab
- sudo ./stop - sudo ./stop
- sudo ./runtest -m check $TEST_NAME - sudo ./runtest -s v2 -m check $TEST_NAME
test-ospf-base: test-ospf-base:
<<: *test-base <<: *test-base
@ -613,6 +440,16 @@ test-bgp-merged:
variables: variables:
TEST_NAME: cf-bgp-merged TEST_NAME: cf-bgp-merged
test-bgp-flowspec:
<<: *test-base
variables:
TEST_NAME: cf-bgp-flowspec
test-bgp-rs-multitab:
<<: *test-base
variables:
TEST_NAME: cf-bgp-rs-multitab
test-ebgp-loop: test-ebgp-loop:
<<: *test-base <<: *test-base
variables: variables:
@ -623,12 +460,32 @@ test-ebgp-star:
variables: variables:
TEST_NAME: cf-ebgp-star TEST_NAME: cf-ebgp-star
test-ebgp-role:
<<: *test-base
variables:
TEST_NAME: cf-ebgp-role
test-ebgp-graceful:
<<: *test-base
variables:
TEST_NAME: cf-ebgp-graceful
test-ebgp-import-limit:
<<: *test-base
variables:
TEST_NAME: cf-ebgp-import-limit
test-ibgp-loop: test-ibgp-loop:
<<: *test-base <<: *test-base
variables: variables:
TEST_NAME: cf-ibgp-loop TEST_NAME: cf-ibgp-loop
test-ibgp-star: test-ibgp-loop-big:
<<: *test-base
variables:
TEST_NAME: cf-ibgp-loop-big
test-ibgp-flat:
<<: *test-base <<: *test-base
variables: variables:
TEST_NAME: cf-ibgp-flat TEST_NAME: cf-ibgp-flat
@ -647,3 +504,49 @@ test-rip-base:
<<: *test-base <<: *test-base
variables: variables:
TEST_NAME: cf-rip-base TEST_NAME: cf-rip-base
test-kernel-learn:
<<: *test-base
variables:
TEST_NAME: cf-kernel-learn
.build-birdlab-base: &build-birdlab-base
stage: build
script:
- autoreconf
- ./configure
- gmake
- gmake check
build-birdlab-debian-11:
<<: *build-birdlab-base
tags:
- birdlab-debian-11
- amd64
build-birdlab-centos-08:
<<: *build-birdlab-base
tags:
- birdlab-centos-08
- amd64
build-birdlab-fedora-37:
<<: *build-birdlab-base
tags:
- birdlab-fedora-37
- amd64
build-birdlab-freebsd-13:
<<: *build-birdlab-base
tags:
- birdlab-freebsd-13
- amd64
build-birdlab-openbsd-71:
<<: *build-birdlab-base
variables:
AUTOCONF_VERSION: "2.71"
tags:
- birdlab-openbsd-71
- amd64

View file

@ -26,6 +26,7 @@ INSTALL_DATA=@INSTALL_DATA@
client=$(addprefix $(exedir)/,@CLIENT@) client=$(addprefix $(exedir)/,@CLIENT@)
daemon=$(exedir)/bird daemon=$(exedir)/bird
protocols=@protocols@ protocols=@protocols@
PROTO_BUILD := $(protocols) dev kif krt
prefix=@prefix@ prefix=@prefix@
exec_prefix=@exec_prefix@ exec_prefix=@exec_prefix@

37
NEWS
View file

@ -1,3 +1,40 @@
Version 2.0.11 (2022-11-12)
o BGP roles (RFC 9234)
o BGP: Keepalive time scaling
o BGP: New 'min hold time' and 'min keepalive time' options
o BGP: New 'next hop prefer global' option
o Filter: For loops and direct recursion
o Filter: Mixed declarations of local variables
o Filter: Improved static type checks
o Filter: Literal [] for empty set
o Linux: Netlink KRT improvements
o BSD: Experimental support for Netlink API
o Memory management improvements
o Many bugfixes
Notes:
In contrast to prior versions, configured keepalive time in BGP now scales
with negotiated hold time to maintain proportion between the keepalive time
and the hold time.
The Linux KRT was updated to use the recent API for IPv6 ECMP routes instead
of the legacy one. Consequently, the Linux versions older than 4.11 are no
longer supported, at least for IPv6 ECMP routes. Also, routing table scanning
now runs separately for each table to avoid congestion.
There is a minor change in recursive next hop processing. Previously,
recursive next hop must be resolved through a non-recursive route, now it must
be resolved through a prefix where both the best route and all routes with the
same preference (as the best route) are non-recursive. The old behavior might
lead in some corner cases to an infinite loop of recursive next hop resolution
due to a priority inversion.
There is a minor change in the 'configure undo' command, it is no longer
available after failed reconfiguration, as the old configuration is already
released.
Version 2.0.10 (2022-06-16) Version 2.0.10 (2022-06-16)
o BGP performance improvements o BGP performance improvements
o BFD: New 'strict bind' option o BFD: New 'strict bind' option

View file

@ -153,7 +153,7 @@ submit_init_command(char *cmd_raw)
if (!cmd) if (!cmd)
{ {
cleanup(); cleanup();
exit(0); exit(1);
} }
submit_server_command(cmd); submit_server_command(cmd);

View file

@ -574,6 +574,8 @@ check_eof(void)
return 0; return 0;
} }
static inline void cf_swap_soft_scope(void);
static struct symbol * static struct symbol *
cf_new_symbol(const byte *c) cf_new_symbol(const byte *c)
{ {
@ -583,6 +585,8 @@ cf_new_symbol(const byte *c)
if (l > SYM_MAX_LEN) if (l > SYM_MAX_LEN)
cf_error("Symbol too long"); cf_error("Symbol too long");
cf_swap_soft_scope();
s = cfg_allocz(sizeof(struct symbol) + l + 1); s = cfg_allocz(sizeof(struct symbol) + l + 1);
*s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, }; *s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, };
strcpy(s->name, c); strcpy(s->name, c);
@ -656,8 +660,8 @@ cf_localize_symbol(struct symbol *sym)
return sym; return sym;
/* If the scope is the current, it is already defined in this scope. */ /* If the scope is the current, it is already defined in this scope. */
if (sym->scope == conf_this_scope) if (cf_symbol_is_local(sym))
cf_error("Symbol already defined"); cf_error("Symbol '%s' already defined", sym->name);
/* Not allocated here yet, doing it now. */ /* Not allocated here yet, doing it now. */
return cf_new_symbol(sym->name); return cf_new_symbol(sym->name);
@ -713,7 +717,7 @@ cf_lex_symbol(const char *data)
static void static void
cf_lex_init_kh(void) cf_lex_init_kh(void)
{ {
HASH_INIT(kw_hash, &root_pool, KW_ORDER); HASH_INIT(kw_hash, config_pool, KW_ORDER);
struct keyword *k; struct keyword *k;
for (k=keyword_list; k->name; k++) for (k=keyword_list; k->name; k++)
@ -787,12 +791,60 @@ cf_push_scope(struct symbol *sym)
void void
cf_pop_scope(void) cf_pop_scope(void)
{ {
ASSERT(!conf_this_scope->soft_scopes);
conf_this_scope->active = 0; conf_this_scope->active = 0;
conf_this_scope = conf_this_scope->next; conf_this_scope = conf_this_scope->next;
ASSERT(conf_this_scope); ASSERT(conf_this_scope);
} }
/**
* cf_push_soft_scope - enter new soft scope
*
* If we want to enter a new anonymous scope that most likely will not contain
* any symbols, we can use cf_push_soft_scope() insteas of cf_push_scope().
* Such scope will be converted to a regular scope on first use.
*/
void
cf_push_soft_scope(void)
{
if (conf_this_scope->soft_scopes < 0xfe)
conf_this_scope->soft_scopes++;
else
cf_push_block_scope();
}
/**
* cf_pop_soft_scope - leave a soft scope
*
* Leave a soft scope entered by cf_push_soft_scope().
*/
void
cf_pop_soft_scope(void)
{
if (conf_this_scope->soft_scopes)
conf_this_scope->soft_scopes--;
else
cf_pop_block_scope();
}
/**
* cf_swap_soft_scope - convert soft scope to regular scope
*
* Soft scopes cannot hold symbols, so they must be converted to regular scopes
* on first use. It is done automatically by cf_new_symbol().
*/
static inline void
cf_swap_soft_scope(void)
{
if (conf_this_scope->soft_scopes)
{
conf_this_scope->soft_scopes--;
cf_push_block_scope();
}
}
/** /**
* cf_symbol_class_name - get name of a symbol class * cf_symbol_class_name - get name of a symbol class
* @sym: symbol * @sym: symbol

View file

@ -61,6 +61,7 @@
static jmp_buf conf_jmpbuf; static jmp_buf conf_jmpbuf;
struct config *config, *new_config; struct config *config, *new_config;
pool *config_pool;
static struct config *old_config; /* Old configuration */ static struct config *old_config; /* Old configuration */
static struct config *future_config; /* New config held here if recon requested during recon */ static struct config *future_config; /* New config held here if recon requested during recon */
@ -89,7 +90,7 @@ int undo_available; /* Undo was not requested from last reconfiguration */
struct config * struct config *
config_alloc(const char *name) config_alloc(const char *name)
{ {
pool *p = rp_new(&root_pool, "Config"); pool *p = rp_new(config_pool, "Config");
linpool *l = lp_new_default(p); linpool *l = lp_new_default(p);
struct config *c = lp_allocz(l, sizeof(struct config)); struct config *c = lp_allocz(l, sizeof(struct config));
@ -200,6 +201,23 @@ config_free(struct config *c)
rfree(c->pool); rfree(c->pool);
} }
/**
* config_free_old - free stored old configuration
*
* This function frees the old configuration (%old_config) that is saved for the
* purpose of undo. It is useful before parsing a new config when reconfig is
* requested, to avoid keeping three (perhaps memory-heavy) configs together.
*/
void
config_free_old(void)
{
tm_stop(config_timer);
undo_available = 0;
config_free(old_config);
old_config = NULL;
}
void void
config_add_obstacle(struct config *c) config_add_obstacle(struct config *c)
{ {
@ -491,10 +509,12 @@ config_timeout(timer *t UNUSED)
void void
config_init(void) config_init(void)
{ {
config_event = ev_new(&root_pool); config_pool = rp_new(&root_pool, "Configurations");
config_event = ev_new(config_pool);
config_event->hook = config_done; config_event->hook = config_done;
config_timer = tm_new(&root_pool); config_timer = tm_new(config_pool);
config_timer->hook = config_timeout; config_timer->hook = config_timeout;
} }

View file

@ -70,6 +70,7 @@ struct config *config_alloc(const char *name);
int config_parse(struct config *); int config_parse(struct config *);
int cli_parse(struct config *); int cli_parse(struct config *);
void config_free(struct config *); void config_free(struct config *);
void config_free_old(void);
int config_commit(struct config *, int type, uint timeout); int config_commit(struct config *, int type, uint timeout);
int config_confirm(void); int config_confirm(void);
int config_undo(void); int config_undo(void);
@ -96,7 +97,7 @@ void order_shutdown(int gr);
/* Pools */ /* Pools */
extern pool *config_pool;
extern linpool *cfg_mem; extern linpool *cfg_mem;
#define cfg_alloc(size) lp_alloc(cfg_mem, size) #define cfg_alloc(size) lp_alloc(cfg_mem, size)
@ -133,7 +134,9 @@ struct sym_scope {
struct sym_scope *next; /* Next on scope stack */ struct sym_scope *next; /* Next on scope stack */
struct symbol *name; /* Name of this scope */ struct symbol *name; /* Name of this scope */
uint slots; /* Variable slots */ uint slots; /* Variable slots */
int active; /* Currently entered */ byte active; /* Currently entered */
byte block; /* No independent stack frame */
byte soft_scopes; /* Number of soft scopes above */
}; };
struct bytestring { struct bytestring {
@ -190,6 +193,9 @@ struct symbol *cf_get_symbol(const byte *c);
struct symbol *cf_default_name(char *template, int *counter); struct symbol *cf_default_name(char *template, int *counter);
struct symbol *cf_localize_symbol(struct symbol *sym); struct symbol *cf_localize_symbol(struct symbol *sym);
static inline int cf_symbol_is_local(struct symbol *sym)
{ return (sym->scope == conf_this_scope) && !conf_this_scope->soft_scopes; }
/** /**
* cf_define_symbol - define meaning of a symbol * cf_define_symbol - define meaning of a symbol
* @sym: symbol to be defined * @sym: symbol to be defined
@ -213,6 +219,15 @@ struct symbol *cf_localize_symbol(struct symbol *sym);
void cf_push_scope(struct symbol *); void cf_push_scope(struct symbol *);
void cf_pop_scope(void); void cf_pop_scope(void);
void cf_push_soft_scope(void);
void cf_pop_soft_scope(void);
static inline void cf_push_block_scope(void)
{ cf_push_scope(NULL); conf_this_scope->block = 1; }
static inline void cf_pop_block_scope(void)
{ ASSERT(conf_this_scope->block); cf_pop_scope(); }
char *cf_symbol_class_name(struct symbol *sym); char *cf_symbol_class_name(struct symbol *sym);
/* Parser */ /* Parser */

View file

@ -75,6 +75,7 @@ CF_DECLS
struct f_static_attr fsa; struct f_static_attr fsa;
struct f_lval flv; struct f_lval flv;
struct f_line *fl; struct f_line *fl;
struct f_arg *fa;
const struct filter *f; const struct filter *f;
struct f_tree *e; struct f_tree *e;
struct f_trie *trie; struct f_trie *trie;
@ -152,14 +153,14 @@ conf: definition ;
definition: definition:
DEFINE symbol '=' term ';' { DEFINE symbol '=' term ';' {
struct f_val *val = cfg_allocz(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"); if (f_eval(f_linearize($4, 1), cfg_mem, val) > F_RETURN) cf_error("Runtime error");
cf_define_symbol($2, SYM_CONSTANT | val->type, val, val); cf_define_symbol($2, SYM_CONSTANT | val->type, val, val);
} }
; ;
expr: expr:
NUM NUM
| '(' term ')' { $$ = f_eval_int(f_linearize($2)); } | '(' term ')' { $$ = f_eval_int(f_linearize($2, 1)); }
| CF_SYM_KNOWN { | CF_SYM_KNOWN {
if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number constant expected"); if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number constant expected");
$$ = SYM_VAL($1).i; } $$ = SYM_VAL($1).i; }

View file

@ -180,7 +180,7 @@ flow6_opts:
flow_builder_init: flow_builder_init:
{ {
if (this_flow == NULL) if (this_flow == NULL)
this_flow = flow_builder_init(&root_pool); this_flow = flow_builder_init(config_pool); /* FIXME: This should be allocated from tmp in future */
else else
flow_builder_clear(this_flow); flow_builder_clear(this_flow);
}; };

View file

@ -238,6 +238,8 @@ else
;; ;;
openbsd*) openbsd*)
sysdesc=bsd sysdesc=bsd
CPPFLAGS="$CPPFLAGS -I/usr/local/include"
LDFLAGS="$LDFLAGS -L/usr/local/lib"
;; ;;
dragonfly*) dragonfly*)
sysdesc=bsd sysdesc=bsd

View file

@ -1113,7 +1113,7 @@ This argument can be omitted if there exists only a single instance.
Show the list of symbols defined in the configuration (names of Show the list of symbols defined in the configuration (names of
protocols, routing tables etc.). protocols, routing tables etc.).
<tag><label id="cli-show-route">show route [[for] <m/prefix/|<m/IP/] [table (<m/t/ | all)] [filter <m/f/|where <m/c/] [(export|preexport|noexport) <m/p/] [protocol <m/p/] [(stats|count)] [<m/options/]</tag> <tag><label id="cli-show-route">show route [[for] <m/prefix/|<m/IP/] [table (<m/t/ | all)] [(import|export) table <m/p/.<m/c/] [filter <m/f/|where <m/c/] [(export|preexport|noexport) <m/p/] [protocol <m/p/] [(stats|count)] [<m/options/]</tag>
Show contents of specified routing tables, that is routes, their metrics Show contents of specified routing tables, that is routes, their metrics
and (in case the <cf/all/ switch is given) all their attributes. and (in case the <cf/all/ switch is given) all their attributes.
@ -1133,6 +1133,11 @@ This argument can be omitted if there exists only a single instance.
Last, the set of default tables is used: <cf/master4/, <cf/master6/ and Last, the set of default tables is used: <cf/master4/, <cf/master6/ and
each first table of any other network type. each first table of any other network type.
<p>There are internal tables when <cf/(import|export) table/ options
are used for some channels. They can be selected explicitly with
<cf/(import|export) table/ switch, specifying protocol <m/p/ and
channel name <m/c/.
<p>You can also ask for printing only routes processed and accepted by <p>You can also ask for printing only routes processed and accepted by
a given filter (<cf>filter <m/name/</cf> or <cf>filter { <m/filter/ } a given filter (<cf>filter <m/name/</cf> or <cf>filter { <m/filter/ }
</cf> or matching a given condition (<cf>where <m/condition/</cf>). </cf> or matching a given condition (<cf>where <m/condition/</cf>).
@ -1168,6 +1173,11 @@ This argument can be omitted if there exists only a single instance.
restarted otherwise. Changes in filters usually lead to restart of restarted otherwise. Changes in filters usually lead to restart of
affected protocols. affected protocols.
The previous configuration is saved and the user can switch back to it
with <ref id="cli-configure-undo" name="configure undo"> command. The
old saved configuration is released (even if the reconfiguration attempt
fails due to e.g. a syntax error).
If <cf/soft/ option is used, changes in filters does not cause BIRD to If <cf/soft/ option is used, changes in filters does not cause BIRD to
restart affected protocols, therefore already accepted routes (according restart affected protocols, therefore already accepted routes (according
to old filters) would be still propagated, but new routes would be to old filters) would be still propagated, but new routes would be
@ -1260,8 +1270,8 @@ this:
<code> <code>
filter not_too_far filter not_too_far
int var;
{ {
int var;
if defined( rip_metric ) then if defined( rip_metric ) then
var = rip_metric; var = rip_metric;
else { else {
@ -1290,9 +1300,9 @@ local variables. Recursion is not allowed. Function definitions look like this:
<code> <code>
function name () function name ()
int local_variable;
{ {
local_variable = 5; int local_variable;
int another_variable = 5;
} }
function with_parameters (int parameter) function with_parameters (int parameter)
@ -1301,16 +1311,19 @@ function with_parameters (int parameter)
} }
</code> </code>
<p>Unlike in C, variables are declared after the <cf/function/ line, but before <p>Like in C programming language, variables are declared inside function body,
the first <cf/{/. You can't declare variables in nested blocks. Functions are either at the beginning, or mixed with other statements. Declarations may
called like in C: <cf>name(); with_parameters(5);</cf>. Function may return contain initialization. You can also declare variables in nested blocks, such
values using the <cf>return <m/[expr]/</cf> command. Returning a value exits variables have scope restricted to such block. There is a deprecated syntax to
from current function (this is similar to C). declare variables after the <cf/function/ line, but before the first <cf/{/.
Functions are called like in C: <cf>name(); with_parameters(5);</cf>. Function
may return values using the <cf>return <m/[expr]/</cf> command. Returning a
value exits from current function (this is similar to C).
<p>Filters are defined in a way similar to functions except they can't have <p>Filters are defined in a way similar to functions except they cannot have
explicit parameters. They get a route table entry as an implicit parameter, it explicit parameters. They get a route table entry as an implicit parameter, it
is also passed automatically to any functions called. The filter must terminate is also passed automatically to any functions called. The filter must terminate
with either <cf/accept/ or <cf/reject/ statement. If there's a runtime error in with either <cf/accept/ or <cf/reject/ statement. If there is a runtime error in
filter, the route is rejected. filter, the route is rejected.
<p>A nice trick to debug filters is to use <cf>show route filter <m/name/</cf> <p>A nice trick to debug filters is to use <cf>show route filter <m/name/</cf>
@ -1680,7 +1693,8 @@ prefix and an ASN as arguments.
<sect>Control structures <sect>Control structures
<label id="control-structures"> <label id="control-structures">
<p>Filters support two control structures: conditions and case switches. <p>Filters support several control structures: conditions, for loops and case
switches.
<p>Syntax of a condition is: <cf>if <M>boolean expression</M> then <m/commandT/; <p>Syntax of a condition is: <cf>if <M>boolean expression</M> then <m/commandT/;
else <m/commandF/;</cf> and you can use <cf>{ <m/command1/; <m/command2/; else <m/commandF/;</cf> and you can use <cf>{ <m/command1/; <m/command2/;
@ -1688,6 +1702,14 @@ else <m/commandF/;</cf> and you can use <cf>{ <m/command1/; <m/command2/;
omitted. If the <cf><m>boolean expression</m></cf> is true, <m/commandT/ is omitted. If the <cf><m>boolean expression</m></cf> is true, <m/commandT/ is
executed, otherwise <m/commandF/ is executed. executed, otherwise <m/commandF/ is executed.
<p>For loops allow to iterate over elements in compound data like BGP paths or
community lists. The syntax is: <cf>for [ <m/type/ ] <m/variable/ in <m/expr/
do <m/command/;</cf> and you can also use compound command like in conditions.
The expression is evaluated to a compound data, then for each element from such
data the command is executed with the item assigned to the variable. A variable
may be an existing one (when just name is used) or a locally defined (when type
and name is used). In both cases, it must have the same type as elements.
<p>The <cf>case</cf> is similar to case from Pascal. Syntax is <cf>case <p>The <cf>case</cf> is similar to case from Pascal. Syntax is <cf>case
<m/expr/ { else: | <m/num_or_prefix [ .. num_or_prefix]/: <m/statement/ ; [ <m/expr/ { else: | <m/num_or_prefix [ .. num_or_prefix]/: <m/statement/ ; [
... ] }</cf>. The expression after <cf>case</cf> can be of any type which can be ... ] }</cf>. The expression after <cf>case</cf> can be of any type which can be
@ -1700,16 +1722,21 @@ neither of the <cf/:/ clauses, the statements after <cf/else:/ are executed.
<p>Here is example that uses <cf/if/ and <cf/case/ structures: <p>Here is example that uses <cf/if/ and <cf/case/ structures:
<code> <code>
if 1234 = i then printn "."; else {
print "not 1234";
print "You need {} around multiple commands";
}
for int asn in bgp_path do {
printn "ASN: ", asn;
if asn < 65536 then print " (2B)"; else print " (4B)";
}
case arg1 { case arg1 {
2: print "two"; print "I can do more commands without {}"; 2: print "two"; print "I can do more commands without {}";
3 .. 5: print "three to five"; 3 .. 5: print "three to five";
else: print "something else"; else: print "something else";
} }
if 1234 = i then printn "."; else {
print "not 1234";
print "You need {} around multiple commands";
}
</code> </code>
@ -1762,7 +1789,7 @@ Common route attributes are:
<tag><label id="rta-source"><m/enum/ source</tag> <tag><label id="rta-source"><m/enum/ source</tag>
what protocol has told me about this route. Possible values: 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/, <cf/RTS_INHERIT/, <cf/RTS_DEVICE/,
<cf/RTS_RIP/, <cf/RTS_OSPF/, <cf/RTS_OSPF_IA/, <cf/RTS_OSPF_EXT1/, <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_OSPF_EXT2/, <cf/RTS_BGP/, <cf/RTS_PIPE/, <cf/RTS_BABEL/.
@ -1880,8 +1907,9 @@ protocol babel [<name>] {
ipv4 { <channel config> }; ipv4 { <channel config> };
ipv6 [sadr] { <channel config> }; ipv6 [sadr] { <channel config> };
randomize router id <switch>; randomize router id <switch>;
metric decay <time>;
interface <interface pattern> { interface <interface pattern> {
type <wired|wireless>; type <wired|wireless|tunnel>;
rxcost <number>; rxcost <number>;
limit <number>; limit <number>;
hello interval <time>; hello interval <time>;
@ -1894,6 +1922,11 @@ protocol babel [<name>] {
check link <switch>; check link <switch>;
next hop ipv4 <address>; next hop ipv4 <address>;
next hop ipv6 <address>; next hop ipv6 <address>;
rtt cost <number>;
rtt min <time>;
rtt max <time>;
rtt winlen <number>;
send timestamps <switch>;
authentication none|mac [permissive]; authentication none|mac [permissive];
password "&lt;text&gt;"; password "&lt;text&gt;";
password "&lt;text&gt;" { password "&lt;text&gt;" {
@ -1924,15 +1957,27 @@ protocol babel [<name>] {
router ID every time it starts up, which avoids this problem at the cost router ID every time it starts up, which avoids this problem at the cost
of not having stable router IDs in the network. Default: no. of not having stable router IDs in the network. Default: no.
<tag><label id="babel-type">type wired|wireless </tag> <tag><label id="babel-metric-decay">metric decay <m/time/ s</tag>
This option specifies the interface type: Wired or wireless. On wired The metric smoothing decay time. When route metrics vary (because of
interfaces a neighbor is considered unreachable after a small number of varying quality of a wireless link, or varying RTT when timestamps are
Hello packets are lost, as described by <cf/limit/ option. On wireless enabled), Babel applies an exponential smoothing procedure to the metric
to dampen route oscillations. This parameter specifies the half-life of
the convergence of the smoothed metric to the actual metric of the route.
I.e., the distance between the smoothed and the actual metric will be
halfed for each time period specified here (until they converge). Set to 0
to disable metric smoothing; if set, the value must be in the interval
1-180 s. Default: 4 s
<tag><label id="babel-type">type wired|wireless|tunnel </tag>
This option specifies the interface type: Wired, wireless or tunnel. On
wired interfaces a neighbor is considered unreachable after a small number
of Hello packets are lost, as described by <cf/limit/ option. On wireless
interfaces the ETX link quality estimation technique is used to compute interfaces the ETX link quality estimation technique is used to compute
the metrics of routes discovered over this interface. This technique will the metrics of routes discovered over this interface. This technique will
gradually degrade the metric of routes when packets are lost rather than gradually degrade the metric of routes when packets are lost rather than
the more binary up/down mechanism of wired type links. Default: the more binary up/down mechanism of wired type links. A tunnel is like a
<cf/wired/. wired interface, but turns on RTT-based metrics with a default cost of 96.
Default: <cf/wired/.
<tag><label id="babel-rxcost">rxcost <m/num/</tag> <tag><label id="babel-rxcost">rxcost <m/num/</tag>
This option specifies the nominal RX cost of the interface. The effective This option specifies the nominal RX cost of the interface. The effective
@ -1998,6 +2043,37 @@ protocol babel [<name>] {
source for Babel packets will be used. In normal operation, it should not source for Babel packets will be used. In normal operation, it should not
be necessary to set this option. be necessary to set this option.
<tag><label id="babel-rtt-cost">rtt cost <m/number/</tag>
The RTT-based cost that will be applied to all routes from each neighbour
based on the measured RTT to that neighbour. If this value is set,
timestamps will be included in generated Babel Hello and IHU messages, and
(if the neighbours also have timestamps enabled), the RTT to each
neighbour will be computed. An additional cost is added to a neighbour if
its RTT is above the <ref id="babel-rtt-min" name="rtt min"> value
configured on the interface. The added cost scales linearly from 0 up to
the RTT cost configured in this option; the full cost is applied if the
neighbour RTT reaches the RTT configured in the <ref id="babel-rtt-max"
name="rtt max"> option (and for all RTTs above this value). Default: 0
(disabled), except for tunnel interfaces, where it is 96.
<tag><label id="babel-rtt-min">rtt min <m/time/ s|ms</tag>
The minimum RTT above which the RTT cost will start to be applied (scaling
linearly from zero up to the full cost). Default: 10 ms
<tag><label id="babel-rtt-max">rtt max <m/time/ s|ms</tag>
The maximum RTT above which the full RTT cost will start be applied.
Default: 120 ms
<tag><label id="babel-rtt-winlen">rtt winlen <m/number/</tag>
The window length user to calculate median of the RTT
samples from each neighbour, Lower values discards old
RTT samples faster. Must be between 1 and 65535. Default: 100
<tag><label id="babel-send-timestamps">send timestamps <m/switch/</tag>
Whether to send the timestamps used for RTT calculation on this interface.
Sending the timestamps enables peers to calculate an RTT to this node,
even if no RTT cost is applied to the route metrics. Default: yes.
<tag><label id="babel-authentication">authentication none|mac [permissive]</tag> <tag><label id="babel-authentication">authentication none|mac [permissive]</tag>
Selects authentication method to be used. <cf/none/ means that packets Selects authentication method to be used. <cf/none/ means that packets
are not authenticated at all, <cf/mac/ means MAC authentication is are not authenticated at all, <cf/mac/ means MAC authentication is
@ -2360,6 +2436,7 @@ avoid routing loops.
<item> <rfc id="8203"> - BGP Administrative Shutdown Communication <item> <rfc id="8203"> - BGP Administrative Shutdown Communication
<item> <rfc id="8212"> - Default EBGP Route Propagation Behavior without Policies <item> <rfc id="8212"> - Default EBGP Route Propagation Behavior without Policies
<item> <rfc id="9117"> - Revised Validation Procedure for BGP Flow Specifications <item> <rfc id="9117"> - Revised Validation Procedure for BGP Flow Specifications
<item> <rfc id="9234"> - Route Leak Prevention and Detection Using Roles
</itemize> </itemize>
<sect1>Route selection rules <sect1>Route selection rules
@ -2722,9 +2799,16 @@ using the following configuration parameters:
<tag><label id="bgp-hold-time">hold time <m/number/</tag> <tag><label id="bgp-hold-time">hold time <m/number/</tag>
Time in seconds to wait for a Keepalive message from the other side Time in seconds to wait for a Keepalive message from the other side
before considering the connection stale. Default: depends on agreement before considering the connection stale. The effective value is
with the neighboring router, we prefer 240 seconds if the other side is negotiated during session establishment and it is a minimum of this
willing to accept it. configured value and the value proposed by the peer. The zero value has
a special meaning, signifying that no keepalives are used. Default: 240
seconds.
<tag><label id="bgp-min-hold-time">min hold time <m/number/</tag>
Minimum value of the hold time that is accepted during session negotiation.
If the peer proposes a lower value, the session is rejected with error.
Default: none.
<tag><label id="bgp-startup-hold-time">startup hold time <m/number/</tag> <tag><label id="bgp-startup-hold-time">startup hold time <m/number/</tag>
Value of the hold timer used before the routers have a chance to exchange Value of the hold timer used before the routers have a chance to exchange
@ -2732,8 +2816,15 @@ using the following configuration parameters:
<tag><label id="bgp-keepalive-time">keepalive time <m/number/</tag> <tag><label id="bgp-keepalive-time">keepalive time <m/number/</tag>
Delay in seconds between sending of two consecutive Keepalive messages. Delay in seconds between sending of two consecutive Keepalive messages.
The effective value depends on the negotiated hold time, as it is scaled
to maintain proportion between the keepalive time and the hold time.
Default: One third of the hold time. Default: One third of the hold time.
<tag><label id="bgp-min-keepalive-time">min keepalive time <m/number/</tag>
Minimum value of the keepalive time that is accepted during session
negotiation. If the proposed hold time would lead to a lower value of
the keepalive time, the session is rejected with error. Default: none.
<tag><label id="bgp-connect-delay-time">connect delay time <m/number/</tag> <tag><label id="bgp-connect-delay-time">connect delay time <m/number/</tag>
Delay in seconds between protocol startup and the first attempt to Delay in seconds between protocol startup and the first attempt to
connect. Default: 5 seconds. connect. Default: 5 seconds.
@ -2800,6 +2891,29 @@ using the following configuration parameters:
protocol itself (for example, if a route is received through eBGP and protocol itself (for example, if a route is received through eBGP and
therefore does not have such attribute). Default: 100 (0 in pre-1.2.0 therefore does not have such attribute). Default: 100 (0 in pre-1.2.0
versions of BIRD). versions of BIRD).
<tag><label id="bgp-local-role">local role <m/role-name/</tag>
BGP roles are a mechanism for route leak prevention and automatic route
filtering based on common BGP topology relationships. They are defined
in <rfc id="9234">. Instead of manually configuring filters and
communities, automatic filtering is done with the help of the OTC
attribute - a flag for routes that should be sent only to customers.
The same attribute is also used to automatically detect and filter route
leaks created by third parties.
This option is valid for EBGP sessions, but it is not recommended to be
used within AS confederations (which would require manual filtering of
<cf/bgp_otc/ attribute on confederation boundaries).
Possible <cf><m/role-name/</cf> values are: <cf/provider/,
<cf/rs_server/, <cf/rs_client/, <cf/customer/ and <cf/peer/.
Default: No local role assigned.
<tag><label id="bgp-require-roles">require roles <m/switch/</tag>
If this option is set, the BGP roles must be defined on both sides,
otherwise the session will not be established. This behavior is defined
in <rfc id="9234"> as "strict mode" and is used to enforce corresponding
configuration at your conterpart side. Default: disabled.
</descrip> </descrip>
<sect1>Channel configuration <sect1>Channel configuration
@ -2870,6 +2984,20 @@ be used in explicit configuration.
BGP session (if acceptable), or the preferred address of an associated BGP session (if acceptable), or the preferred address of an associated
interface. interface.
<tag><label id="bgp-next-hop-prefer">next hop prefer global</tag>
Prefer global IPv6 address to link-local IPv6 address for immediate next
hops of received routes. For IPv6 routes, the Next Hop attribute may
contain both a global IP address and a link-local IP address. For IBGP
sessions, the global IP address is resolved (<ref id="bgp-gateway"
name="gateway recursive">) through an IGP routing table
(<ref id="bgp-igp-table" name="igp table">) to get an immediate next
hop. If the resulting IGP route is a direct route (i.e., the next hop is
a direct neighbor), then the link-local IP address from the Next Hop
attribute is used as the immediate next hop. This option change it to
use the global IP address instead. Note that even with this option
enabled a route may end with a link-local immediate next hop when the
IGP route has one. Default: disabled.
<tag><label id="bgp-gateway">gateway direct|recursive</tag> <tag><label id="bgp-gateway">gateway direct|recursive</tag>
For received routes, their <cf/gw/ (immediate next hop) attribute is For received routes, their <cf/gw/ (immediate next hop) attribute is
computed from received <cf/bgp_next_hop/ attribute. This option computed from received <cf/bgp_next_hop/ attribute. This option
@ -2903,6 +3031,13 @@ be used in explicit configuration.
be examined later by <cf/show route/, and can be used to reconfigure be examined later by <cf/show route/, and can be used to reconfigure
import filters without full route refresh. Default: off. import filters without full route refresh. Default: off.
Note that currently the import table breaks routes with recursive
nexthops (e.g. ones from IBGP, see <ref id="bgp-gateway" name="gateway
recursive">), they are not properly updated after next hop change. For
the same reason, it also breaks re-evaluation of flowspec routes with
<ref id="bgp-validate" name="flowspec validation"> option enabled on
flowspec channels.
<tag><label id="bgp-export-table">export table <m/switch/</tag> <tag><label id="bgp-export-table">export table <m/switch/</tag>
A BGP export table contains all routes sent to given BGP neighbor, after A BGP export table contains all routes sent to given BGP neighbor, after
application of export filters. It is also called <em/Adj-RIB-Out/ in BGP application of export filters. It is also called <em/Adj-RIB-Out/ in BGP
@ -3107,6 +3242,11 @@ some of them (marked with `<tt/O/') are optional.
This attribute contains accumulated IGP metric, which is a total This attribute contains accumulated IGP metric, which is a total
distance to the destination through multiple autonomous systems. distance to the destination through multiple autonomous systems.
Currently, the attribute is not accessible from filters. Currently, the attribute is not accessible from filters.
<tag><label id="bgp-otc">int bgp_otc [O]</tag>
This attribute is defined in <rfc id="9234">. OTC is a flag that marks
routes that should be sent only to customers. If <ref id="bgp-role"
name="local Role"> is configured it set automatically.
</descrip> </descrip>
<sect1>Example <sect1>Example

View file

@ -22,6 +22,36 @@ static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
#define f_generate_complex(fi_code, da, arg) \ #define f_generate_complex(fi_code, da, arg) \
f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da) f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da)
static int
f_new_var(struct sym_scope *s)
{
/*
* - A variable is an offset on vstack from vbase.
* - Vbase is set on filter start / function call.
* - Scopes contain (non-frame) block scopes inside filter/function scope
* - Each scope knows number of vars in that scope
* - Offset is therefore a sum of 'slots' up to filter/function scope
* - New variables are added on top of vstk, so intermediate values cannot
* be there during FI_VAR_INIT. I.e. no 'var' inside 'term'.
* - Also, each f_line must always have its scope, otherwise a variable may
* be defined but not initialized if relevant f_line is not executed.
*/
int offset = s->slots++;
while (s->block)
{
s = s->next;
ASSERT(s);
offset += s->slots;
}
if (offset >= 0xff)
cf_error("Too many variables, at most 255 allowed");
return offset;
}
/* /*
* Sets and their items are during parsing handled as lists, linked * Sets and their items are during parsing handled as lists, linked
* through left ptr. The first item in a list also contains a pointer * through left ptr. The first item in a list also contains a pointer
@ -248,10 +278,6 @@ assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const
setter = f_new_inst(FI_VAR_SET, expr, lval->sym); setter = f_new_inst(FI_VAR_SET, expr, lval->sym);
getter = f_new_inst(FI_VAR_GET, lval->sym); getter = f_new_inst(FI_VAR_GET, lval->sym);
break; break;
case F_LVAL_PREFERENCE:
setter = f_new_inst(FI_PREF_SET, expr);
getter = f_new_inst(FI_PREF_GET);
break;
case F_LVAL_SA: case F_LVAL_SA:
setter = f_new_inst(FI_RTA_SET, expr, lval->sa); setter = f_new_inst(FI_RTA_SET, expr, lval->sa);
getter = f_new_inst(FI_RTA_GET, lval->sa); getter = f_new_inst(FI_RTA_GET, lval->sa);
@ -277,6 +303,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC, INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST, SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
IF, THEN, ELSE, CASE, IF, THEN, ELSE, CASE,
FOR, IN, DO,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC, TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS, FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
PREFERENCE, PREFERENCE,
@ -296,13 +323,14 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%nonassoc ELSE %nonassoc ELSE
%type <xp> cmds_int cmd_prep %type <xp> cmds_int cmd_prep
%type <x> term block cmd cmds constant constructor print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail %type <x> term cmd cmd_var cmds cmds_scoped constant constructor print_list var var_init var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
%type <fda> dynamic_attr %type <fda> dynamic_attr
%type <fsa> static_attr %type <fsa> static_attr
%type <f> filter where_filter %type <f> filter where_filter
%type <fl> filter_body function_body %type <fl> filter_body function_body
%type <flv> lvalue %type <flv> lvalue
%type <i> type function_args function_vars %type <i> type function_vars
%type <fa> function_argsn function_args
%type <ecs> ec_kind %type <ecs> ec_kind
%type <fret> break_command %type <fret> break_command
%type <i32> cnum %type <i32> cnum
@ -311,6 +339,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%type <v> set_atom switch_atom fipa %type <v> set_atom switch_atom fipa
%type <px> fprefix %type <px> fprefix
%type <t> get_cf_position %type <t> get_cf_position
%type <s> for_var
CF_GRAMMAR CF_GRAMMAR
@ -328,7 +357,7 @@ filter_def:
conf: filter_eval ; conf: filter_eval ;
filter_eval: filter_eval:
EVAL term { f_eval_int(f_linearize($2)); } EVAL term { f_eval_int(f_linearize($2, 1)); }
; ;
conf: custom_attr ; conf: custom_attr ;
@ -403,25 +432,28 @@ type:
; ;
function_argsn: function_argsn:
/* EMPTY */ /* EMPTY */ { $$ = NULL; }
| function_argsn type symbol ';' { | function_argsn type symbol ';' {
if ($3->scope->slots >= 0xfe) cf_error("Too many declarations, at most 255 allowed"); if ($3->scope->slots >= 0xfe) cf_error("Too many declarations, at most 255 allowed");
cf_define_symbol($3, SYM_VARIABLE | $2, offset, $3->scope->slots++); $$ = cfg_alloc(sizeof(struct f_arg));
$$->arg = cf_define_symbol($3, SYM_VARIABLE | $2, offset, sym_->scope->slots++);
$$->next = $1;
} }
; ;
function_args: function_args:
'(' ')' { $$ = 0; } '(' ')' { $$ = NULL; }
| '(' function_argsn type symbol ')' { | '(' function_argsn type symbol ')' {
cf_define_symbol($4, SYM_VARIABLE | $3, offset, $4->scope->slots++); $$ = cfg_alloc(sizeof(struct f_arg));
$$ = $4->scope->slots; $$->arg = cf_define_symbol($4, SYM_VARIABLE | $3, offset, sym_->scope->slots++);
$$->next = $2;
} }
; ;
function_vars: function_vars:
/* EMPTY */ { $$ = 0; } /* EMPTY */ { $$ = 0; }
| function_vars type symbol ';' { | function_vars type symbol ';' {
cf_define_symbol($3, SYM_VARIABLE | $2, offset, $3->scope->slots++); cf_define_symbol($3, SYM_VARIABLE | $2, offset, f_new_var(sym_->scope));
$$ = $1 + 1; $$ = $1 + 1;
} }
; ;
@ -433,10 +465,12 @@ filter:
cf_assert_symbol($1, SYM_FILTER); cf_assert_symbol($1, SYM_FILTER);
$$ = $1->filter; $$ = $1->filter;
} }
| filter_body { | { cf_push_scope(NULL); } filter_body {
struct filter *f = cfg_alloc(sizeof(struct filter)); struct filter *f = cfg_alloc(sizeof(struct filter));
*f = (struct filter) { .root = $1 }; *f = (struct filter) { .root = $2 };
$$ = f; $$ = f;
cf_pop_scope();
} }
; ;
@ -449,20 +483,35 @@ where_filter:
function_body: function_body:
function_vars '{' cmds '}' { function_vars '{' cmds '}' {
$$ = f_linearize($3); $$ = f_linearize($3, 0);
$$->vars = $1; $$->vars = $1;
} }
; ;
conf: function_def ; conf: function_def ;
function_def: function_def:
FUNCTION symbol { DBG( "Beginning of function %s\n", $2->name ); FUNCTION symbol {
DBG( "Beginning of function %s\n", $2->name );
$2 = cf_define_symbol($2, SYM_FUNCTION, function, NULL); $2 = cf_define_symbol($2, SYM_FUNCTION, function, NULL);
cf_push_scope($2); cf_push_scope($2);
} function_args function_body { } function_args {
DBG("Definition of function %s with %u args and %u local vars.\n", $2->name, $4, $5->vars); /* Make dummy f_line for storing function prototype */
$5->args = $4; struct f_line *dummy = cfg_allocz(sizeof(struct f_line));
$2->function = $5; $2->function = dummy;
/* Revert the args */
while ($4) {
struct f_arg *tmp = $4;
$4 = $4->next;
tmp->next = dummy->arg_list;
dummy->arg_list = tmp;
dummy->args++;
}
} function_body {
$6->args = $2->function->args;
$6->arg_list = $2->function->arg_list;
$2->function = $6;
cf_pop_scope(); cf_pop_scope();
} }
; ;
@ -473,7 +522,11 @@ cmds: /* EMPTY */ { $$ = NULL; }
| cmds_int { $$ = $1.begin; } | cmds_int { $$ = $1.begin; }
; ;
cmd_prep: cmd { cmds_scoped: { cf_push_soft_scope(); } cmds { cf_pop_soft_scope(); $$ = $2; } ;
cmd_var: var | cmd ;
cmd_prep: cmd_var {
$$.begin = $$.end = $1; $$.begin = $$.end = $1;
if ($1) if ($1)
while ($$.end->next) while ($$.end->next)
@ -495,15 +548,6 @@ cmds_int: cmd_prep
} }
; ;
block:
cmd {
$$=$1;
}
| '{' cmds '}' {
$$=$2;
}
;
/* /*
* Complex types, their bison value is struct f_val * Complex types, their bison value is struct f_val
*/ */
@ -527,7 +571,7 @@ set_atom:
| VPN_RD { $$.type = T_RD; $$.val.ec = $1; } | VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); } | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
| '(' term ')' { | '(' term ')' {
if (f_eval(f_linearize($2), cfg_mem, &($$)) > F_RETURN) cf_error("Runtime error"); if (f_eval(f_linearize($2, 1), cfg_mem, &($$)) > F_RETURN) cf_error("Runtime error");
if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type"); if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
} }
| CF_SYM_KNOWN { | CF_SYM_KNOWN {
@ -539,13 +583,13 @@ set_atom:
switch_atom: switch_atom:
NUM { $$.type = T_INT; $$.val.i = $1; } NUM { $$.type = T_INT; $$.val.i = $1; }
| '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int(f_linearize($2)); } | '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int(f_linearize($2, 1)); }
| fipa { $$ = $1; } | fipa { $$ = $1; }
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); } | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
; ;
cnum: cnum:
term { $$ = f_eval_int(f_linearize($1)); } term { $$ = f_eval_int(f_linearize($1, 1)); }
pair_item: pair_item:
'(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); } '(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); }
@ -629,19 +673,19 @@ fprefix_set:
; ;
switch_body: /* EMPTY */ { $$ = NULL; } switch_body: /* EMPTY */ { $$ = NULL; }
| switch_body switch_items ':' cmds { | switch_body switch_items ':' cmds_scoped {
/* Fill data fields */ /* Fill data fields */
struct f_tree *t; struct f_tree *t;
struct f_line *line = f_linearize($4); struct f_line *line = f_linearize($4, 0);
for (t = $2; t; t = t->left) for (t = $2; t; t = t->left)
t->data = line; t->data = line;
$$ = f_merge_items($1, $2); $$ = f_merge_items($1, $2);
} }
| switch_body ELSECOL cmds { | switch_body ELSECOL cmds_scoped {
struct f_tree *t = f_new_tree(); struct f_tree *t = f_new_tree();
t->from.type = t->to.type = T_VOID; t->from.type = t->to.type = T_VOID;
t->right = t; t->right = t;
t->data = f_linearize($3); t->data = f_linearize($3, 0);
$$ = f_merge_items($1, t); $$ = f_merge_items($1, t);
} }
; ;
@ -658,6 +702,7 @@ bgp_path:
bgp_path_tail: bgp_path_tail:
NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .asn = $1, .kind = PM_ASN, }, }); $$->next = $2; } NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .asn = $1, .kind = PM_ASN, }, }); $$->next = $2; }
| NUM DDOT NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .from = $1, .to = $3, .kind = PM_ASN_RANGE }, }); $$->next = $4; } | NUM DDOT NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .from = $1, .to = $3, .kind = PM_ASN_RANGE }, }); $$->next = $4; }
| '[' ']' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .set = NULL, .kind = PM_ASN_SET }, }); $$->next = $3; }
| '[' set_items ']' bgp_path_tail { | '[' set_items ']' bgp_path_tail {
if ($2->from.type != T_INT) cf_error("Only integer sets allowed in path mask"); if ($2->from.type != T_INT) cf_error("Only integer sets allowed in path mask");
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .set = build_tree($2), .kind = PM_ASN_SET }, }); $$->next = $4; $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .set = build_tree($2), .kind = PM_ASN_SET }, }); $$->next = $4;
@ -677,6 +722,7 @@ constant:
| fipa { $$ = f_new_inst(FI_CONSTANT, $1); } | fipa { $$ = f_new_inst(FI_CONSTANT, $1); }
| VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); } | VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); }
| net_ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); } | net_ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
| '[' ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = NULL, }); }
| '[' set_items ']' { | '[' set_items ']' {
DBG( "We've got a set here..." ); DBG( "We've got a set here..." );
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = build_tree($2), }); $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = build_tree($2), });
@ -700,27 +746,22 @@ var_list: /* EMPTY */ { $$ = NULL; }
| var_list ',' term { $$ = $3; $$->next = $1; } | var_list ',' term { $$ = $3; $$->next = $1; }
function_call: function_call:
CF_SYM_KNOWN '(' var_list ')' { CF_SYM_KNOWN '(' var_list ')'
{
if ($1->class != SYM_FUNCTION) if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really."); cf_error("You can't call something which is not a function. Really.");
struct f_inst *fc = f_new_inst(FI_CALL, $1); /* Revert the var_list */
uint args = 0; struct f_inst *args = NULL;
while ($3) { while ($3) {
args++; struct f_inst *tmp = $3;
struct f_inst *tmp = $3->next; $3 = $3->next;
$3->next = fc;
fc = $3; tmp->next = args;
$3 = tmp; args = tmp;
} }
if (args != $1->function->args) $$ = f_new_inst(FI_CALL, args, $1);
cf_error("Function call '%s' got %u arguments, need %u arguments.",
$1->name, args, $1->function->args);
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
$$->next = fc;
} }
; ;
@ -753,6 +794,7 @@ static_attr:
| IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); } | IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
| IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); } | IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); }
| WEIGHT { $$ = f_new_static_attr(T_INT, SA_WEIGHT, 0); } | WEIGHT { $$ = f_new_static_attr(T_INT, SA_WEIGHT, 0); }
| PREFERENCE { $$ = f_new_static_attr(T_INT, SA_PREF, 0); }
| GW_MPLS { $$ = f_new_static_attr(T_INT, SA_GW_MPLS, 0); } | GW_MPLS { $$ = f_new_static_attr(T_INT, SA_GW_MPLS, 0); }
; ;
@ -779,8 +821,6 @@ term:
| constant { $$ = $1; } | constant { $$ = $1; }
| constructor { $$ = $1; } | constructor { $$ = $1; }
| PREFERENCE { $$ = f_new_inst(FI_PREF_GET); }
| static_attr { $$ = f_new_inst(FI_RTA_GET, $1); } | static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
| dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); } | dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); }
@ -847,13 +887,44 @@ print_list: /* EMPTY */ { $$ = NULL; }
} }
; ;
var_init:
/* empty */ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { }); }
| '=' term { $$ = $2; }
;
var:
type symbol var_init ';' {
struct symbol *sym = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope));
$$ = f_new_inst(FI_VAR_INIT, $3, sym);
}
for_var:
type symbol { $$ = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope)); }
| CF_SYM_KNOWN { $$ = $1; cf_assert_symbol($1, SYM_VARIABLE); }
;
cmd: cmd:
IF term THEN block { '{' cmds_scoped '}' {
$$ = $2;
}
| IF term THEN cmd {
$$ = f_new_inst(FI_CONDITION, $2, $4, NULL); $$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
} }
| IF term THEN block ELSE block { | IF term THEN cmd ELSE cmd {
$$ = f_new_inst(FI_CONDITION, $2, $4, $6); $$ = f_new_inst(FI_CONDITION, $2, $4, $6);
} }
| FOR {
/* Reserve space for walk data on stack */
cf_push_block_scope();
conf_this_scope->slots += 2;
} for_var IN
/* Parse term in the parent scope */
{ conf_this_scope->active = 0; } term { conf_this_scope->active = 1; }
DO cmd {
cf_pop_block_scope();
$$ = f_new_inst(FI_FOR_INIT, $6, $3);
$$->next = f_new_inst(FI_FOR_NEXT, $3, $9);
}
| CF_SYM_KNOWN '=' term ';' { | CF_SYM_KNOWN '=' term ';' {
switch ($1->class) { switch ($1->class) {
case SYM_VARIABLE_RANGE: case SYM_VARIABLE_RANGE:
@ -878,9 +949,6 @@ cmd:
cf_error( "This static attribute is read-only."); cf_error( "This static attribute is read-only.");
$$ = f_new_inst(FI_RTA_SET, $3, $1); $$ = f_new_inst(FI_RTA_SET, $3, $1);
} }
| PREFERENCE '=' term ';' {
$$ = f_new_inst(FI_PREF_SET, $3);
}
| UNSET '(' dynamic_attr ')' ';' { | UNSET '(' dynamic_attr ')' ';' {
$$ = f_new_inst(FI_EA_UNSET, $3); $$ = f_new_inst(FI_EA_UNSET, $3);
} }
@ -923,7 +991,6 @@ get_cf_position:
lvalue: lvalue:
CF_SYM_KNOWN { cf_assert_symbol($1, SYM_VARIABLE); $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 }; } CF_SYM_KNOWN { cf_assert_symbol($1, SYM_VARIABLE); $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 }; }
| PREFERENCE { $$ = (struct f_lval) { .type = F_LVAL_PREFERENCE }; }
| static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; } | static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; }
| dynamic_attr { $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1 }; }; | dynamic_attr { $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1 }; };

View file

@ -72,6 +72,7 @@ enum f_type
f_type_element_type(enum f_type t) f_type_element_type(enum f_type t)
{ {
switch(t) { switch(t) {
case T_PATH: return T_INT;
case T_CLIST: return T_PAIR; case T_CLIST: return T_PAIR;
case T_ECLIST: return T_EC; case T_ECLIST: return T_EC;
case T_LCLIST: return T_LC; case T_LCLIST: return T_LC;
@ -79,6 +80,8 @@ f_type_element_type(enum f_type t)
}; };
} }
const struct f_trie f_const_empty_trie = { .ipv4 = -1, };
const struct f_val f_const_empty_path = { const struct f_val f_const_empty_path = {
.type = T_PATH, .type = T_PATH,
.val.ad = &null_adata, .val.ad = &null_adata,
@ -91,6 +94,9 @@ const struct f_val f_const_empty_path = {
}, f_const_empty_lclist = { }, f_const_empty_lclist = {
.type = T_LCLIST, .type = T_LCLIST,
.val.ad = &null_adata, .val.ad = &null_adata,
}, f_const_empty_prefix_set = {
.type = T_PREFIX_SET,
.val.ti = &f_const_empty_trie,
}; };
static struct adata * static struct adata *
@ -187,7 +193,7 @@ val_compare(const struct f_val *v1, const struct f_val *v2)
if (val_is_ip4(v1) && (v2->type == T_QUAD)) if (val_is_ip4(v1) && (v2->type == T_QUAD))
return uint_cmp(ipa_to_u32(v1->val.ip), v2->val.i); return uint_cmp(ipa_to_u32(v1->val.ip), v2->val.i);
debug( "Types do not match in val_compare\n" ); DBG( "Types do not match in val_compare\n" );
return F_CMP_ERROR; return F_CMP_ERROR;
} }
@ -301,6 +307,12 @@ val_same(const struct f_val *v1, const struct f_val *v2)
int int
clist_set_type(const struct f_tree *set, struct f_val *v) clist_set_type(const struct f_tree *set, struct f_val *v)
{ {
if (!set)
{
v->type = T_VOID;
return 1;
}
switch (set->from.type) switch (set->from.type)
{ {
case T_PAIR: case T_PAIR:
@ -324,7 +336,7 @@ clist_set_type(const struct f_tree *set, struct f_val *v)
} }
} }
static int int
clist_match_set(const struct adata *clist, const struct f_tree *set) clist_match_set(const struct adata *clist, const struct f_tree *set)
{ {
if (!clist) if (!clist)
@ -345,7 +357,7 @@ clist_match_set(const struct adata *clist, const struct f_tree *set)
return 0; return 0;
} }
static int int
eclist_match_set(const struct adata *list, const struct f_tree *set) eclist_match_set(const struct adata *list, const struct f_tree *set)
{ {
if (!list) if (!list)
@ -369,7 +381,7 @@ eclist_match_set(const struct adata *list, const struct f_tree *set)
return 0; return 0;
} }
static int int
lclist_match_set(const struct adata *list, const struct f_tree *set) lclist_match_set(const struct adata *list, const struct f_tree *set)
{ {
if (!list) if (!list)
@ -537,6 +549,9 @@ val_in_range(const struct f_val *v1, const struct f_val *v2)
if (v2->type != T_SET) if (v2->type != T_SET)
return F_CMP_ERROR; return F_CMP_ERROR;
if (!v2->val.t)
return 0;
/* With integrated Quad<->IP implicit conversion */ /* With integrated Quad<->IP implicit conversion */
if ((v1->type == v2->val.t->from.type) || if ((v1->type == v2->val.t->from.type) ||
((v1->type == T_QUAD) && val_is_ip4(&(v2->val.t->from)) && val_is_ip4(&(v2->val.t->to)))) ((v1->type == T_QUAD) && val_is_ip4(&(v2->val.t->from)) && val_is_ip4(&(v2->val.t->to))))

View file

@ -100,6 +100,7 @@ enum f_sa_code {
SA_IFNAME, SA_IFNAME,
SA_IFINDEX, SA_IFINDEX,
SA_WEIGHT, SA_WEIGHT,
SA_PREF,
SA_GW_MPLS, SA_GW_MPLS,
} PACKED; } PACKED;
@ -191,6 +192,7 @@ struct f_tree *f_new_tree(void);
struct f_tree *build_tree(struct f_tree *); struct f_tree *build_tree(struct f_tree *);
const struct f_tree *find_tree(const struct f_tree *t, const struct f_val *val); 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); int same_tree(const struct f_tree *t0, const struct f_tree *t2);
int tree_node_count(const struct f_tree *t);
void tree_format(const struct f_tree *t, buffer *buf); 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); void tree_walk(const struct f_tree *t, void (*hook)(const struct f_tree *, void *), void *data);
@ -279,9 +281,15 @@ int val_in_range(const struct f_val *v1, const struct f_val *v2);
int clist_set_type(const struct f_tree *set, struct f_val *v); int clist_set_type(const struct f_tree *set, struct f_val *v);
static inline int eclist_set_type(const struct f_tree *set) static inline int eclist_set_type(const struct f_tree *set)
{ return set->from.type == T_EC; } { return !set || set->from.type == T_EC; }
static inline int lclist_set_type(const struct f_tree *set) static inline int lclist_set_type(const struct f_tree *set)
{ return set->from.type == T_LC; } { return !set || set->from.type == T_LC; }
static inline int path_set_type(const struct f_tree *set)
{ return !set || set->from.type == T_INT; }
int clist_match_set(const struct adata *clist, const struct f_tree *set);
int eclist_match_set(const struct adata *list, const struct f_tree *set);
int lclist_match_set(const struct adata *list, const struct f_tree *set);
const struct adata *clist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos); const struct adata *clist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
const struct adata *eclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos); const struct adata *eclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
@ -297,7 +305,7 @@ undef_value(struct f_val v)
(v.val.ad == &null_adata); (v.val.ad == &null_adata);
} }
extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist; extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist, f_const_empty_prefix_set;
enum filter_return f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres); enum filter_return f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres);

View file

@ -32,6 +32,7 @@ m4_divert(-1)m4_dnl
# #
# 101 content of per-inst struct # 101 content of per-inst struct
# 102 constructor arguments # 102 constructor arguments
# 110 constructor attributes
# 103 constructor body # 103 constructor body
# 104 dump line item content # 104 dump line item content
# (there may be nothing in dump-line content and # (there may be nothing in dump-line content and
@ -45,6 +46,7 @@ m4_divert(-1)m4_dnl
# Here are macros to allow you to _divert to the right directions. # Here are macros to allow you to _divert to the right directions.
m4_define(FID_STRUCT_IN, `m4_divert(101)') m4_define(FID_STRUCT_IN, `m4_divert(101)')
m4_define(FID_NEW_ARGS, `m4_divert(102)') m4_define(FID_NEW_ARGS, `m4_divert(102)')
m4_define(FID_NEW_ATTRIBUTES, `m4_divert(110)')
m4_define(FID_NEW_BODY, `m4_divert(103)') m4_define(FID_NEW_BODY, `m4_divert(103)')
m4_define(FID_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])') m4_define(FID_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])')
m4_define(FID_LINEARIZE_BODY, `m4_divert(105)') m4_define(FID_LINEARIZE_BODY, `m4_divert(105)')
@ -106,15 +108,18 @@ FID_STRUCT_IN()m4_dnl
struct f_inst * f$1; struct f_inst * f$1;
FID_NEW_ARGS()m4_dnl FID_NEW_ARGS()m4_dnl
, struct f_inst * f$1 , struct f_inst * f$1
FID_NEW_ATTRIBUTES()m4_dnl
NONNULL(m4_eval($1+1))
FID_NEW_BODY()m4_dnl FID_NEW_BODY()m4_dnl
whati->f$1 = f$1; whati->f$1 = f$1;
for (const struct f_inst *child = f$1; child; child = child->next) { const struct f_inst *child$1 = f$1;
what->size += child->size; do {
what->size += child$1->size;
FID_IFCONST([[ FID_IFCONST([[
if (child->fi_code != FI_CONSTANT) if (child$1->fi_code != FI_CONSTANT)
constargs = 0; constargs = 0;
]]) ]])
} } while (child$1 = child$1->next);
FID_LINEARIZE_BODY FID_LINEARIZE_BODY
pos = linearize(dest, whati->f$1, pos); pos = linearize(dest, whati->f$1, pos);
FID_INTERPRET_BODY()') FID_INTERPRET_BODY()')
@ -186,6 +191,12 @@ if (f$1->type && f$2->type && (f$1->type != f$2->type) &&
cf_error("Arguments $1 and $2 of %s must be of the same type", f_instruction_name(what->fi_code)); cf_error("Arguments $1 and $2 of %s must be of the same type", f_instruction_name(what->fi_code));
FID_INTERPRET_BODY()') FID_INTERPRET_BODY()')
m4_define(ARG_PREFER_SAME_TYPE, `
FID_NEW_BODY()m4_dnl
if (f$1->type && f$2->type && (f$1->type != f$2->type))
(void) (f_const_promotion(f$2, f$1->type) || f_const_promotion(f$1, f$2->type));
FID_INTERPRET_BODY()')
# Executing another filter line. This replaces the recursion # Executing another filter line. This replaces the recursion
# that was needed in the former implementation. # that was needed in the former implementation.
m4_define(LINEX, `FID_INTERPRET_EXEC()LINEX_($1)FID_INTERPRET_NEW()return $1 FID_INTERPRET_BODY()') m4_define(LINEX, `FID_INTERPRET_EXEC()LINEX_($1)FID_INTERPRET_NEW()return $1 FID_INTERPRET_BODY()')
@ -210,7 +221,7 @@ whati->f$1 = f$1;
FID_DUMP_BODY()m4_dnl FID_DUMP_BODY()m4_dnl
f_dump_line(item->fl$1, indent + 1); f_dump_line(item->fl$1, indent + 1);
FID_LINEARIZE_BODY()m4_dnl FID_LINEARIZE_BODY()m4_dnl
item->fl$1 = f_linearize(whati->f$1); item->fl$1 = f_linearize(whati->f$1, $2);
FID_SAME_BODY()m4_dnl FID_SAME_BODY()m4_dnl
if (!f_same(f1->fl$1, f2->fl$1)) return 0; if (!f_same(f1->fl$1, f2->fl$1)) return 0;
FID_ITERATE_BODY()m4_dnl FID_ITERATE_BODY()m4_dnl
@ -238,9 +249,13 @@ m4_define(ERROR,
# This macro specifies result type and makes there are no conflicting definitions # This macro specifies result type and makes there are no conflicting definitions
m4_define(RESULT_TYPE, m4_define(RESULT_TYPE,
`m4_ifdef([[INST_RESULT_TYPE]], `m4_ifdef([[INST_RESULT_TYPE]],
[[m4_ifelse(INST_RESULT_TYPE,$1,,[[ERROR([[Multiple type definitons]])]])]], [[m4_ifelse(INST_RESULT_TYPE,$1,,[[ERROR([[Multiple type definitions in]] INST_NAME)]])]],
[[m4_define(INST_RESULT_TYPE,$1) RESULT_TYPE_($1)]])') [[m4_define(INST_RESULT_TYPE,$1) RESULT_TYPE_($1)]])')
m4_define(RESULT_TYPE_CHECK,
`m4_ifelse(INST_OUTVAL,0,,
[[m4_ifdef([[INST_RESULT_TYPE]],,[[ERROR([[Missing type definition in]] INST_NAME)]])]])')
m4_define(RESULT_TYPE_, ` m4_define(RESULT_TYPE_, `
FID_NEW_BODY()m4_dnl FID_NEW_BODY()m4_dnl
what->type = $1; what->type = $1;
@ -294,6 +309,7 @@ m4_define(FID_ITERATE, `FID_ZONE(10, Iteration)')
# This macro does all the code wrapping. See inline comments. # This macro does all the code wrapping. See inline comments.
m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[ m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[
RESULT_TYPE_CHECK()m4_dnl Check for defined RESULT_TYPE()
FID_ENUM()m4_dnl Contents of enum fi_code { ... } FID_ENUM()m4_dnl Contents of enum fi_code { ... }
INST_NAME(), INST_NAME(),
FID_ENUM_STR()m4_dnl Contents of const char * indexed by enum fi_code FID_ENUM_STR()m4_dnl Contents of const char * indexed by enum fi_code
@ -309,7 +325,9 @@ m4_undivert(107)m4_dnl
FID_NEW()m4_dnl Constructor and interpreter code together FID_NEW()m4_dnl Constructor and interpreter code together
FID_HIC( FID_HIC(
[[m4_dnl Public declaration of constructor in H file [[m4_dnl Public declaration of constructor in H file
struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code struct f_inst *
m4_undivert(110)m4_dnl
f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code
m4_undivert(102)m4_dnl m4_undivert(102)m4_dnl
);]], );]],
[[m4_dnl The one case in The Big Switch inside interpreter [[m4_dnl The one case in The Big Switch inside interpreter
@ -321,7 +339,9 @@ m4_undivert(102)m4_dnl
break; break;
]], ]],
[[m4_dnl Constructor itself [[m4_dnl Constructor itself
struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code struct f_inst *
m4_undivert(110)m4_dnl
f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code
m4_undivert(102)m4_dnl m4_undivert(102)m4_dnl
) )
{ {
@ -365,6 +385,7 @@ case INST_NAME(): {
#undef whati #undef whati
#undef item #undef item
dest->items[pos].fi_code = what->fi_code; dest->items[pos].fi_code = what->fi_code;
dest->items[pos].flags = what->flags;
dest->items[pos].lineno = what->lineno; dest->items[pos].lineno = what->lineno;
break; break;
} }
@ -392,6 +413,7 @@ m4_define(INST, `m4_dnl This macro is called on beginning of each instruction
INST_FLUSH()m4_dnl First, old data is flushed INST_FLUSH()m4_dnl First, old data is flushed
m4_define([[INST_NAME]], [[$1]])m4_dnl Then we store instruction name, m4_define([[INST_NAME]], [[$1]])m4_dnl Then we store instruction name,
m4_define([[INST_INVAL]], [[$2]])m4_dnl instruction input value count, m4_define([[INST_INVAL]], [[$2]])m4_dnl instruction input value count,
m4_define([[INST_OUTVAL]], [[$3]])m4_dnl instruction output value count,
m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl reset NEVER_CONSTANT trigger, m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl reset NEVER_CONSTANT trigger,
m4_undefine([[INST_RESULT_TYPE]])m4_dnl and reset RESULT_TYPE value. m4_undefine([[INST_RESULT_TYPE]])m4_dnl and reset RESULT_TYPE value.
FID_INTERPRET_BODY()m4_dnl By default, every code is interpreter code. FID_INTERPRET_BODY()m4_dnl By default, every code is interpreter code.
@ -495,6 +517,11 @@ f_const_promotion(struct f_inst *arg, enum f_type want)
return 1; return 1;
} }
else if ((c->type == T_SET) && (!c->val.t) && (want == T_PREFIX_SET)) {
*c = f_const_empty_prefix_set;
return 1;
}
return 0; return 0;
} }
@ -550,7 +577,7 @@ FID_WR_PUT(8)
} }
struct f_line * struct f_line *
f_linearize_concat(const struct f_inst * const inst[], uint count) f_linearize_concat(const struct f_inst * const inst[], uint count, uint results)
{ {
uint len = 0; uint len = 0;
for (uint i=0; i<count; i++) for (uint i=0; i<count; i++)
@ -562,6 +589,8 @@ f_linearize_concat(const struct f_inst * const inst[], uint count)
for (uint i=0; i<count; i++) for (uint i=0; i<count; i++)
out->len = linearize(out, inst[i], out->len); out->len = linearize(out, inst[i], out->len);
out->results = results;
#ifdef LOCAL_DEBUG #ifdef LOCAL_DEBUG
f_dump_line(out, 0); f_dump_line(out, 0);
#endif #endif
@ -630,6 +659,7 @@ FID_WR_PUT(4)m4_dnl
struct f_inst { struct f_inst {
struct f_inst *next; /* Next instruction */ struct f_inst *next; /* Next instruction */
enum f_instruction_code fi_code; /* Instruction code */ enum f_instruction_code fi_code; /* Instruction code */
enum f_instruction_flags flags; /* Flags, instruction-specific */
enum f_type type; /* Type of returned value, if known */ enum f_type type; /* Type of returned value, if known */
int size; /* How many instructions are underneath */ int size; /* How many instructions are underneath */
int lineno; /* Line number */ int lineno; /* Line number */

View file

@ -62,8 +62,9 @@
* m4_dnl INST(FI_NOP, in, out) { enum value, input args, output args * m4_dnl INST(FI_NOP, in, out) { enum value, input args, output args
* m4_dnl ARG(num, type); argument, its id (in data fields) and type accessible by v1, v2, v3 * m4_dnl ARG(num, type); argument, its id (in data fields) and type accessible by v1, v2, v3
* m4_dnl ARG_ANY(num); argument with no type check accessible by v1, v2, v3 * m4_dnl ARG_ANY(num); argument with no type check accessible by v1, v2, v3
* m4_dnl ARG_TYPE(num, type); just declare the type of argument
* m4_dnl VARARG; variable-length argument list; accessible by vv(i) and whati->varcount * m4_dnl VARARG; variable-length argument list; accessible by vv(i) and whati->varcount
* m4_dnl LINE(num, unused); this argument has to be converted to its own f_line * m4_dnl LINE(num, out); this argument has to be converted to its own f_line
* m4_dnl SYMBOL; symbol handed from config * m4_dnl SYMBOL; symbol handed from config
* m4_dnl STATIC_ATTR; static attribute definition * m4_dnl STATIC_ATTR; static attribute definition
* m4_dnl DYNAMIC_ATTR; dynamic attribute definition * m4_dnl DYNAMIC_ATTR; dynamic attribute definition
@ -80,10 +81,17 @@
* m4_dnl ) * m4_dnl )
* *
* m4_dnl RESULT(type, union-field, value); putting this on value stack * m4_dnl RESULT(type, union-field, value); putting this on value stack
* m4_dnl RESULT_(type, union-field, value); like RESULT(), but do not declare the type
* m4_dnl RESULT_VAL(value-struct); pass the struct f_val directly * m4_dnl RESULT_VAL(value-struct); pass the struct f_val directly
* m4_dnl RESULT_TYPE(type); just declare the type of result value
* m4_dnl RESULT_VOID; return undef * m4_dnl RESULT_VOID; return undef
* m4_dnl } * m4_dnl }
* *
* Note that runtime arguments m4_dnl (ARG*, VARARG) must be defined before
* parse-time arguments m4_dnl (LINE, SYMBOL, ...). During linearization,
* first ones move position in f_line by linearizing arguments first, while
* second ones store data to the current position.
*
* Also note that the { ... } blocks are not respected by M4 at all. * Also note that the { ... } blocks are not respected by M4 at all.
* If you get weird unmatched-brace-pair errors, check what it generated and why. * If you get weird unmatched-brace-pair errors, check what it generated and why.
* What is really considered as one instruction is not the { ... } block * What is really considered as one instruction is not the { ... } block
@ -91,6 +99,24 @@
* *
* Other code is just copied into the interpreter part. * Other code is just copied into the interpreter part.
* *
* The filter language uses a simple type system, where values have types
* (constants T_*) and also terms (instructions) are statically typed. Our
* static typing is partial (some terms do not declare types of arguments
* or results), therefore it can detect most but not all type errors and
* therefore we still have runtime type checks.
*
* m4_dnl Types of arguments are declared by macros ARG() and ARG_TYPE(),
* m4_dnl types of results are declared by RESULT() and RESULT_TYPE().
* m4_dnl Macros ARG_ANY(), RESULT_() and RESULT_VAL() do not declare types
* m4_dnl themselves, but can be combined with ARG_TYPE() / RESULT_TYPE().
*
* m4_dnl Note that types should be declared only once. If there are
* m4_dnl multiple RESULT() macros in an instruction definition, they must
* m4_dnl use the exact same expression for type, or they should be replaced
* m4_dnl by multiple RESULT_() macros and a common RESULT_TYPE() macro.
* m4_dnl See e.g. FI_EA_GET or FI_MIN instructions.
*
*
* If you are satisfied with this, you don't need to read the following * If you are satisfied with this, you don't need to read the following
* detailed description of what is really done with the instruction definitions. * detailed description of what is really done with the instruction definitions.
* *
@ -216,6 +242,37 @@
* *
* m4_dnl If you are stymied, see FI_CALL or FI_CONSTANT or just search for * m4_dnl If you are stymied, see FI_CALL or FI_CONSTANT or just search for
* m4_dnl the mentioned macros in this file to see what is happening there in wild. * m4_dnl the mentioned macros in this file to see what is happening there in wild.
*
*
* A note about soundness of the type system:
*
* A type system is sound when types of expressions are consistent with
* types of values resulting from evaluation of such expressions. Untyped
* expressions are ok, but badly typed expressions are not sound. So is
* the type system of BIRD filtering code sound? There are some points:
*
* All cases of (one) m4_dnl RESULT() macro are obviously ok, as the macro
* both declares a type and returns a value. One have to check instructions
* that use m4_dnl RESULT_TYPE() macro. There are two issues:
*
* FI_AND, FI_OR - second argument is statically checked to be T_BOOL and
* passed as result without dynamic typecheck, declared to be T_BOOL. If
* an untyped non-bool expression is used as a second argument, then
* the mismatched type is returned.
*
* FI_VAR_GET - soundness depends on consistency of declared symbol types
* and stored values. This is maintained when values are stored by
* FI_VAR_SET, but when they are stored by FI_CALL, only static checking is
* used, so when an untyped expression returning mismatched value is used
* as a function argument, then inconsistent value is stored and subsequent
* FI_VAR_GET would be unsound.
*
* Both of these issues are inconsequential, as mismatched values from
* unsound expressions will be caught by dynamic typechecks like mismatched
* values from untyped expressions.
*
* Also note that FI_CALL is the only expression without properly declared
* result type.
*/ */
/* Binary operators */ /* Binary operators */
@ -246,7 +303,7 @@
RESULT_TYPE(T_BOOL); RESULT_TYPE(T_BOOL);
if (v1.val.i) if (v1.val.i)
LINE(2,0); LINE(2,1);
else else
RESULT_VAL(v1); RESULT_VAL(v1);
} }
@ -256,7 +313,7 @@
RESULT_TYPE(T_BOOL); RESULT_TYPE(T_BOOL);
if (!v1.val.i) if (!v1.val.i)
LINE(2,0); LINE(2,1);
else else
RESULT_VAL(v1); RESULT_VAL(v1);
} }
@ -349,7 +406,7 @@
break; break;
case T_SET: case T_SET:
if (vv(i).val.t->from.type != T_INT) if (!path_set_type(vv(i).val.t))
runtime("Only integer sets allowed in path mask"); runtime("Only integer sets allowed in path mask");
pm->item[i] = (struct f_path_mask_item) { pm->item[i] = (struct f_path_mask_item) {
@ -371,12 +428,14 @@
INST(FI_NEQ, 2, 1) { INST(FI_NEQ, 2, 1) {
ARG_ANY(1); ARG_ANY(1);
ARG_ANY(2); ARG_ANY(2);
ARG_PREFER_SAME_TYPE(1, 2);
RESULT(T_BOOL, i, !val_same(&v1, &v2)); RESULT(T_BOOL, i, !val_same(&v1, &v2));
} }
INST(FI_EQ, 2, 1) { INST(FI_EQ, 2, 1) {
ARG_ANY(1); ARG_ANY(1);
ARG_ANY(2); ARG_ANY(2);
ARG_PREFER_SAME_TYPE(1, 2);
RESULT(T_BOOL, i, val_same(&v1, &v2)); RESULT(T_BOOL, i, val_same(&v1, &v2));
} }
@ -447,6 +506,18 @@
RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip)); RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip));
} }
INST(FI_VAR_INIT, 1, 0) {
NEVER_CONSTANT;
ARG_ANY(1);
SYMBOL;
ARG_TYPE(1, sym->class & 0xff);
/* New variable is always the last on stack */
uint pos = curline.vbase + sym->offset;
fstk->vstk[pos] = v1;
fstk->vcnt = pos + 1;
}
/* Set to indirect value prepared in v1 */ /* Set to indirect value prepared in v1 */
INST(FI_VAR_SET, 1, 0) { INST(FI_VAR_SET, 1, 0) {
NEVER_CONSTANT; NEVER_CONSTANT;
@ -477,12 +548,100 @@
RESULT_VAL(val); RESULT_VAL(val);
} }
INST(FI_FOR_INIT, 1, 0) {
NEVER_CONSTANT;
ARG_ANY(1);
SYMBOL;
FID_NEW_BODY()
ASSERT((sym->class & ~0xff) == SYM_VARIABLE);
/* Static type check */
if (f1->type)
{
enum f_type t_var = (sym->class & 0xff);
enum f_type t_arg = f_type_element_type(f1->type);
if (!t_arg)
cf_error("Value of expression in FOR must be iterable, got %s",
f_type_name(f1->type));
if (t_var != t_arg)
cf_error("Loop variable '%s' in FOR must be %s, is %s",
sym->name, f_type_name(t_arg), f_type_name(t_var));
}
FID_INTERPRET_BODY()
/* Dynamic type check */
if ((sym->class & 0xff) != f_type_element_type(v1.type))
runtime("Mismatched argument and variable type");
/* Setup the index */
v2 = (struct f_val) { .type = T_INT, .val.i = 0 };
/* Keep v1 and v2 on the stack */
fstk->vcnt += 2;
}
INST(FI_FOR_NEXT, 2, 0) {
NEVER_CONSTANT;
SYMBOL;
/* Type checks are done in FI_FOR_INIT */
/* Loop variable */
struct f_val *var = &fstk->vstk[curline.vbase + sym->offset];
int step = 0;
switch(v1.type)
{
case T_PATH:
var->type = T_INT;
step = as_path_walk(v1.val.ad, &v2.val.i, &var->val.i);
break;
case T_CLIST:
var->type = T_PAIR;
step = int_set_walk(v1.val.ad, &v2.val.i, &var->val.i);
break;
case T_ECLIST:
var->type = T_EC;
step = ec_set_walk(v1.val.ad, &v2.val.i, &var->val.ec);
break;
case T_LCLIST:
var->type = T_LC;
step = lc_set_walk(v1.val.ad, &v2.val.i, &var->val.lc);
break;
default:
runtime( "Clist or lclist expected" );
}
if (step)
{
/* Keep v1 and v2 on the stack */
fstk->vcnt += 2;
/* Repeat this instruction */
curline.pos--;
/* Execute the loop body */
LINE(1, 0);
/* Space for loop variable, may be unused */
fstk->vcnt += 1;
}
else
var->type = T_VOID;
}
INST(FI_CONDITION, 1, 0) { INST(FI_CONDITION, 1, 0) {
ARG(1, T_BOOL); ARG(1, T_BOOL);
if (v1.val.i) if (v1.val.i)
LINE(2,0); LINE(2,0);
else else
LINE(3,1); LINE(3,0);
} }
INST(FI_PRINT, 0, 0) { INST(FI_PRINT, 0, 0) {
@ -526,13 +685,14 @@
case SA_FROM: RESULT(sa.f_type, ip, rta->from); break; case SA_FROM: RESULT(sa.f_type, ip, rta->from); break;
case SA_GW: RESULT(sa.f_type, ip, rta->nh.gw); break; case SA_GW: RESULT(sa.f_type, ip, rta->nh.gw); break;
case SA_NET: RESULT(sa.f_type, net, (*fs->rte)->net->n.addr); break; case SA_NET: RESULT(sa.f_type, net, (*fs->rte)->net->n.addr); break;
case SA_PROTO: RESULT(sa.f_type, s, rta->src->proto->name); break; case SA_PROTO: RESULT(sa.f_type, s, (*fs->rte)->src->proto->name); break;
case SA_SOURCE: RESULT(sa.f_type, i, rta->source); break; case SA_SOURCE: RESULT(sa.f_type, i, rta->source); break;
case SA_SCOPE: RESULT(sa.f_type, i, rta->scope); break; case SA_SCOPE: RESULT(sa.f_type, i, rta->scope); break;
case SA_DEST: RESULT(sa.f_type, i, rta->dest); break; 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_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_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; case SA_WEIGHT: RESULT(sa.f_type, i, rta->nh.weight + 1); break;
case SA_PREF: RESULT(sa.f_type, i, rta->pref); break;
case SA_GW_MPLS: RESULT(sa.f_type, i, rta->nh.labels ? rta->nh.label[0] : MPLS_NULL); break; case SA_GW_MPLS: RESULT(sa.f_type, i, rta->nh.labels ? rta->nh.label[0] : MPLS_NULL); break;
default: default:
@ -561,7 +721,7 @@
{ {
ip_addr ip = v1.val.ip; ip_addr ip = v1.val.ip;
struct iface *ifa = ipa_is_link_local(ip) ? rta->nh.iface : NULL; struct iface *ifa = ipa_is_link_local(ip) ? rta->nh.iface : NULL;
neighbor *n = neigh_find(rta->src->proto, ip, ifa, 0); neighbor *n = neigh_find((*fs->rte)->src->proto, ip, ifa, 0);
if (!n || (n->scope == SCOPE_HOST)) if (!n || (n->scope == SCOPE_HOST))
runtime( "Invalid gw address" ); runtime( "Invalid gw address" );
@ -637,6 +797,10 @@
} }
break; break;
case SA_PREF:
rta->pref = v1.val.i;
break;
default: default:
bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code); bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
} }
@ -709,9 +873,6 @@
case EAF_TYPE_LC_SET: case EAF_TYPE_LC_SET:
RESULT_(T_LCLIST, ad, e->u.ptr); RESULT_(T_LCLIST, ad, e->u.ptr);
break; break;
case EAF_TYPE_UNDEF:
RESULT_VOID;
break;
default: default:
bug("Unknown dynamic attribute type"); bug("Unknown dynamic attribute type");
} }
@ -732,7 +893,10 @@
l->count = 1; l->count = 1;
l->attrs[0].id = da.ea_code; l->attrs[0].id = da.ea_code;
l->attrs[0].flags = 0; l->attrs[0].flags = 0;
l->attrs[0].type = da.type | EAF_ORIGINATED | EAF_FRESH; l->attrs[0].type = da.type;
l->attrs[0].originated = 1;
l->attrs[0].fresh = 1;
l->attrs[0].undef = 0;
switch (da.type) { switch (da.type) {
case EAF_TYPE_INT: case EAF_TYPE_INT:
@ -787,35 +951,8 @@
ACCESS_RTE; ACCESS_RTE;
ACCESS_EATTRS; ACCESS_EATTRS;
{ f_rta_cow(fs);
struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr)); ea_unset_attr(fs->eattrs, fs->pool, 1, da.ea_code);
l->next = NULL;
l->flags = EALF_SORTED;
l->count = 1;
l->attrs[0].id = da.ea_code;
l->attrs[0].flags = 0;
l->attrs[0].type = EAF_TYPE_UNDEF | EAF_ORIGINATED | EAF_FRESH;
l->attrs[0].u.data = 0;
f_rta_cow(fs);
l->next = *fs->eattrs;
*fs->eattrs = l;
}
}
INST(FI_PREF_GET, 0, 1) {
ACCESS_RTE;
RESULT(T_INT, i, (*fs->rte)->pref);
}
INST(FI_PREF_SET, 1, 0) {
ACCESS_RTE;
ARG(1,T_INT);
if (v1.val.i > 0xFFFF)
runtime( "Setting preference value out of bounds" );
f_rte_cow(fs);
(*fs->rte)->pref = v1.val.i;
} }
INST(FI_LENGTH, 1, 1) { /* Get length of */ INST(FI_LENGTH, 1, 1) { /* Get length of */
@ -983,7 +1120,7 @@
RESULT(T_INT, i, v1.val.lc.ldp2); RESULT(T_INT, i, v1.val.lc.ldp2);
} }
INST(FI_MIN, 1, 1) { /* Get minimum element from set */ INST(FI_MIN, 1, 1) { /* Get minimum element from list */
ARG_ANY(1); ARG_ANY(1);
RESULT_TYPE(f_type_element_type(v1.type)); RESULT_TYPE(f_type_element_type(v1.type));
switch(v1.type) switch(v1.type)
@ -1017,7 +1154,7 @@
} }
} }
INST(FI_MAX, 1, 1) { /* Get maximum element from set */ INST(FI_MAX, 1, 1) { /* Get maximum element from list */
ARG_ANY(1); ARG_ANY(1);
RESULT_TYPE(f_type_element_type(v1.type)); RESULT_TYPE(f_type_element_type(v1.type));
switch(v1.type) switch(v1.type)
@ -1051,7 +1188,7 @@
} }
} }
INST(FI_RETURN, 1, 1) { INST(FI_RETURN, 1, 0) {
NEVER_CONSTANT; NEVER_CONSTANT;
/* Acquire the return value */ /* Acquire the return value */
ARG_ANY(1); ARG_ANY(1);
@ -1079,28 +1216,59 @@
INST(FI_CALL, 0, 1) { INST(FI_CALL, 0, 1) {
NEVER_CONSTANT; NEVER_CONSTANT;
VARARG;
SYMBOL; SYMBOL;
/* Fake result type declaration */
RESULT_TYPE(T_VOID);
FID_NEW_BODY()
ASSERT(sym->class == SYM_FUNCTION);
if (whati->varcount != sym->function->args)
cf_error("Function '%s' expects %u arguments, got %u arguments",
sym->name, sym->function->args, whati->varcount);
/* Typecheck individual arguments */
struct f_inst *a = fvar;
struct f_arg *b = sym->function->arg_list;
for (uint i = 1; a && b; a = a->next, b = b->next, i++)
{
enum f_type b_type = b->arg->class & 0xff;
if (a->type && (a->type != b_type) && !f_const_promotion(a, b_type))
cf_error("Argument %u of '%s' must be %s, got %s",
i, sym->name, f_type_name(b_type), f_type_name(a->type));
}
ASSERT(!a && !b);
/* Add implicit void slot for the return value */
struct f_inst *tmp = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
tmp->next = whati->fvar;
whati->fvar = tmp;
what->size += tmp->size;
/* Mark recursive calls, they have dummy f_line */
if (!sym->function->len)
what->flags |= FIF_RECURSIVE;
FID_SAME_BODY() FID_SAME_BODY()
if (!(f1->sym->flags & SYM_FLAG_SAME)) if (!(f1->sym->flags & SYM_FLAG_SAME) && !(f1_->flags & FIF_RECURSIVE))
return 0; return 0;
FID_ITERATE_BODY() FID_ITERATE_BODY()
if (!(what->flags & FIF_RECURSIVE))
BUFFER_PUSH(fit->lines) = whati->sym->function; BUFFER_PUSH(fit->lines) = whati->sym->function;
FID_INTERPRET_BODY() FID_INTERPRET_BODY()
/* Push the body on stack */ /* Push the body on stack */
LINEX(sym->function); LINEX(sym->function);
curline.vbase = curline.ventry;
curline.emask |= FE_RETURN; curline.emask |= FE_RETURN;
/* Before this instruction was called, there was the T_VOID /* Arguments on stack */
* automatic return value pushed on value stack and also fstk->vcnt += sym->function->args;
* sym->function->args function arguments. Setting the
* vbase to point to first argument. */
ASSERT(curline.ventry >= sym->function->args);
curline.ventry -= sym->function->args;
curline.vbase = curline.ventry;
/* Storage for local variables */ /* Storage for local variables */
memset(&(fstk->vstk[fstk->vcnt]), 0, sizeof(struct f_val) * sym->function->vars); memset(&(fstk->vstk[fstk->vcnt]), 0, sizeof(struct f_val) * sym->function->vars);
@ -1213,17 +1381,10 @@
if (v1.type == T_PATH) if (v1.type == T_PATH)
{ {
const struct f_tree *set = NULL; if ((v2.type == T_SET) && path_set_type(v2.val.t) || (v2.type == T_INT))
u32 key = 0; RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
if (v2.type == T_INT)
key = v2.val.i;
else if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT))
set = v2.val.t;
else else
runtime("Can't delete non-integer (set)"); runtime("Can't delete non-integer (set)");
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, set, key, 0) ]]);
} }
else if (v1.type == T_CLIST) else if (v1.type == T_CLIST)
@ -1275,10 +1436,8 @@
if (v1.type == T_PATH) if (v1.type == T_PATH)
{ {
u32 key = 0; if ((v2.type == T_SET) && path_set_type(v2.val.t))
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]);
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) ]]);
else else
runtime("Can't filter integer"); runtime("Can't filter integer");
} }
@ -1368,7 +1527,7 @@
} }
INST(FI_FORMAT, 1, 0) { /* Format */ INST(FI_FORMAT, 1, 1) { /* Format */
ARG_ANY(1); ARG_ANY(1);
RESULT(T_STRING, s, val_format_str(fpool, &v1)); RESULT(T_STRING, s, val_format_str(fpool, &v1));
} }

View file

@ -22,7 +22,7 @@
/* Flags for instructions */ /* Flags for instructions */
enum f_instruction_flags { enum f_instruction_flags {
FIF_PRINTED = 1, /* FI_PRINT_AND_DIE: message put in buffer */ FIF_RECURSIVE = 1, /* FI_CALL: function is directly recursive */
} PACKED; } PACKED;
/* Include generated filter instruction declarations */ /* Include generated filter instruction declarations */
@ -35,19 +35,26 @@ const char *f_instruction_name_(enum f_instruction_code fi);
static inline 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; } { return f_instruction_name_(fi) + 3; }
struct f_arg {
struct symbol *arg;
struct f_arg *next;
};
/* Filter structures for execution */ /* Filter structures for execution */
/* Line of instructions to be unconditionally executed one after another */ /* Line of instructions to be unconditionally executed one after another */
struct f_line { struct f_line {
uint len; /* Line length */ uint len; /* Line length */
u8 args; /* Function: Args required */ u8 args; /* Function: Args required */
u8 vars; u8 vars;
u8 results; /* Results left on stack: cmd -> 0, term -> 1 */
struct f_arg *arg_list;
struct f_line_item items[0]; /* The items themselves */ struct f_line_item items[0]; /* The items themselves */
}; };
/* Convert the f_inst infix tree to the f_line structures */ /* Convert the f_inst infix tree to the f_line structures */
struct f_line *f_linearize_concat(const struct f_inst * const inst[], uint count); struct f_line *f_linearize_concat(const struct f_inst * const inst[], uint count, uint results);
static inline struct f_line *f_linearize(const struct f_inst *root) static inline struct f_line *f_linearize(const struct f_inst *root, uint results)
{ return f_linearize_concat(&root, 1); } { return f_linearize_concat(&root, 1, results); }
void f_dump_line(const struct f_line *, uint indent); void f_dump_line(const struct f_line *, uint indent);

View file

@ -37,7 +37,7 @@ struct filter *f_new_where(struct f_inst *where)
f_new_inst(FI_DIE, F_REJECT)); f_new_inst(FI_DIE, F_REJECT));
struct filter *f = cfg_allocz(sizeof(struct filter)); struct filter *f = cfg_allocz(sizeof(struct filter));
f->root = f_linearize(cond); f->root = f_linearize(cond, 0);
return f; return f;
} }
@ -128,11 +128,11 @@ ca_lookup(pool *p, const char *name, int f_type)
static int inited = 0; static int inited = 0;
if (!inited) { if (!inited) {
idm_init(&ca_idm, &root_pool, 8); idm_init(&ca_idm, config_pool, 8);
HASH_INIT(ca_hash, &root_pool, CA_ORDER); HASH_INIT(ca_hash, config_pool, CA_ORDER);
ca_storage_max = 256; ca_storage_max = 256;
ca_storage = mb_allocz(&root_pool, sizeof(struct ca_storage *) * ca_storage_max); ca_storage = mb_allocz(config_pool, sizeof(struct ca_storage *) * ca_storage_max);
inited++; inited++;
} }
@ -152,7 +152,7 @@ ca_lookup(pool *p, const char *name, int f_type)
ca_storage = mb_realloc(ca_storage, sizeof(struct ca_storage *) * ca_storage_max * 2); ca_storage = mb_realloc(ca_storage, sizeof(struct ca_storage *) * ca_storage_max * 2);
} }
cas = mb_allocz(&root_pool, sizeof(struct ca_storage) + strlen(name) + 1); cas = mb_allocz(config_pool, sizeof(struct ca_storage) + strlen(name) + 1);
cas->fda = f_new_dynamic_attr(ea_type, f_type, EA_CUSTOM(id)); cas->fda = f_new_dynamic_attr(ea_type, f_type, EA_CUSTOM(id));
cas->uc = 1; cas->uc = 1;

View file

@ -215,8 +215,7 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
} }
/* End of current line. Drop local variables before exiting. */ /* End of current line. Drop local variables before exiting. */
fstk->vcnt -= curline.line->vars; fstk->vcnt = curline.ventry + curline.line->results;
fstk->vcnt -= curline.line->args;
fstk->ecnt--; fstk->ecnt--;
} }

View file

@ -46,9 +46,7 @@ run_function(const void *arg)
if (t->cmp) if (t->cmp)
return t->result == f_same(t->fn, t->cmp); return t->result == f_same(t->fn, t->cmp);
linpool *tmp = lp_new_default(&root_pool); enum filter_return fret = f_eval(t->fn, tmp_linpool, NULL);
enum filter_return fret = f_eval(t->fn, tmp, NULL);
rfree(tmp);
return (fret < F_REJECT); return (fret < F_REJECT);
} }

View file

@ -44,9 +44,8 @@ bt_test_same(onef, twof, 0);
*/ */
function t_bool() function t_bool()
bool b;
{ {
b = true; bool b = true;
bt_assert(b); bt_assert(b);
bt_assert(!!b); bt_assert(!!b);
@ -82,12 +81,11 @@ define xyzzy = (120+10);
define '1a-a1' = (xyzzy-100); define '1a-a1' = (xyzzy-100);
function t_int() function t_int()
int i;
{ {
bt_assert(xyzzy = 130); bt_assert(xyzzy = 130);
bt_assert('1a-a1' = 30); bt_assert('1a-a1' = 30);
i = four; int i = four;
i = 12*100 + 60/2 + i; i = 12*100 + 60/2 + i;
i = (i + 0); i = (i + 0);
bt_assert(i = 1234); bt_assert(i = 1234);
@ -128,14 +126,19 @@ define is2 = [(17+2), 17, 15, 11, 8, 5, 3, 2];
define is3 = [5, 17, 2, 11, 8, 15, 3, 19]; define is3 = [5, 17, 2, 11, 8, 15, 3, 19];
function t_int_set() function t_int_set()
int set is;
{ {
int set is = [];
bt_assert(is = []);
bt_assert(0 !~ is);
bt_assert(1 ~ [1,2,3]); bt_assert(1 ~ [1,2,3]);
bt_assert(5 ~ [1..20]); bt_assert(5 ~ [1..20]);
bt_assert(2 ~ [ 1, 2, 3 ]); bt_assert(2 ~ [ 1, 2, 3 ]);
bt_assert(5 ~ [ 4 .. 7 ]); bt_assert(5 ~ [ 4 .. 7 ]);
bt_assert(1 !~ [ 2, 3, 4 ]); bt_assert(1 !~ [ 2, 3, 4 ]);
bt_assert(999 !~ [ 666, 333 ]); bt_assert(999 !~ [ 666, 333 ]);
bt_assert(1 !~ []);
bt_assert(1 !~ is);
is = [ 2, 3, 4, 7..11 ]; is = [ 2, 3, 4, 7..11 ];
bt_assert(10 ~ is); bt_assert(10 ~ is);
@ -170,6 +173,7 @@ int set is;
bt_assert([1,4..10,20] = [1,4..10,20]); bt_assert([1,4..10,20] = [1,4..10,20]);
bt_assert(format([ 1, 2, 1, 1, 1, 3, 4, 1, 1, 1, 5 ]) = "[1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5]"); bt_assert(format([ 1, 2, 1, 1, 1, 3, 4, 1, 1, 1, 5 ]) = "[1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5]");
bt_assert(format([]) = "[]");
} }
bt_test_suite(t_int_set, "Testing sets of integers"); bt_test_suite(t_int_set, "Testing sets of integers");
@ -183,9 +187,8 @@ bt_test_suite(t_int_set, "Testing sets of integers");
*/ */
function t_string() function t_string()
string st;
{ {
st = "Hello"; string st = "Hello";
bt_assert(format(st) = "Hello"); bt_assert(format(st) = "Hello");
bt_assert(st ~ "Hell*"); bt_assert(st ~ "Hell*");
bt_assert(st ~ "?ello"); bt_assert(st ~ "?ello");
@ -210,9 +213,8 @@ function 'mkpair-a'(int a)
} }
function t_pair() function t_pair()
pair pp;
{ {
pp = (1, 2); pair pp = (1, 2);
bt_assert(format(pp) = "(1,2)"); bt_assert(format(pp) = "(1,2)");
bt_assert((1,2) = pp); bt_assert((1,2) = pp);
bt_assert((1,1+1) = pp); bt_assert((1,1+1) = pp);
@ -233,10 +235,11 @@ bt_test_suite(t_pair, "Testing pairs");
*/ */
function t_pair_set() function t_pair_set()
pair pp;
pair set ps;
{ {
pp = (1, 2); pair pp = (1, 2);
pair set ps = [];
bt_assert(pp !~ ps);
ps = [(1,(one+one)), (3,4)..(4,8), (5,*), (6,3..6)]; ps = [(1,(one+one)), (3,4)..(4,8), (5,*), (6,3..6)];
bt_assert(format(ps) = "[(1,2), (3,4)..(4,8), (5,0)..(5,65535), (6,3)..(6,6)]"); bt_assert(format(ps) = "[(1,2), (3,4)..(4,8), (5,0)..(5,65535), (6,3)..(6,6)]");
bt_assert(pp ~ ps); bt_assert(pp ~ ps);
@ -253,6 +256,7 @@ pair set ps;
bt_assert((6,6+one) !~ ps); bt_assert((6,6+one) !~ ps);
bt_assert(((one+6),2) !~ ps); bt_assert(((one+6),2) !~ ps);
bt_assert((1,1) !~ ps); bt_assert((1,1) !~ ps);
bt_assert(pp !~ []);
ps = [(20..150, 200..300), (50100..50200, 1000..50000), (*, 5+5)]; ps = [(20..150, 200..300), (50100..50200, 1000..50000), (*, 5+5)];
bt_assert((100,200) ~ ps); bt_assert((100,200) ~ ps);
@ -304,6 +308,7 @@ quad qq;
qq = 1.2.3.4; qq = 1.2.3.4;
bt_assert(qq ~ [1.2.3.4, 5.6.7.8]); bt_assert(qq ~ [1.2.3.4, 5.6.7.8]);
bt_assert(qq !~ [1.2.1.1, 1.2.3.5]); bt_assert(qq !~ [1.2.1.1, 1.2.3.5]);
bt_assert(qq !~ []);
} }
bt_test_suite(t_quad_set, "Testing sets of quads"); bt_test_suite(t_quad_set, "Testing sets of quads");
@ -384,6 +389,7 @@ ip set ips;
bt_assert(1.2.3.4 !~ [ 1.2.3.3, 1.2.3.5 ]); bt_assert(1.2.3.4 !~ [ 1.2.3.3, 1.2.3.5 ]);
bt_assert(1.2.3.4 ~ [ 1.2.3.3..1.2.3.5 ]); bt_assert(1.2.3.4 ~ [ 1.2.3.3..1.2.3.5 ]);
bt_assert(1.2.3.4 !~ []);
} }
bt_test_suite(t_ip_set, "Testing sets of ip address"); bt_test_suite(t_ip_set, "Testing sets of ip address");
@ -398,7 +404,6 @@ bt_test_suite(t_ip_set, "Testing sets of ip address");
function t_enum() function t_enum()
{ {
bt_assert(format(RTS_DUMMY) = "(enum 30)0");
bt_assert(format(RTS_STATIC) = "(enum 30)1"); bt_assert(format(RTS_STATIC) = "(enum 30)1");
bt_assert(format(NET_IP4) = "(enum 36)1"); bt_assert(format(NET_IP4) = "(enum 36)1");
bt_assert(format(NET_VPN6) = "(enum 36)4"); bt_assert(format(NET_VPN6) = "(enum 36)4");
@ -473,13 +478,34 @@ function test_pxset(prefix set pxs)
bt_assert(1.0.0.0/8 ~ [ 1.0.0.0/8+ ]); bt_assert(1.0.0.0/8 ~ [ 1.0.0.0/8+ ]);
bt_assert(1.0.0.0/9 !~ [ 1.0.0.0/8- ]); bt_assert(1.0.0.0/9 !~ [ 1.0.0.0/8- ]);
bt_assert(1.2.0.0/17 !~ [ 1.0.0.0/8{ 15 , 16 } ]); bt_assert(1.2.0.0/17 !~ [ 1.0.0.0/8{ 15 , 16 } ]);
bt_assert(net10 !~ []);
bt_assert([ 10.0.0.0/8{ 15 , 17 } ] = [ 10.0.0.0/8{ 15 , 17 } ]); bt_assert([ 10.0.0.0/8{ 15 , 17 } ] = [ 10.0.0.0/8{ 15 , 17 } ]);
} }
function test_empty_pxset(prefix set pxs)
int set s0;
prefix set s1;
{
s0 = [];
s1 = [];
bt_assert(pxs != s0);
bt_assert(pxs = s1);
bt_assert(pxs = []);
}
function t_prefix_set() function t_prefix_set()
prefix set pxs; prefix set pxs;
{ {
pxs = [];
bt_assert(format(pxs) = "[]");
bt_assert(pxs = []);
bt_assert(1.2.0.0/16 !~ []);
bt_assert(1.2.0.0/16 !~ pxs);
test_empty_pxset([]);
test_empty_pxset(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} ]; 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/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(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}]");
@ -564,6 +590,12 @@ bt_test_suite(t_prefix6, "Testing prefix IPv6");
function t_prefix6_set() function t_prefix6_set()
prefix set pxs; prefix set pxs;
{ {
pxs = [];
bt_assert(format(pxs) = "[]");
bt_assert(pxs = []);
bt_assert(12::34/128 !~ []);
bt_assert(12::34/128 !~ pxs);
bt_assert(1180::/16 ~ [ 1100::/8{15, 17} ]); bt_assert(1180::/16 ~ [ 1100::/8{15, 17} ]);
bt_assert(12::34 = 12::34); bt_assert(12::34 = 12::34);
bt_assert(12::34 ~ [ 12::33..12::35 ]); bt_assert(12::34 ~ [ 12::33..12::35 ]);
@ -681,6 +713,7 @@ int set set12;
bt_assert(3 ~ p2); bt_assert(3 ~ p2);
bt_assert(p2 ~ [2, 10..20]); bt_assert(p2 ~ [2, 10..20]);
bt_assert(p2 ~ [4, 10..20]); bt_assert(p2 ~ [4, 10..20]);
bt_assert(p2 !~ []);
p2 = prepend(p2, 5); p2 = prepend(p2, 5);
bt_assert(p2 !~ pm1); bt_assert(p2 !~ pm1);
@ -691,6 +724,8 @@ int set set12;
bt_assert(p2 ~ [= 5 [2, 4, 6] 3 [1..2] 1 =]); bt_assert(p2 ~ [= 5 [2, 4, 6] 3 [1..2] 1 =]);
bt_assert(p2 ~ [= 5 set35 3 set12 set12 =]); bt_assert(p2 ~ [= 5 set35 3 set12 set12 =]);
bt_assert(p2 ~ mkpath(5, 4)); bt_assert(p2 ~ mkpath(5, 4));
bt_assert(p2 ~ [= * [3] * =]);
bt_assert(p2 !~ [= * [] * =]);
bt_assert(p2.len = 5); bt_assert(p2.len = 5);
bt_assert(p2.first = 5); bt_assert(p2.first = 5);
@ -699,6 +734,10 @@ int set set12;
bt_assert(p2.len = 5); bt_assert(p2.len = 5);
bt_assert(delete(p2, 3) = prepend(prepend(prepend(prepend(+empty+, 1), 2), 4), 5)); 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)); bt_assert(filter(p2, [1..3]) = prepend(prepend(prepend(+empty+, 1), 2), 3));
bt_assert(delete(p2, []) = p2);
bt_assert(filter(p2, []) = +empty+);
bt_assert(delete(prepend(prepend(+empty+, 0), 1), []) = prepend(prepend(+empty+, 0), 1));
bt_assert(filter(prepend(prepend(+empty+, 0), 1), []) = +empty+);
p2 = prepend( + empty +, 5 ); p2 = prepend( + empty +, 5 );
p2 = prepend( p2, 4 ); p2 = prepend( p2, 4 );
@ -718,6 +757,15 @@ int set set12;
bt_assert(delete(p2, [4..5]) = prepend(prepend(prepend(prepend(+empty+, 3), 3), 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_assert(format([= 1 2+ 3 =]) = "[= 1 2 + 3 =]");
# iteration over path
int x = 0;
int y = 0;
for int i in p2 do {
x = x + i;
y = y + x;
}
bt_assert(x = 18 && y = 50);
} }
bt_test_suite(t_path, "Testing paths"); bt_test_suite(t_path, "Testing paths");
@ -759,6 +807,7 @@ clist r;
bt_assert(l ~ [(2,2..3)]); bt_assert(l ~ [(2,2..3)]);
bt_assert(l ~ [(1,1..2)]); bt_assert(l ~ [(1,1..2)]);
bt_assert(l ~ [(1,1)..(1,2)]); bt_assert(l ~ [(1,1)..(1,2)]);
bt_assert(l !~ []);
l = add(l, (2,5)); l = add(l, (2,5));
l = add(l, (5,one)); l = add(l, (5,one));
@ -796,6 +845,9 @@ clist r;
bt_assert(l !~ [(*,(one+6))]); bt_assert(l !~ [(*,(one+6))]);
bt_assert(l !~ [(*, (one+one+one))]); bt_assert(l !~ [(*, (one+one+one))]);
bt_assert(delete(l, []) = l);
bt_assert(filter(l, []) = -empty-);
l = delete(l, [(*,(one+onef(3)))]); l = delete(l, [(*,(one+onef(3)))]);
l = delete(l, [(*,(4+one))]); l = delete(l, [(*,(4+one))]);
bt_assert(l = add(-empty-, (3,1))); bt_assert(l = add(-empty-, (3,1)));
@ -840,6 +892,12 @@ clist r;
bt_assert(format(r) = "(clist (2,1) (1,3) (2,2) (3,1) (2,3))"); bt_assert(format(r) = "(clist (2,1) (1,3) (2,2) (3,1) (2,3))");
bt_assert(r.min = (1,3)); bt_assert(r.min = (1,3));
bt_assert(r.max = (3,1)); bt_assert(r.max = (3,1));
# iteration over clist
int x = 0;
for pair c in r do
x = x + c.asn * c.asn * c.data;
bt_assert(x = 36);
} }
bt_test_suite(t_clist, "Testing lists of communities"); bt_test_suite(t_clist, "Testing lists of communities");
@ -913,11 +971,15 @@ eclist r;
bt_assert((ro, 10.20.30.40, 100) !~ el); bt_assert((ro, 10.20.30.40, 100) !~ el);
bt_assert(el !~ [(rt, 10, 35..40)]); bt_assert(el !~ [(rt, 10, 35..40)]);
bt_assert(el !~ [(ro, 10, *)]); bt_assert(el !~ [(ro, 10, *)]);
bt_assert(el !~ []);
el = add(el, (rt, 10, 40)); el = add(el, (rt, 10, 40));
el2 = filter(el, [(rt, 10, 20..40)] ); el2 = filter(el, [(rt, 10, 20..40)] );
el2 = add(el2, (rt, 10, 50)); el2 = add(el2, (rt, 10, 50));
bt_assert(delete(el, []) = el);
bt_assert(filter(el, []) = --empty--);
# eclist A (1,30,40) # eclist A (1,30,40)
bt_assert(el = add(add(add(--empty--, (rt, 10, 1)), (rt, 10, 30)), (rt, 10, 40))); bt_assert(el = add(add(add(--empty--, (rt, 10, 1)), (rt, 10, 30)), (rt, 10, 40)));
bt_assert(format(el) = "(eclist (rt, 10, 1) (rt, 10, 30) (rt, 10, 40))"); bt_assert(format(el) = "(eclist (rt, 10, 1) (rt, 10, 30) (rt, 10, 40))");
@ -951,6 +1013,13 @@ eclist r;
bt_assert(format(r) = "(eclist (rt, 2, 1) (rt, 1, 3) (rt, 2, 2) (rt, 3, 1) (rt, 2, 3))"); bt_assert(format(r) = "(eclist (rt, 2, 1) (rt, 1, 3) (rt, 2, 2) (rt, 3, 1) (rt, 2, 3))");
bt_assert(r.min = (rt, 1, 3)); bt_assert(r.min = (rt, 1, 3));
bt_assert(r.max = (rt, 3, 1)); bt_assert(r.max = (rt, 3, 1));
# iteration over eclist
int x = 0;
for ec c in r do
if c > (rt, 2, 0) && c < (rt, 3, 0) then
x = x + 1;
bt_assert(x = 3);
} }
bt_test_suite(t_eclist, "Testing lists of extended communities"); bt_test_suite(t_eclist, "Testing lists of extended communities");
@ -1035,6 +1104,9 @@ lclist r;
ll2 = add(ll2, (30, 30, 30)); ll2 = add(ll2, (30, 30, 30));
ll2 = add(ll2, (40, 40, 40)); ll2 = add(ll2, (40, 40, 40));
bt_assert(delete(ll, []) = ll);
bt_assert(filter(ll, []) = ---empty---);
# lclist A (10, 20, 30) # lclist A (10, 20, 30)
bt_assert(format(ll) = "(lclist (10, 10, 10) (20, 20, 20) (30, 30, 30))"); bt_assert(format(ll) = "(lclist (10, 10, 10) (20, 20, 20) (30, 30, 30))");
@ -1066,6 +1138,19 @@ lclist r;
bt_assert(format(r) = "(lclist (2, 3, 3) (1, 2, 3) (2, 3, 1) (3, 1, 2) (2, 1, 3))"); bt_assert(format(r) = "(lclist (2, 3, 3) (1, 2, 3) (2, 3, 1) (3, 1, 2) (2, 1, 3))");
bt_assert(r.min = (1, 2, 3)); bt_assert(r.min = (1, 2, 3));
bt_assert(r.max = (3, 1, 2)); bt_assert(r.max = (3, 1, 2));
# iteration over lclist
int x = 0;
int y = 0;
lc mx = (0, 0, 0);
for lc c in r do {
int asn2 = c.asn * c.asn;
x = x + asn2 * c.data1;
y = y + asn2 * c.data2;
if c > mx then mx = c;
}
bt_assert(x = 39 && y = 49);
bt_assert(mx = r.max);
} }
bt_test_suite(t_lclist, "Testing lists of large communities"); bt_test_suite(t_lclist, "Testing lists of large communities");
@ -1094,6 +1179,7 @@ lc set lls;
bt_assert(ll !~ [(5,10,15), (10,21,30)]); bt_assert(ll !~ [(5,10,15), (10,21,30)]);
bt_assert(ll !~ [(10,21..25,*)]); bt_assert(ll !~ [(10,21..25,*)]);
bt_assert(ll !~ [(11, *, *)]); bt_assert(ll !~ [(11, *, *)]);
bt_assert(ll !~ []);
lls = [(10, 10, 10), (20, 20, 15..25), (30, 30, *), (40, 35..45, *), (50, *, *), (55..65, *, *)]; lls = [(10, 10, 10), (20, 20, 15..25), (30, 30, *), (40, 35..45, *), (50, *, *), (55..65, *, *)];
bt_assert(format(lls) = "[(10, 10, 10), (20, 20, 15)..(20, 20, 25), (30, 30, 0)..(30, 30, 4294967295), (40, 35, 0)..(40, 45, 4294967295), (50, 0, 0)..(50, 4294967295, 4294967295), (55, 0, 0)..(65, 4294967295, 4294967295)]"); bt_assert(format(lls) = "[(10, 10, 10), (20, 20, 15)..(20, 20, 25), (30, 30, 0)..(30, 30, 4294967295), (40, 35, 0)..(40, 45, 4294967295), (50, 0, 0)..(50, 4294967295, 4294967295), (55, 0, 0)..(65, 4294967295, 4294967295)]");
@ -1150,6 +1236,10 @@ bt_test_suite(t_rd, "Testing route distinguishers");
function t_rd_set() function t_rd_set()
rd set rds; rd set rds;
{ {
rds = [];
bt_assert(rds = []);
bt_assert(10:20 !~ rds);
rds = [10:20, 100000:100..100000:200]; rds = [10:20, 100000:100..100000:200];
bt_assert(format(rds) = "[10:20, 100000:100..100000:200]"); bt_assert(format(rds) = "[10:20, 100000:100..100000:200]");
@ -1160,6 +1250,7 @@ rd set rds;
bt_assert(100000:128 ~ rds); bt_assert(100000:128 ~ rds);
bt_assert(100000:200 ~ rds); bt_assert(100000:200 ~ rds);
bt_assert(100010:150 !~ rds); bt_assert(100010:150 !~ rds);
bt_assert(100010:150 !~ []);
} }
bt_test_suite(t_rd_set, "Testing sets of route distinguishers"); bt_test_suite(t_rd_set, "Testing sets of route distinguishers");
@ -1226,7 +1317,85 @@ function fifteen()
return 15; return 15;
} }
function local_vars(int j)
{
int k = 10;
bt_assert(j = 5 && k = 10);
{
int j = 15;
k = 20;
bt_assert(j = 15 && k = 20);
}
bt_assert(j = 5 && k = 20);
if j < 10 then
{
int j = 25;
string k = "hello";
bt_assert(j = 25 && k = "hello");
}
bt_assert(j = 5 && k = 20);
int m = 100;
{
j = 35;
int k = 40;
bt_assert(j = 35 && k = 40 && m = 100);
}
bt_assert(j = 35 && k = 20 && m = 100);
}
function factorial(int x)
{
if x = 0 then return 0;
if x = 1 then return 1;
else return x * factorial(x - 1);
}
function fibonacci(int x)
{
if x = 0 then return 0;
if x = 1 then return 1;
else return fibonacci(x - 1) + fibonacci(x - 2);
}
function hanoi_init(int a; int b)
{
if b = 0
then return +empty+;
else return prepend(hanoi_init(a + 1, b - 1), a);
}
function hanoi_solve(int n; bgppath h_src; bgppath h_dst; bgppath h_aux; bool x; bool y)
{
# x -> return src or dst
# y -> print state
if n = 0 then { if x then return h_src; else return h_dst; }
bgppath tmp1 = hanoi_solve(n - 1, h_src, h_aux, h_dst, true, y);
bgppath tmp2 = hanoi_solve(n - 1, h_src, h_aux, h_dst, false, false);
h_src = tmp1;
h_aux = tmp2;
int v = h_src.first;
# bt_assert(h_dst = +empty+ || v < h_dst.first);
h_src = delete(h_src, v);
h_dst = prepend(h_dst, v);
if y then
print "move: ", v, " src: ", h_src, " dst:", h_dst, " aux:", h_aux;
tmp1 = hanoi_solve(n - 1, h_aux, h_dst, h_src, true, y);
tmp2 = hanoi_solve(n - 1, h_aux, h_dst, h_src, false, false);
h_aux = tmp1;
h_dst = tmp2;
if x then return h_src; else return h_dst;
}
function t_call_function() function t_call_function()
bgppath h_src;
{ {
bt_assert(fifteen() = 15); bt_assert(fifteen() = 15);
@ -1238,6 +1407,17 @@ function t_call_function()
bt_assert(callme(4, 4) = 16); bt_assert(callme(4, 4) = 16);
bt_assert(callme(7, 2) = 14); bt_assert(callme(7, 2) = 14);
bt_assert(callmeagain(1, 2, 3) = 6); bt_assert(callmeagain(1, 2, 3) = 6);
local_vars(5);
bt_assert(factorial(5) = 120);
bt_assert(factorial(10) = 3628800);
bt_assert(fibonacci(10) = 55);
bt_assert(fibonacci(20) = 6765);
h_src = hanoi_init(1, 6);
bt_assert(format(h_src) = "(path 1 2 3 4 5 6)");
bt_assert(hanoi_solve(6, h_src, +empty+, +empty+, false, false) = h_src);
} }
bt_test_suite(t_call_function, "Testing calling functions"); bt_test_suite(t_call_function, "Testing calling functions");
@ -1434,13 +1614,16 @@ filter vpn_filter
bt_assert(net.type != NET_IP6); bt_assert(net.type != NET_IP6);
bt_assert(net.rd = 0:1:2); bt_assert(net.rd = 0:1:2);
bool b = false;
case (net.type) { case (net.type) {
NET_IP4: print "IPV4"; NET_IP4: print "IPV4";
NET_IP6: print "IPV6"; NET_IP6: print "IPV6";
else: b = true;
} }
bt_assert(b);
bt_check_assign(from, 10.20.30.40); bt_check_assign(from, 10.20.30.40);
bt_check_assign(gw, 55.55.55.44); # bt_check_assign(gw, 55.55.55.44);
bgp_community.add((3,5)); bgp_community.add((3,5));
bgp_ext_community.add((ro, 135, 999)); bgp_ext_community.add((ro, 135, 999));

View file

@ -134,6 +134,14 @@ same_tree(const struct f_tree *t1, const struct f_tree *t2)
return 1; return 1;
} }
int
tree_node_count(const struct f_tree *t)
{
if (t == NULL)
return 0;
return 1 + tree_node_count(t->left) + tree_node_count(t->right);
}
static void static void
tree_node_format(const struct f_tree *t, buffer *buf) tree_node_format(const struct f_tree *t, buffer *buf)

View file

@ -19,10 +19,7 @@ static void
start_conf_env(void) start_conf_env(void)
{ {
bt_bird_init(); bt_bird_init();
cfg_mem = tmp_linpool;
pool *p = rp_new(&root_pool, "helper_pool");
linpool *l = lp_new_default(p);
cfg_mem = l;
} }
static struct f_tree * static struct f_tree *

View file

@ -249,14 +249,14 @@ get_outer_net(net_addr *net, const struct f_prefix *src)
} }
static list * static list *
make_random_prefix_list(linpool *lp, int num, int v6, int tight) make_random_prefix_list(int num, int v6, int tight)
{ {
list *prefixes = lp_allocz(lp, sizeof(struct f_prefix_node)); list *prefixes = lp_allocz(tmp_linpool, sizeof(struct f_prefix_node));
init_list(prefixes); init_list(prefixes);
for (int i = 0; i < num; i++) for (int i = 0; i < num; i++)
{ {
struct f_prefix_node *px = lp_allocz(lp, sizeof(struct f_prefix_node)); struct f_prefix_node *px = lp_allocz(tmp_linpool, sizeof(struct f_prefix_node));
get_random_prefix(&px->prefix, v6, tight); get_random_prefix(&px->prefix, v6, tight);
add_tail(prefixes, &px->n); add_tail(prefixes, &px->n);
@ -269,9 +269,9 @@ make_random_prefix_list(linpool *lp, int num, int v6, int tight)
} }
static struct f_trie * static struct f_trie *
make_trie_from_prefix_list(linpool *lp, list *prefixes) make_trie_from_prefix_list(list *prefixes)
{ {
struct f_trie *trie = f_new_trie(lp, 0); struct f_trie *trie = f_new_trie(tmp_linpool, 0);
struct f_prefix_node *n; struct f_prefix_node *n;
WALK_LIST(n, *prefixes) WALK_LIST(n, *prefixes)
@ -286,7 +286,7 @@ make_trie_from_prefix_list(linpool *lp, list *prefixes)
* Arg @plus means prefix should include all longer ones. * Arg @plus means prefix should include all longer ones.
*/ */
static list * static list *
read_prefix_list(linpool *lp, FILE *f, int v6, int plus) read_prefix_list(FILE *f, int v6, int plus)
{ {
ASSERT(!v6); ASSERT(!v6);
@ -294,7 +294,7 @@ read_prefix_list(linpool *lp, FILE *f, int v6, int plus)
char s[32]; char s[32];
int n; int n;
list *pxlist = lp_allocz(lp, sizeof(struct f_prefix_node)); list *pxlist = lp_allocz(tmp_linpool, sizeof(struct f_prefix_node));
init_list(pxlist); init_list(pxlist);
errno = 0; errno = 0;
@ -308,7 +308,7 @@ read_prefix_list(linpool *lp, FILE *f, int v6, int plus)
if (n != 5) if (n != 5)
bt_abort_msg("Invalid content of trie_data"); bt_abort_msg("Invalid content of trie_data");
struct f_prefix_node *px = lp_allocz(lp, sizeof(struct f_prefix_node)); struct f_prefix_node *px = lp_allocz(tmp_linpool, sizeof(struct f_prefix_node));
net_fill_ip4(&px->prefix.net, ip4_build(a0, a1, a2, a3), pl); net_fill_ip4(&px->prefix.net, ip4_build(a0, a1, a2, a3), pl);
px->prefix.lo = pl; px->prefix.lo = pl;
px->prefix.hi = plus ? IP4_MAX_PREFIX_LENGTH : pl; px->prefix.hi = plus ? IP4_MAX_PREFIX_LENGTH : pl;
@ -331,7 +331,6 @@ read_prefix_list(linpool *lp, FILE *f, int v6, int plus)
*/ */
static int static int
read_prefix_file(const char *filename, int plus, read_prefix_file(const char *filename, int plus,
linpool *lp0, linpool *lp1,
list *data[], struct f_trie *trie[]) list *data[], struct f_trie *trie[])
{ {
FILE *f = fopen(filename, "r"); FILE *f = fopen(filename, "r");
@ -339,10 +338,10 @@ read_prefix_file(const char *filename, int plus,
int n = 0; int n = 0;
list *pxlist; list *pxlist;
while (pxlist = read_prefix_list(lp0, f, 0, plus)) while (pxlist = read_prefix_list(f, 0, plus))
{ {
data[n] = pxlist; data[n] = pxlist;
trie[n] = make_trie_from_prefix_list(lp1, pxlist); trie[n] = make_trie_from_prefix_list(pxlist);
bt_debug("NEXT\n"); bt_debug("NEXT\n");
n++; n++;
} }
@ -437,11 +436,10 @@ t_match_random_net(void)
bt_config_parse(BT_CONFIG_SIMPLE); bt_config_parse(BT_CONFIG_SIMPLE);
int v6 = 0; int v6 = 0;
linpool *lp = lp_new_default(&root_pool);
for (int round = 0; round < TESTS_NUM; round++) for (int round = 0; round < TESTS_NUM; round++)
{ {
list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6, 0); list *prefixes = make_random_prefix_list(PREFIXES_NUM, v6, 0);
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes); struct f_trie *trie = make_trie_from_prefix_list(prefixes);
for (int i = 0; i < PREFIX_TESTS_NUM; i++) for (int i = 0; i < PREFIX_TESTS_NUM; i++)
{ {
@ -451,7 +449,7 @@ t_match_random_net(void)
} }
v6 = !v6; v6 = !v6;
lp_flush(lp); tmp_flush();
} }
bt_bird_cleanup(); bt_bird_cleanup();
@ -465,11 +463,10 @@ t_match_inner_net(void)
bt_config_parse(BT_CONFIG_SIMPLE); bt_config_parse(BT_CONFIG_SIMPLE);
int v6 = 0; int v6 = 0;
linpool *lp = lp_new_default(&root_pool);
for (int round = 0; round < TESTS_NUM; round++) for (int round = 0; round < TESTS_NUM; round++)
{ {
list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6, 0); list *prefixes = make_random_prefix_list(PREFIXES_NUM, v6, 0);
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes); struct f_trie *trie = make_trie_from_prefix_list(prefixes);
struct f_prefix_node *n = HEAD(*prefixes); struct f_prefix_node *n = HEAD(*prefixes);
for (int i = 0; i < PREFIX_TESTS_NUM; i++) for (int i = 0; i < PREFIX_TESTS_NUM; i++)
@ -482,7 +479,7 @@ t_match_inner_net(void)
} }
v6 = !v6; v6 = !v6;
lp_flush(lp); tmp_flush();
} }
bt_bird_cleanup(); bt_bird_cleanup();
@ -496,11 +493,10 @@ t_match_outer_net(void)
bt_config_parse(BT_CONFIG_SIMPLE); bt_config_parse(BT_CONFIG_SIMPLE);
int v6 = 0; int v6 = 0;
linpool *lp = lp_new_default(&root_pool);
for (int round = 0; round < TESTS_NUM; round++) for (int round = 0; round < TESTS_NUM; round++)
{ {
list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6, 0); list *prefixes = make_random_prefix_list(PREFIXES_NUM, v6, 0);
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes); struct f_trie *trie = make_trie_from_prefix_list(prefixes);
struct f_prefix_node *n = HEAD(*prefixes); struct f_prefix_node *n = HEAD(*prefixes);
for (int i = 0; i < PREFIX_TESTS_NUM; i++) for (int i = 0; i < PREFIX_TESTS_NUM; i++)
@ -513,7 +509,7 @@ t_match_outer_net(void)
} }
v6 = !v6; v6 = !v6;
lp_flush(lp); tmp_flush();
} }
v6 = !v6; v6 = !v6;
@ -531,24 +527,22 @@ static int
benchmark_trie_dataset(const char *filename, int plus) benchmark_trie_dataset(const char *filename, int plus)
{ {
int n = 0; int n = 0;
linpool *lp0 = lp_new_default(&root_pool);
linpool *lp1 = lp_new_default(&root_pool);
list *data[TRIE_BUFFER_SIZE]; list *data[TRIE_BUFFER_SIZE];
struct f_trie *trie[TRIE_BUFFER_SIZE]; struct f_trie *trie[TRIE_BUFFER_SIZE];
net_addr *nets; net_addr *nets;
bt_reset_suite_case_timer(); bt_reset_suite_case_timer();
bt_log_suite_case_result(1, "Reading %s", filename, n); bt_log_suite_case_result(1, "Reading %s", filename, n);
n = read_prefix_file(filename, plus, lp0, lp1, data, trie); n = read_prefix_file(filename, plus, data, trie);
bt_log_suite_case_result(1, "Read prefix data, %d lists, ", n); bt_log_suite_case_result(1, "Read prefix data, %d lists, ", n);
size_t trie_size = rmemsize(lp1).effective * 1000 / (1024*1024); size_t trie_size = rmemsize(tmp_linpool).effective * 1000 / (1024*1024);
bt_log_suite_case_result(1, "Trie size %u.%03u MB", bt_log_suite_case_result(1, "Trie size %u.%03u MB",
(uint) (trie_size / 1000), (uint) (trie_size % 1000)); (uint) (trie_size / 1000), (uint) (trie_size % 1000));
int t = PREFIX_BENCH_NUM / n; int t = PREFIX_BENCH_NUM / n;
int tb = MIN(t, TEST_BUFFER_SIZE); int tb = MIN(t, TEST_BUFFER_SIZE);
nets = lp_alloc(lp0, tb * sizeof(net_addr)); nets = tmp_alloc(tb * sizeof(net_addr));
if (!plus) if (!plus)
select_random_prefix_subset(data, nets, n, tb); select_random_prefix_subset(data, nets, n, tb);
@ -573,9 +567,7 @@ benchmark_trie_dataset(const char *filename, int plus)
bt_log_suite_case_result(1, "Matching done, %d / %d matches", match, t * n); bt_log_suite_case_result(1, "Matching done, %d / %d matches", match, t * n);
rfree(lp0); tmp_flush();
rfree(lp1);
return 1; return 1;
} }
@ -621,12 +613,11 @@ t_trie_same(void)
bt_config_parse(BT_CONFIG_SIMPLE); bt_config_parse(BT_CONFIG_SIMPLE);
int v6 = 0; int v6 = 0;
linpool *lp = lp_new_default(&root_pool);
for (int round = 0; round < TESTS_NUM*4; round++) for (int round = 0; round < TESTS_NUM*4; round++)
{ {
list *prefixes = make_random_prefix_list(lp, 100 * PREFIXES_NUM, v6, 0); list *prefixes = make_random_prefix_list(100 * PREFIXES_NUM, v6, 0);
struct f_trie *trie1 = f_new_trie(lp, 0); struct f_trie *trie1 = f_new_trie(tmp_linpool, 0);
struct f_trie *trie2 = f_new_trie(lp, 0); struct f_trie *trie2 = f_new_trie(tmp_linpool, 0);
struct f_prefix_node *n; struct f_prefix_node *n;
WALK_LIST(n, *prefixes) WALK_LIST(n, *prefixes)
@ -638,7 +629,7 @@ t_trie_same(void)
bt_assert(trie_same(trie1, trie2)); bt_assert(trie_same(trie1, trie2));
v6 = !v6; v6 = !v6;
lp_flush(lp); tmp_flush();
} }
bt_bird_cleanup(); bt_bird_cleanup();
@ -664,15 +655,14 @@ t_trie_walk(void)
bt_bird_init(); bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE); bt_config_parse(BT_CONFIG_SIMPLE);
linpool *lp = lp_new_default(&root_pool);
for (int round = 0; round < TESTS_NUM*8; round++) for (int round = 0; round < TESTS_NUM*8; round++)
{ {
int level = round / TESTS_NUM; int level = round / TESTS_NUM;
int v6 = level % 2; int v6 = level % 2;
int num = PREFIXES_NUM * (int[]){1, 10, 100, 1000}[level / 2]; int num = PREFIXES_NUM * (int[]){1, 10, 100, 1000}[level / 2];
int pos = 0, end = 0; int pos = 0, end = 0;
list *prefixes = make_random_prefix_list(lp, num, v6, 1); list *prefixes = make_random_prefix_list(num, v6, 1);
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes); struct f_trie *trie = make_trie_from_prefix_list(prefixes);
struct f_prefix *pxset = malloc((num + 1) * sizeof(struct f_prefix)); struct f_prefix *pxset = malloc((num + 1) * sizeof(struct f_prefix));
struct f_prefix_node *n; struct f_prefix_node *n;
@ -770,7 +760,7 @@ t_trie_walk(void)
bt_assert((pos == num) || !net_in_netX(&pxset[pos].net, &from.net)); bt_assert((pos == num) || !net_in_netX(&pxset[pos].net, &from.net));
bt_debug("Subnet walk done for %s (found %d nets)\n", buf0, pos - p0); bt_debug("Subnet walk done for %s (found %d nets)\n", buf0, pos - p0);
lp_flush(lp); tmp_flush();
} }
bt_bird_cleanup(); bt_bird_cleanup();
@ -815,7 +805,6 @@ t_trie_walk_to_root(void)
bt_bird_init(); bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE); bt_config_parse(BT_CONFIG_SIMPLE);
linpool *lp = lp_new_default(&root_pool);
for (int round = 0; round < TESTS_NUM * 4; round++) for (int round = 0; round < TESTS_NUM * 4; round++)
{ {
int level = round / TESTS_NUM; int level = round / TESTS_NUM;
@ -824,8 +813,8 @@ t_trie_walk_to_root(void)
int pos = 0; int pos = 0;
int st = 0, sn = 0, sm = 0; int st = 0, sn = 0, sm = 0;
list *prefixes = make_random_prefix_list(lp, num, v6, 1); list *prefixes = make_random_prefix_list(num, v6, 1);
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes); struct f_trie *trie = make_trie_from_prefix_list(prefixes);
struct f_prefix *pxset = malloc((num + 1) * sizeof(struct f_prefix)); struct f_prefix *pxset = malloc((num + 1) * sizeof(struct f_prefix));
struct f_prefix_node *pxn; struct f_prefix_node *pxn;
@ -884,7 +873,7 @@ t_trie_walk_to_root(void)
bt_debug("Success in %d / %d, sum %d, max %d\n", sn, i, st, sm); bt_debug("Success in %d / %d, sum %d, max %d\n", sn, i, st, sm);
lp_flush(lp); tmp_flush();
} }
bt_bird_cleanup(); bt_bird_cleanup();

View file

@ -2,6 +2,6 @@ src := bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c
obj := $(src-o-files) obj := $(src-o-files)
$(all-daemon) $(all-daemon)
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_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 slab_test.c
tests_targets := $(tests_targets) $(tests-target-files) tests_targets := $(tests_targets) $(tests-target-files)
tests_objs := $(tests_objs) $(src-o-files) tests_objs := $(tests_objs) $(src-o-files)

View file

@ -24,7 +24,6 @@ t_bmap_set_clear_random(void)
{ {
struct bmap b; struct bmap b;
resource_init();
bmap_init(&b, &root_pool, 1024); bmap_init(&b, &root_pool, 1024);
char expected[MAX_NUM] = {}; char expected[MAX_NUM] = {};
@ -60,7 +59,6 @@ t_hmap_set_clear_random(void)
{ {
struct hmap b; struct hmap b;
resource_init();
hmap_init(&b, &root_pool, 1024); hmap_init(&b, &root_pool, 1024);
char expected[MAX_NUM] = {}; char expected[MAX_NUM] = {};
@ -119,7 +117,6 @@ t_hmap_set_clear_fill(void)
{ {
struct hmap b; struct hmap b;
resource_init();
hmap_init(&b, &root_pool, 1024); hmap_init(&b, &root_pool, 1024);
char expected[MAX_NUM] = {}; char expected[MAX_NUM] = {};

View file

@ -41,7 +41,6 @@ fill_expected_array(void)
static void static void
init_buffer(void) init_buffer(void)
{ {
resource_init();
buffer_pool = &root_pool; buffer_pool = &root_pool;
BUFFER_INIT(buf, buffer_pool, MAX_NUM); BUFFER_INIT(buf, buffer_pool, MAX_NUM);
} }

View file

@ -157,6 +157,7 @@ ev_run_list(event_list *l)
io_log_event(e->hook, e->data); io_log_event(e->hook, e->data);
ev_run(e); ev_run(e);
tmp_flush();
} }
return !EMPTY_LIST(*l); return !EMPTY_LIST(*l);
@ -184,6 +185,7 @@ ev_run_list_limited(event_list *l, uint limit)
io_log_event(e->hook, e->data); io_log_event(e->hook, e->data);
ev_run(e); ev_run(e);
tmp_flush();
limit--; limit--;
} }

View file

@ -53,7 +53,6 @@ t_ev_run_list(void)
{ {
int i; int i;
resource_init();
olock_init(); olock_init();
timer_init(); timer_init();
io_init(); io_init();

View file

@ -446,10 +446,7 @@ t_validation6(void)
static int static int
t_builder4(void) t_builder4(void)
{ {
resource_init();
struct flow_builder *fb = flow_builder_init(&root_pool); struct flow_builder *fb = flow_builder_init(&root_pool);
linpool *lp = lp_new_default(&root_pool);
/* Expectation */ /* Expectation */
@ -492,7 +489,7 @@ t_builder4(void)
flow_builder_set_type(fb, FLOW_TYPE_TCP_FLAGS); flow_builder_set_type(fb, FLOW_TYPE_TCP_FLAGS);
flow_builder_add_op_val(fb, 0, 0x55); flow_builder_add_op_val(fb, 0, 0x55);
net_addr_flow4 *res = flow_builder4_finalize(fb, lp); net_addr_flow4 *res = flow_builder4_finalize(fb, tmp_linpool);
bt_assert(memcmp(res, expect, expect->length) == 0); bt_assert(memcmp(res, expect, expect->length) == 0);
@ -529,8 +526,6 @@ t_builder6(void)
{ {
net_addr_ip6 ip; net_addr_ip6 ip;
resource_init();
linpool *lp = lp_new_default(&root_pool);
struct flow_builder *fb = flow_builder_init(&root_pool); struct flow_builder *fb = flow_builder_init(&root_pool);
fb->ipv6 = 1; fb->ipv6 = 1;
@ -574,7 +569,7 @@ t_builder6(void)
flow_builder_set_type(fb, FLOW_TYPE_LABEL); flow_builder_set_type(fb, FLOW_TYPE_LABEL);
flow_builder_add_op_val(fb, 0, 0x55); flow_builder_add_op_val(fb, 0, 0x55);
net_addr_flow6 *res = flow_builder6_finalize(fb, lp); net_addr_flow6 *res = flow_builder6_finalize(fb, tmp_linpool);
bt_assert(memcmp(res, expect, expect->length) == 0); bt_assert(memcmp(res, expect, expect->length) == 0);
/* Reverse order */ /* Reverse order */
@ -601,7 +596,7 @@ t_builder6(void)
flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX); flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
flow_builder6_add_pfx(fb, &ip, 61); flow_builder6_add_pfx(fb, &ip, 61);
res = flow_builder6_finalize(fb, lp); res = flow_builder6_finalize(fb, tmp_linpool);
bt_assert(memcmp(res, expect, expect->length) == 0); bt_assert(memcmp(res, expect, expect->length) == 0);
return 1; return 1;

View file

@ -215,6 +215,12 @@ mem_hash_mix(u64 *h, const void *p, uint s)
*h = *h * multiplier + pp[i]; *h = *h * multiplier + pp[i];
} }
static inline void
mem_hash_mix_num(u64 *h, u64 val)
{
mem_hash_mix(h, &val, sizeof(val));
}
static inline uint static inline uint
mem_hash_value(u64 *h) mem_hash_value(u64 *h)
{ {

View file

@ -61,7 +61,6 @@ dump_nodes(void)
static void static void
init_hash_(uint order) init_hash_(uint order)
{ {
resource_init();
my_pool = rp_new(&root_pool, "Test pool"); my_pool = rp_new(&root_pool, "Test pool");
HASH_INIT(hash, my_pool, order); HASH_INIT(hash, my_pool, order);

View file

@ -42,6 +42,7 @@ typedef union list { /* In fact two overlayed nodes */
}; };
} list; } list;
#define STATIC_LIST_INIT(name) name = { .head = &name.tail_node, .tail = &name.head_node, .null = NULL }
#define NODE (node *) #define NODE (node *)
#define HEAD(list) ((void *)((list).head)) #define HEAD(list) ((void *)((list).head))

View file

@ -27,21 +27,22 @@
struct lp_chunk { struct lp_chunk {
struct lp_chunk *next; struct lp_chunk *next;
uint size;
uintptr_t data_align[0]; uintptr_t data_align[0];
byte data[0]; byte data[0];
}; };
const int lp_chunk_size = sizeof(struct lp_chunk); #define LP_DATA_SIZE (page_size - OFFSETOF(struct lp_chunk, data))
struct linpool { struct linpool {
resource r; resource r;
byte *ptr, *end; byte *ptr, *end;
struct lp_chunk *first, *current; /* Normal (reusable) chunks */ struct lp_chunk *first, *current; /* Normal (reusable) chunks */
struct lp_chunk *first_large; /* Large chunks */ struct lp_chunk *first_large; /* Large chunks */
uint chunk_size, threshold, total, total_large; uint total, total_large;
}; };
_Thread_local linpool *tmp_linpool;
static void lp_free(resource *); static void lp_free(resource *);
static void lp_dump(resource *); static void lp_dump(resource *);
static resource *lp_lookup(resource *, unsigned long); static resource *lp_lookup(resource *, unsigned long);
@ -59,19 +60,14 @@ static struct resclass lp_class = {
/** /**
* lp_new - create a new linear memory pool * lp_new - create a new linear memory pool
* @p: pool * @p: pool
* @blk: block size
* *
* lp_new() creates a new linear memory pool resource inside the pool @p. * lp_new() creates a new linear memory pool resource inside the pool @p.
* The linear pool consists of a list of memory chunks of size at least * The linear pool consists of a list of memory chunks of page size.
* @blk.
*/ */
linpool linpool
*lp_new(pool *p, uint blk) *lp_new(pool *p)
{ {
linpool *m = ralloc(p, &lp_class); return ralloc(p, &lp_class);
m->chunk_size = blk;
m->threshold = 3*blk/4;
return m;
} }
/** /**
@ -102,14 +98,13 @@ lp_alloc(linpool *m, uint size)
else else
{ {
struct lp_chunk *c; struct lp_chunk *c;
if (size >= m->threshold) if (size > LP_DATA_SIZE)
{ {
/* Too large => allocate large chunk */ /* Too large => allocate large chunk */
c = xmalloc(sizeof(struct lp_chunk) + size); c = xmalloc(sizeof(struct lp_chunk) + size);
m->total_large += size; m->total_large += size;
c->next = m->first_large; c->next = m->first_large;
m->first_large = c; m->first_large = c;
c->size = size;
} }
else else
{ {
@ -121,10 +116,10 @@ lp_alloc(linpool *m, uint size)
else else
{ {
/* Need to allocate a new chunk */ /* Need to allocate a new chunk */
c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size); c = alloc_page();
m->total += m->chunk_size;
m->total += LP_DATA_SIZE;
c->next = NULL; c->next = NULL;
c->size = m->chunk_size;
if (m->current) if (m->current)
m->current->next = c; m->current->next = c;
@ -133,7 +128,7 @@ lp_alloc(linpool *m, uint size)
} }
m->current = c; m->current = c;
m->ptr = c->data + size; m->ptr = c->data + size;
m->end = c->data + m->chunk_size; m->end = c->data + LP_DATA_SIZE;
} }
return c->data; return c->data;
} }
@ -195,7 +190,7 @@ lp_flush(linpool *m)
/* Move ptr to the first chunk and free all large chunks */ /* Move ptr to the first chunk and free all large chunks */
m->current = c = m->first; m->current = c = m->first;
m->ptr = c ? c->data : NULL; m->ptr = c ? c->data : NULL;
m->end = c ? c->data + m->chunk_size : NULL; m->end = c ? c->data + LP_DATA_SIZE : NULL;
while (c = m->first_large) while (c = m->first_large)
{ {
@ -218,6 +213,7 @@ lp_save(linpool *m, lp_state *p)
{ {
p->current = m->current; p->current = m->current;
p->large = m->first_large; p->large = m->first_large;
p->total_large = m->total_large;
p->ptr = m->ptr; p->ptr = m->ptr;
} }
@ -239,12 +235,12 @@ lp_restore(linpool *m, lp_state *p)
/* Move ptr to the saved pos and free all newer large chunks */ /* Move ptr to the saved pos and free all newer large chunks */
m->current = c = p->current; m->current = c = p->current;
m->ptr = p->ptr; m->ptr = p->ptr;
m->end = c ? c->data + m->chunk_size : NULL; m->end = c ? c->data + LP_DATA_SIZE : NULL;
m->total_large = p->total_large;
while ((c = m->first_large) && (c != p->large)) while ((c = m->first_large) && (c != p->large))
{ {
m->first_large = c->next; m->first_large = c->next;
m->total_large -= c->size;
xfree(c); xfree(c);
} }
} }
@ -258,7 +254,7 @@ lp_free(resource *r)
for(d=m->first; d; d = c) for(d=m->first; d; d = c)
{ {
c = d->next; c = d->next;
xfree(d); free_page(d);
} }
for(d=m->first_large; d; d = c) for(d=m->first_large; d; d = c)
{ {
@ -278,9 +274,7 @@ lp_dump(resource *r)
; ;
for(cntl=0, c=m->first_large; c; c=c->next, cntl++) for(cntl=0, c=m->first_large; c; c=c->next, cntl++)
; ;
debug("(chunk=%d threshold=%d count=%d+%d total=%d+%d)\n", debug("(count=%d+%d total=%d+%d)\n",
m->chunk_size,
m->threshold,
cnt, cnt,
cntl, cntl,
m->total, m->total,
@ -291,19 +285,22 @@ static struct resmem
lp_memsize(resource *r) lp_memsize(resource *r)
{ {
linpool *m = (linpool *) r; linpool *m = (linpool *) r;
struct lp_chunk *c; struct resmem sz = {
int cnt = 0; .overhead = sizeof(struct linpool) + ALLOC_OVERHEAD,
.effective = m->total_large,
for(c=m->first; c; c=c->next)
cnt++;
for(c=m->first_large; c; c=c->next)
cnt++;
return (struct resmem) {
.effective = m->total + m->total_large,
.overhead = ALLOC_OVERHEAD + sizeof(struct linpool) +
cnt * (ALLOC_OVERHEAD + sizeof(struct lp_chunk)),
}; };
for (struct lp_chunk *c = m->first_large; c; c = c->next)
sz.overhead += sizeof(struct lp_chunk) + ALLOC_OVERHEAD;
uint regular = 0;
for (struct lp_chunk *c = m->first; c; c = c->next)
regular++;
sz.effective += LP_DATA_SIZE * regular;
sz.overhead += (sizeof(struct lp_chunk) + ALLOC_OVERHEAD) * regular;
return sz;
} }
@ -314,10 +311,7 @@ lp_lookup(resource *r, unsigned long a)
struct lp_chunk *c; struct lp_chunk *c;
for(c=m->first; c; c=c->next) for(c=m->first; c; c=c->next)
if ((unsigned long) c->data <= a && (unsigned long) c->data + c->size > a) if ((unsigned long) c->data <= a && (unsigned long) c->data + LP_DATA_SIZE > a)
return r;
for(c=m->first_large; c; c=c->next)
if ((unsigned long) c->data <= a && (unsigned long) c->data + c->size > a)
return r; return r;
return NULL; return NULL;
} }

View file

@ -568,3 +568,51 @@ buffer_puts(buffer *buf, const char *str)
buf->pos = (bp < be) ? bp : buf->end; buf->pos = (bp < be) ? bp : buf->end;
} }
#define POOL_PRINTF_MAXBUF 1024
char *mb_vsprintf(pool *p, const char *fmt, va_list args)
{
char buf[POOL_PRINTF_MAXBUF];
int count = bvsnprintf(buf, POOL_PRINTF_MAXBUF, fmt, args);
if (count < 0)
bug("Attempted to mb_vsprintf() a too long string");
char *out = mb_alloc(p, count + 1);
memcpy(out, buf, count + 1);
return out;
}
char *mb_sprintf(pool *p, const char *fmt, ...)
{
va_list args;
char *out;
va_start(args, fmt);
out = mb_vsprintf(p, fmt, args);
va_end(args);
return out;
}
char *lp_vsprintf(linpool *p, const char *fmt, va_list args)
{
char buf[POOL_PRINTF_MAXBUF];
int count = bvsnprintf(buf, POOL_PRINTF_MAXBUF, fmt, args);
if (count < 0)
bug("Attempted to mb_vsprintf() a too long string");
char *out = lp_alloc(p, count + 1);
memcpy(out, buf, count + 1);
return out;
}
char *lp_sprintf(linpool *p, const char *fmt, ...)
{
va_list args;
char *out;
va_start(args, fmt);
out = lp_vsprintf(p, fmt, args);
va_end(args);
return out;
}

View file

@ -70,6 +70,20 @@ rp_new(pool *p, const char *name)
return z; return z;
} }
pool *
rp_newf(pool *p, const char *fmt, ...)
{
pool *z = rp_new(p, NULL);
va_list args;
va_start(args, fmt);
z->name = mb_vsprintf(p, fmt, args);
va_end(args);
return z;
}
static void static void
pool_free(resource *P) pool_free(resource *P)
{ {
@ -270,9 +284,12 @@ rlookup(unsigned long a)
void void
resource_init(void) resource_init(void)
{ {
resource_sys_init();
root_pool.r.class = &pool_class; root_pool.r.class = &pool_class;
root_pool.name = "Root"; root_pool.name = "Root";
init_list(&root_pool.inside); init_list(&root_pool.inside);
tmp_init(&root_pool);
} }
/** /**
@ -407,21 +424,6 @@ mb_realloc(void *m, unsigned size)
return b->data; return b->data;
} }
/**
* mb_move - move a memory block
* @m: memory block
* @p: target pool
*
* mb_move() moves the given memory block to another pool in the same way
* as rmove() moves a plain resource.
*/
void
mb_move(void *m, pool *p)
{
struct mblock *b = SKIP_BACK(struct mblock, data, m);
rmove(b, p);
}
/** /**
* mb_free - free a memory block * mb_free - free a memory block

View file

@ -44,6 +44,7 @@ typedef struct pool pool;
void resource_init(void); void resource_init(void);
pool *rp_new(pool *, const char *); /* Create new pool */ pool *rp_new(pool *, const char *); /* Create new pool */
pool *rp_newf(pool *, const char *, ...); /* Create a new pool with a formatted string as its name */
void rfree(void *); /* Free single resource */ void rfree(void *); /* Free single resource */
void rdump(void *); /* Dump to debug output */ void rdump(void *); /* Dump to debug output */
struct resmem rmemsize(void *res); /* Return size of memory used by the resource */ struct resmem rmemsize(void *res); /* Return size of memory used by the resource */
@ -59,7 +60,6 @@ extern pool root_pool;
void *mb_alloc(pool *, unsigned size); void *mb_alloc(pool *, unsigned size);
void *mb_allocz(pool *, unsigned size); void *mb_allocz(pool *, unsigned size);
void *mb_realloc(void *m, unsigned size); void *mb_realloc(void *m, unsigned size);
void mb_move(void *, pool *);
void mb_free(void *); void mb_free(void *);
/* Memory pools with linear allocation */ /* Memory pools with linear allocation */
@ -69,9 +69,10 @@ typedef struct linpool linpool;
typedef struct lp_state { typedef struct lp_state {
void *current, *large; void *current, *large;
byte *ptr; byte *ptr;
uint total_large;
} lp_state; } lp_state;
linpool *lp_new(pool *, unsigned blk); linpool *lp_new(pool *);
void *lp_alloc(linpool *, unsigned size); /* Aligned */ void *lp_alloc(linpool *, unsigned size); /* Aligned */
void *lp_allocu(linpool *, unsigned size); /* Unaligned */ void *lp_allocu(linpool *, unsigned size); /* Unaligned */
void *lp_allocz(linpool *, unsigned size); /* With clear */ void *lp_allocz(linpool *, unsigned size); /* With clear */
@ -79,10 +80,16 @@ void lp_flush(linpool *); /* Free everything, but leave linpool */
void lp_save(linpool *m, lp_state *p); /* Save state */ void lp_save(linpool *m, lp_state *p); /* Save state */
void lp_restore(linpool *m, lp_state *p); /* Restore state */ void lp_restore(linpool *m, lp_state *p); /* Restore state */
extern const int lp_chunk_size; extern _Thread_local linpool *tmp_linpool; /* Temporary linpool autoflushed regularily */
#define LP_GAS 1024
#define LP_GOOD_SIZE(x) (((x + LP_GAS - 1) & (~(LP_GAS - 1))) - lp_chunk_size) #define tmp_alloc(sz) lp_alloc(tmp_linpool, sz)
#define lp_new_default(p) lp_new(p, LP_GOOD_SIZE(LP_GAS*4)) #define tmp_allocu(sz) lp_allocu(tmp_linpool, sz)
#define tmp_allocz(sz) lp_allocz(tmp_linpool, sz)
#define tmp_init(p) tmp_linpool = lp_new_default(p)
#define tmp_flush() lp_flush(tmp_linpool)
#define lp_new_default lp_new
/* Slabs */ /* Slabs */
@ -91,7 +98,7 @@ typedef struct slab slab;
slab *sl_new(pool *, unsigned size); slab *sl_new(pool *, unsigned size);
void *sl_alloc(slab *); void *sl_alloc(slab *);
void *sl_allocz(slab *); void *sl_allocz(slab *);
void sl_free(slab *, void *); void sl_free(void *);
/* /*
* Low-level memory allocation functions, please don't use * Low-level memory allocation functions, please don't use
@ -101,10 +108,11 @@ void sl_free(slab *, void *);
void buffer_realloc(void **buf, unsigned *size, unsigned need, unsigned item_size); void buffer_realloc(void **buf, unsigned *size, unsigned need, unsigned item_size);
/* Allocator of whole pages; for use in slabs and other high-level allocators. */ /* Allocator of whole pages; for use in slabs and other high-level allocators. */
u64 get_page_size(void); extern long page_size;
void *alloc_page(void); void *alloc_page(void);
void free_page(void *); void free_page(void *);
extern uint pages_kept;
void resource_sys_init(void);
#ifdef HAVE_LIBDMALLOC #ifdef HAVE_LIBDMALLOC
/* /*

View file

@ -32,6 +32,7 @@
#include "nest/bird.h" #include "nest/bird.h"
#include "lib/resource.h" #include "lib/resource.h"
#include "lib/string.h" #include "lib/string.h"
#include "lib/tlists.h"
#undef FAKE_SLAB /* Turn on if you want to debug memory allocations */ #undef FAKE_SLAB /* Turn on if you want to debug memory allocations */
@ -98,7 +99,7 @@ sl_allocz(slab *s)
} }
void void
sl_free(slab *s, void *oo) sl_free(void *oo)
{ {
struct sl_obj *o = SKIP_BACK(struct sl_obj, data, oo); struct sl_obj *o = SKIP_BACK(struct sl_obj, data, oo);
@ -153,11 +154,38 @@ slab_memsize(resource *r)
#define MAX_EMPTY_HEADS 1 #define MAX_EMPTY_HEADS 1
enum sl_head_state {
slh_empty = 2,
slh_partial = 0,
slh_full = 1,
} PACKED;
struct sl_head {
struct slab *slab;
TLIST_NODE(sl_head, struct sl_head) n;
u16 num_full;
enum sl_head_state state;
u32 used_bits[0];
};
struct sl_alignment { /* Magic structure for testing of alignment */
byte data;
int x[0];
};
#define TLIST_PREFIX sl_head
#define TLIST_TYPE struct sl_head
#define TLIST_ITEM n
#define TLIST_WANT_WALK
#define TLIST_WANT_ADD_HEAD
#include "lib/tlists.h"
struct slab { struct slab {
resource r; resource r;
uint obj_size, head_size, head_bitfield_len; uint obj_size, head_size, head_bitfield_len;
uint objs_per_slab, num_empty_heads, data_size; uint objs_per_slab, num_empty_heads, data_size;
list empty_heads, partial_heads, full_heads; struct sl_head_list empty_heads, partial_heads, full_heads;
}; };
static struct resclass sl_class = { static struct resclass sl_class = {
@ -169,18 +197,15 @@ static struct resclass sl_class = {
slab_memsize slab_memsize
}; };
struct sl_head { #define SL_GET_HEAD(x) ((struct sl_head *) (((uintptr_t) (x)) & ~(page_size-1)))
node n;
u32 num_full;
u32 used_bits[0];
};
struct sl_alignment { /* Magic structure for testing of alignment */ #define SL_HEAD_CHANGE_STATE(_s, _h, _from, _to) ({ \
byte data; ASSERT_DIE(_h->state == slh_##_from); \
int x[0]; sl_head_rem_node(&_s->_from##_heads, _h); \
}; sl_head_add_head(&_s->_to##_heads, _h); \
_h->state = slh_##_to; \
})
#define SL_GET_HEAD(x) ((struct sl_head *) (((uintptr_t) (x)) & ~(get_page_size()-1)))
/** /**
* sl_new - create a new Slab * sl_new - create a new Slab
@ -202,7 +227,6 @@ sl_new(pool *p, uint size)
s->obj_size = size; s->obj_size = size;
s->head_size = sizeof(struct sl_head); s->head_size = sizeof(struct sl_head);
u64 page_size = get_page_size();
do { do {
s->objs_per_slab = (page_size - s->head_size) / size; s->objs_per_slab = (page_size - s->head_size) / size;
@ -218,9 +242,6 @@ sl_new(pool *p, uint size)
bug("Slab: object too large"); bug("Slab: object too large");
s->num_empty_heads = 0; s->num_empty_heads = 0;
init_list(&s->empty_heads);
init_list(&s->partial_heads);
init_list(&s->full_heads);
return s; return s;
} }
@ -237,8 +258,7 @@ sl_alloc(slab *s)
struct sl_head *h; struct sl_head *h;
redo: redo:
h = HEAD(s->partial_heads); if (!(h = s->partial_heads.first))
if (!h->n.next)
goto no_partial; goto no_partial;
okay: okay:
for (uint i=0; i<s->head_bitfield_len; i++) for (uint i=0; i<s->head_bitfield_len; i++)
@ -258,23 +278,27 @@ okay:
return out; return out;
} }
rem_node(&h->n); SL_HEAD_CHANGE_STATE(s, h, partial, full);
add_tail(&s->full_heads, &h->n);
goto redo; goto redo;
no_partial: no_partial:
h = HEAD(s->empty_heads); if (h = s->empty_heads.first)
if (h->n.next)
{ {
rem_node(&h->n); SL_HEAD_CHANGE_STATE(s, h, empty, partial);
add_head(&s->partial_heads, &h->n);
s->num_empty_heads--; s->num_empty_heads--;
goto okay; goto okay;
} }
h = alloc_page(); h = alloc_page();
ASSERT_DIE(SL_GET_HEAD(h) == h); ASSERT_DIE(SL_GET_HEAD(h) == h);
#ifdef POISON
memset(h, 0xba, page_size);
#endif
memset(h, 0, s->head_size); memset(h, 0, s->head_size);
add_head(&s->partial_heads, &h->n); h->slab = s;
sl_head_add_head(&s->partial_heads, h);
goto okay; goto okay;
} }
@ -303,9 +327,10 @@ sl_allocz(slab *s)
* and returns it back to the Slab @s. * and returns it back to the Slab @s.
*/ */
void void
sl_free(slab *s, void *oo) sl_free(void *oo)
{ {
struct sl_head *h = SL_GET_HEAD(oo); struct sl_head *h = SL_GET_HEAD(oo);
struct slab *s = h->slab;
#ifdef POISON #ifdef POISON
memset(oo, 0xdb, s->data_size); memset(oo, 0xdb, s->data_size);
@ -318,19 +343,22 @@ sl_free(slab *s, void *oo)
h->used_bits[pos / 32] &= ~(1 << (pos % 32)); h->used_bits[pos / 32] &= ~(1 << (pos % 32));
if (h->num_full-- == s->objs_per_slab) if ((h->num_full-- == s->objs_per_slab) && (h->state == slh_full))
{ SL_HEAD_CHANGE_STATE(s, h, full, partial);
rem_node(&h->n);
add_head(&s->partial_heads, &h->n);
}
else if (!h->num_full) else if (!h->num_full)
{ {
rem_node(&h->n); sl_head_rem_node(&s->partial_heads, h);
if (s->num_empty_heads >= MAX_EMPTY_HEADS) if (s->num_empty_heads >= MAX_EMPTY_HEADS)
{
#ifdef POISON
memset(h, 0xde, page_size);
#endif
free_page(h); free_page(h);
}
else else
{ {
add_head(&s->empty_heads, &h->n); sl_head_add_head(&s->empty_heads, h);
h->state = slh_empty;
s->num_empty_heads++; s->num_empty_heads++;
} }
} }
@ -340,13 +368,12 @@ static void
slab_free(resource *r) slab_free(resource *r)
{ {
slab *s = (slab *) r; slab *s = (slab *) r;
struct sl_head *h, *g;
WALK_LIST_DELSAFE(h, g, s->empty_heads) WALK_TLIST_DELSAFE(sl_head, h, &s->empty_heads)
free_page(h); free_page(h);
WALK_LIST_DELSAFE(h, g, s->partial_heads) WALK_TLIST_DELSAFE(sl_head, h, &s->partial_heads)
free_page(h); free_page(h);
WALK_LIST_DELSAFE(h, g, s->full_heads) WALK_TLIST_DELSAFE(sl_head, h, &s->full_heads)
free_page(h); free_page(h);
} }
@ -355,13 +382,12 @@ slab_dump(resource *r)
{ {
slab *s = (slab *) r; slab *s = (slab *) r;
int ec=0, pc=0, fc=0; int ec=0, pc=0, fc=0;
struct sl_head *h;
WALK_LIST(h, s->empty_heads) WALK_TLIST(sl_head, h, &s->empty_heads)
ec++; ec++;
WALK_LIST(h, s->partial_heads) WALK_TLIST(sl_head, h, &s->partial_heads)
pc++; pc++;
WALK_LIST(h, s->full_heads) WALK_TLIST(sl_head, h, &s->full_heads)
fc++; fc++;
debug("(%de+%dp+%df blocks per %d objs per %d bytes)\n", ec, pc, fc, s->objs_per_slab, s->obj_size); debug("(%de+%dp+%df blocks per %d objs per %d bytes)\n", ec, pc, fc, s->objs_per_slab, s->obj_size);
} }
@ -371,27 +397,26 @@ slab_memsize(resource *r)
{ {
slab *s = (slab *) r; slab *s = (slab *) r;
size_t heads = 0; size_t heads = 0;
struct sl_head *h;
WALK_LIST(h, s->full_heads) WALK_TLIST(sl_head, h, &s->full_heads)
heads++; heads++;
size_t items = heads * s->objs_per_slab; size_t items = heads * s->objs_per_slab;
WALK_LIST(h, s->partial_heads) WALK_TLIST(sl_head, h, &s->partial_heads)
{ {
heads++; heads++;
items += h->num_full; items += h->num_full;
} }
WALK_LIST(h, s->empty_heads) WALK_TLIST(sl_head, h, &s->empty_heads)
heads++; heads++;
size_t eff = items * s->obj_size; size_t eff = items * s->data_size;
return (struct resmem) { return (struct resmem) {
.effective = eff, .effective = eff,
.overhead = ALLOC_OVERHEAD + sizeof(struct slab) + heads * get_page_size() - eff, .overhead = ALLOC_OVERHEAD + sizeof(struct slab) + heads * page_size - eff,
}; };
} }
@ -399,13 +424,12 @@ static resource *
slab_lookup(resource *r, unsigned long a) slab_lookup(resource *r, unsigned long a)
{ {
slab *s = (slab *) r; slab *s = (slab *) r;
struct sl_head *h;
WALK_LIST(h, s->partial_heads) WALK_TLIST(sl_head, h, &s->partial_heads)
if ((unsigned long) h < a && (unsigned long) h + get_page_size() < a) if ((unsigned long) h < a && (unsigned long) h + page_size < a)
return r; return r;
WALK_LIST(h, s->full_heads) WALK_TLIST(sl_head, h, &s->full_heads)
if ((unsigned long) h < a && (unsigned long) h + get_page_size() < a) if ((unsigned long) h < a && (unsigned long) h + page_size < a)
return r; return r;
return NULL; return NULL;
} }

171
lib/slab_test.c Normal file
View file

@ -0,0 +1,171 @@
/*
* BIRD Library -- Slab Alloc / Dealloc Tests
*
* (c) 2022 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "lib/resource.h"
#include "lib/bitops.h"
static const int sizes[] = {
8, 12, 18, 27, 41, 75, 131, 269,
};
#define TEST_SIZE 1024 * 128
#define ITEMS(sz) TEST_SIZE / ( (sz) >> u32_log2((sz))/2 )
struct test_request {
int size;
enum strategy {
TEST_NONE,
TEST_FORWARDS,
TEST_BACKWARDS,
TEST_RANDOM,
TEST_MIXED,
TEST__MAX,
} strategy;
};
const char * const strategy_name[TEST__MAX] = {
[TEST_FORWARDS] = "forwards",
[TEST_BACKWARDS] = "backwards",
[TEST_RANDOM] = "random",
[TEST_MIXED] = "mixed",
};
static inline byte *test_alloc(slab *s, int sz, struct resmem *sliz)
{
byte *out = sl_alloc(s);
for (int p=0; p < sz; p++)
out[p] = p & 0xff;
struct resmem ns = rmemsize((resource *) s);
bt_assert(sliz->effective + sz == ns.effective);
bt_assert((sliz->overhead - sz - ns.overhead) % page_size == 0);
*sliz = ns;
return out;
}
static inline void test_free(slab *s, byte *block, int sz, struct resmem *sliz)
{
for (int p=0; p < sz; p++)
{
bt_assert(block[p] == (p & 0xff));
block[p]++;
}
sl_free(block);
struct resmem ns = rmemsize((resource *) s);
bt_assert(sliz->effective - sz == ns.effective);
bt_assert((sliz->overhead + sz - ns.overhead) % page_size == 0);
*sliz = ns;
}
static inline struct resmem get_memsize(slab *s)
{
struct resmem sz = rmemsize((resource *) s);
bt_assert(sz.effective == 0);
return sz;
}
static int
t_slab(const void *data)
{
const struct test_request *tr = data;
int sz = tr->size;
slab *s = sl_new(&root_pool, sz);
struct resmem sliz = get_memsize(s);
int n = ITEMS(sz);
byte **block = mb_alloc(&root_pool, n * sizeof(*block));
switch (tr->strategy) {
case TEST_FORWARDS:
for (int i = 0; i < n; i++)
block[i] = test_alloc(s, sz, &sliz);
for (int i = 0; i < n; i++)
test_free(s, block[i], sz, &sliz);
break;
case TEST_BACKWARDS:
for (int i = 0; i < n; i++)
block[i] = test_alloc(s, sz, &sliz);
for (int i = n - 1; i >= 0; i--)
test_free(s, block[i], sz, &sliz);
break;
case TEST_RANDOM:
for (int i = 0; i < n; i++)
block[i] = test_alloc(s, sz, &sliz);
for (int i = 0; i < n; i++)
{
int pos = bt_random() % (n - i);
test_free(s, block[pos], sz, &sliz);
if (pos != n - i - 1)
block[pos] = block[n - i - 1];
}
break;
case TEST_MIXED:
{
int cur = 0;
int pending = n;
while (cur + pending > 0) {
int action = bt_random() % (cur + pending);
if (action < cur) {
test_free(s, block[action], sz, &sliz);
if (action != --cur)
block[action] = block[cur];
} else {
block[cur++] = test_alloc(s, sz, &sliz);
pending--;
}
}
break;
}
default: bug("This shouldn't happen");
}
mb_free(block);
return 1;
}
int main(int argc, char *argv[])
{
bt_init(argc, argv);
struct test_request tr;
for (uint i = 0; i < sizeof(sizes) / sizeof(*sizes); i++)
for (uint strategy = TEST_FORWARDS; strategy < TEST__MAX; strategy++)
{
tr = (struct test_request) {
.size = sizes[i],
.strategy = strategy,
};
bt_test_suite_arg(t_slab, &tr, "Slab allocator test, size=%d, strategy=%s",
tr.size, strategy_name[strategy]);
}
return bt_exit_value();
}

View file

@ -20,6 +20,11 @@ int bvsprintf(char *str, const char *fmt, va_list args);
int bsnprintf(char *str, int size, const char *fmt, ...); int bsnprintf(char *str, int size, const char *fmt, ...);
int bvsnprintf(char *str, int size, const char *fmt, va_list args); int bvsnprintf(char *str, int size, const char *fmt, va_list args);
char *mb_sprintf(pool *p, const char *fmt, ...);
char *mb_vsprintf(pool *p, const char *fmt, va_list args);
char *lp_sprintf(linpool *p, const char *fmt, ...);
char *lp_vsprintf(linpool *p, const char *fmt, va_list args);
int buffer_vprint(buffer *buf, const char *fmt, va_list args); int buffer_vprint(buffer *buf, const char *fmt, va_list args);
int buffer_print(buffer *buf, const char *fmt, ...); int buffer_print(buffer *buf, const char *fmt, ...);
void buffer_puts(buffer *buf, const char *str); void buffer_puts(buffer *buf, const char *str);

View file

@ -76,6 +76,19 @@ current_time(void)
return timeloop_current()->last_time; return timeloop_current()->last_time;
} }
btime
current_time_now(void)
{
struct timespec ts;
int rv;
rv = clock_gettime(CLOCK_MONOTONIC, &ts);
if (rv < 0)
die("clock_gettime: %m");
return ts.tv_sec S + ts.tv_nsec NS;
}
btime btime
current_real_time(void) current_real_time(void)
{ {
@ -233,6 +246,7 @@ timers_fire(struct timeloop *loop)
io_log_event(t->hook, t->data); io_log_event(t->hook, t->data);
t->hook(t); t->hook(t);
tmp_flush();
} }
} }

View file

@ -44,6 +44,7 @@ static inline timer *timers_first(struct timeloop *loop)
extern struct timeloop main_timeloop; extern struct timeloop main_timeloop;
btime current_time(void); btime current_time(void);
btime current_time_now(void);
btime current_real_time(void); btime current_real_time(void);
//#define now (current_time() TO_S) //#define now (current_time() TO_S)

172
lib/tlists.h Normal file
View file

@ -0,0 +1,172 @@
/*
* BIRD Library -- Typed Linked Lists
*
* (c) 2022 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*
*
* This implementation of linked lists forces its members to be
* typed. On the other hand, it needs to be implemented as ugly macros to
* keep the needed genericity.
*
* Usage:
* 1. Include this file
* 2. Define the node structure
* 3. For every list type you need to define:
* A. #define TLIST_PREFIX and other macros
* B. Include this file once again
*
* Macros to define:
* TLIST_PREFIX: prefix to prepend to everything generated
* TLIST_TYPE: the actual node type
* TLIST_ITEM: where the tlist structure is
* TLIST_WANT_WALK: if defined, generates a helper functions for list walking macros
* TLIST_WANT_ADD_HEAD: if defined, TLIST_PREFIX_add_head() is generated to
* add an item to the beginning of the list
* TLIST_WANT_ADD_TAIL: if defined, TLIST_PREFIX_add_tail() is generated to
* add an item to the end of the list
*
* TLIST_PREFIX_rem_node() is generated always.
*
* All these macros are #undef-ed by including this file.
*
* Example:
*
* #include "lib/tlists.h"
*
* struct foo {
* ...
* TLIST_NODE(bar, struct foo) baz;
* ...
* };
*
* #define TLIST_PREFIX bar
* #define TLIST_TYPE struct foo
* #define TLIST_ITEM baz
*
* #define TLIST_WANT_WALK
* #define TLIST_WANT_ADD_HEAD
*
* #include "lib/tlists.h"
*
* ...
* (end of example)
*
*/
#ifdef _BIRD_LIB_TLISTS_H_
# ifdef TLIST_PREFIX
/* Check for mandatory arguments */
#ifndef TLIST_TYPE
#error "TLIST_TYPE must be defined"
#endif
#ifndef TLIST_ITEM
#error "TLIST_ITEM must be defined"
#endif
#ifndef TLIST_PREFIX
#error "TLIST_PREFIX must be defined"
#endif
#define TLIST_NAME(x) MACRO_CONCAT_AFTER(TLIST_PREFIX,_##x)
#ifndef TLIST_LIST_STRUCT
#define TLIST_LIST_STRUCT TLIST_NAME(list)
#endif
typedef struct TLIST_LIST_STRUCT {
TLIST_TYPE *first;
TLIST_TYPE *last;
} TLIST_LIST_STRUCT;
#ifdef TLIST_WANT_WALK
static inline struct TLIST_NAME(node) * TLIST_NAME(node_get)(TLIST_TYPE *node)
{ return &(node->TLIST_ITEM); }
#endif
#ifdef TLIST_WANT_ADD_HEAD
static inline void TLIST_NAME(add_head)(TLIST_LIST_STRUCT *list, TLIST_TYPE *node)
{
ASSERT_DIE(!node->TLIST_ITEM.prev && !node->TLIST_ITEM.next);
if (node->TLIST_ITEM.next = list->first)
list->first->TLIST_ITEM.prev = node;
else
list->last = node;
list->first = node;
}
#endif
#ifdef TLIST_WANT_ADD_TAIL
static inline void TLIST_NAME(add_tail)(TLIST_LIST_STRUCT *list, TLIST_TYPE *node)
{
ASSERT_DIE(!node->TLIST_ITEM.prev && !node->TLIST_ITEM.next);
if (node->TLIST_ITEM.prev = list->last)
list->last->TLIST_ITEM.next = node;
else
list->first = node;
list->last = node;
}
#endif
static inline void TLIST_NAME(rem_node)(TLIST_LIST_STRUCT *list, TLIST_TYPE *node)
{
if (node->TLIST_ITEM.prev)
node->TLIST_ITEM.prev->TLIST_ITEM.next = node->TLIST_ITEM.next;
else
{
ASSERT_DIE(list->first == node);
list->first = node->TLIST_ITEM.next;
}
if (node->TLIST_ITEM.next)
node->TLIST_ITEM.next->TLIST_ITEM.prev = node->TLIST_ITEM.prev;
else
{
ASSERT_DIE(list->last == node);
list->last = node->TLIST_ITEM.prev;
}
node->TLIST_ITEM.next = node->TLIST_ITEM.prev = NULL;
}
#undef TLIST_PREFIX
#undef TLIST_NAME
#undef TLIST_LIST_STRUCT
#undef TLIST_TYPE
#undef TLIST_ITEM
#undef TLIST_WANT_ADD_HEAD
#undef TLIST_WANT_ADD_TAIL
# endif
#else
#define _BIRD_LIB_TLISTS_H_
#include "lib/macro.h"
#if defined(TLIST_NAME) || defined(TLIST_PREFIX)
#error "You should first include lib/tlists.h without requesting a TLIST"
#endif
#define TLIST_NODE(_name, _type) struct _name##_node { _type *next; _type *prev; }
#define TLIST_LIST(_name) struct _name##_list
/* Use ->first and ->last to access HEAD and TAIL */
#define THEAD(_name, _list) (_list)->first
#define TTAIL(_name, _list) (_list)->last
/* Walkaround macros: simple and resilient to node removal */
#define WALK_TLIST(_name, _node, _list) \
for (typeof((_list)->first) _node = (_list)->first; \
_node; _node = _name##_node_get((_node))->next)
#define WALK_TLIST_DELSAFE(_name, _node, _list) \
for (typeof((_list)->first) _node = (_list)->first, \
_helper = _node ? _name##_node_get((_list)->first)->next : NULL; \
_node; \
(_node = _helper) ? (_helper = _name##_node_get(_helper)->next) : 0)
/* Empty check */
#define EMPTY_TLIST(_name, _list) (!(_list)->first)
#endif

View file

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

View file

@ -1,8 +1,12 @@
src := a-path.c a-set.c cli.c cmds.c iface.c locks.c neighbor.c password.c proto.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c src := a-path.c a-set.c cli.c cmds.c iface.c locks.c neighbor.c password.c proto.c proto-build.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c
obj := $(src-o-files) obj := $(src-o-files)
$(all-daemon) $(all-daemon)
$(cf-local) $(cf-local)
$(objdir)/nest/proto-build.c: $(lastword $(MAKEFILE_LIST))
$(E)echo GEN $@
$(Q)echo "$(patsubst %,void %_build(void); ,$(PROTO_BUILD)) void protos_build_gen(void) { $(patsubst %, %_build(); ,$(PROTO_BUILD))}" > $@
tests_src := a-set_test.c a-path_test.c tests_src := a-set_test.c a-path_test.c
tests_targets := $(tests_targets) $(tests-target-files) tests_targets := $(tests_targets) $(tests-target-files)
tests_objs := $(tests_objs) $(src-o-files) tests_objs := $(tests_objs) $(src-o-files)

View file

@ -591,7 +591,7 @@ as_path_match_set(const struct adata *path, const struct f_tree *set)
p += 2; p += 2;
for (i=0; i<n; i++) for (i=0; i<n; i++)
{ {
struct f_val v = {T_INT, .val.i = get_as(p)}; struct f_val v = { .type = T_INT, .val.i = get_as(p)};
if (find_tree(set, &v)) if (find_tree(set, &v))
return 1; return 1;
p += BS; p += BS;
@ -602,8 +602,10 @@ as_path_match_set(const struct adata *path, const struct f_tree *set)
} }
const struct adata * const struct adata *
as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tree *set, u32 key, int pos) as_path_filter(struct linpool *pool, const struct adata *path, const struct f_val *set, int pos)
{ {
ASSERT((set->type == T_SET) || (set->type == T_INT));
if (!path) if (!path)
return NULL; return NULL;
@ -629,13 +631,13 @@ as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tr
u32 as = get_as(p); u32 as = get_as(p);
int match; int match;
if (set) if (set->type == T_SET)
{ {
struct f_val v = {T_INT, .val.i = as}; struct f_val v = { .type = T_INT, .val.i = as};
match = !!find_tree(set, &v); match = !!find_tree(set->val.t, &v);
} }
else else /* T_INT */
match = (as == key); match = (as == set->val.i);
if (match == pos) if (match == pos)
{ {
@ -667,6 +669,35 @@ as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tr
return res; return res;
} }
int
as_path_walk(const struct adata *path, uint *pos, uint *val)
{
if (!path)
return 0;
const u8 *p = path->data;
const u8 *q = p + path->length;
uint n, x = *pos;
while (p < q)
{
n = p[1];
p += 2;
if (x < n)
{
*val = get_as(p + x * BS);
*pos += 1;
return 1;
}
p += n * BS;
x -= n;
}
return 0;
}
struct pm_pos struct pm_pos
{ {

View file

@ -12,6 +12,7 @@
#include "nest/route.h" #include "nest/route.h"
#include "nest/attrs.h" #include "nest/attrs.h"
#include "lib/resource.h" #include "lib/resource.h"
#include "filter/data.h"
#define TESTS_NUM 30 #define TESTS_NUM 30
#define AS_PATH_LENGTH 1000 #define AS_PATH_LENGTH 1000
@ -23,8 +24,6 @@
static int static int
t_as_path_match(void) t_as_path_match(void)
{ {
resource_init();
int round; int round;
for (round = 0; round < TESTS_NUM; round++) for (round = 0; round < TESTS_NUM; round++)
{ {
@ -32,14 +31,13 @@ t_as_path_match(void)
struct adata *as_path = &empty_as_path; struct adata *as_path = &empty_as_path;
u32 first_prepended, last_prepended; u32 first_prepended, last_prepended;
first_prepended = last_prepended = 0; first_prepended = last_prepended = 0;
struct linpool *lp = lp_new_default(&root_pool);
struct f_path_mask *mask = alloca(sizeof(struct f_path_mask) + AS_PATH_LENGTH * sizeof(struct f_path_mask_item)); struct f_path_mask *mask = alloca(sizeof(struct f_path_mask) + AS_PATH_LENGTH * sizeof(struct f_path_mask_item));
mask->len = AS_PATH_LENGTH; mask->len = AS_PATH_LENGTH;
for (int i = AS_PATH_LENGTH - 1; i >= 0; i--) for (int i = AS_PATH_LENGTH - 1; i >= 0; i--)
{ {
u32 val = bt_random(); u32 val = bt_random();
as_path = as_path_prepend(lp, as_path, val); as_path = as_path_prepend(tmp_linpool, as_path, val);
bt_debug("Prepending ASN: %10u \n", val); bt_debug("Prepending ASN: %10u \n", val);
if (i == 0) if (i == 0)
@ -61,7 +59,7 @@ t_as_path_match(void)
bt_assert(as_path_get_last(as_path, &asn)); bt_assert(as_path_get_last(as_path, &asn));
bt_assert_msg(asn == first_prepended, "as_path_get_last() should return the first prepended ASN"); bt_assert_msg(asn == first_prepended, "as_path_get_last() should return the first prepended ASN");
rfree(lp); tmp_flush();
} }
return 1; return 1;
@ -70,16 +68,13 @@ t_as_path_match(void)
static int static int
t_path_format(void) t_path_format(void)
{ {
resource_init();
struct adata empty_as_path = {}; struct adata empty_as_path = {};
struct adata *as_path = &empty_as_path; struct adata *as_path = &empty_as_path;
struct linpool *lp = lp_new_default(&root_pool);
uint i; uint i;
for (i = 4294967285; i <= 4294967294; i++) for (i = 4294967285; i <= 4294967294; i++)
{ {
as_path = as_path_prepend(lp, as_path, i); as_path = as_path_prepend(tmp_linpool, as_path, i);
bt_debug("Prepending ASN: %10u \n", i); bt_debug("Prepending ASN: %10u \n", i);
} }
@ -97,7 +92,7 @@ t_path_format(void)
as_path_format(as_path, buf2, SMALL_BUFFER_SIZE); as_path_format(as_path, buf2, SMALL_BUFFER_SIZE);
bt_assert_msg(strcmp(buf2, "4294967294 42...") == 0, "Small Buffer(%zu): '%s'", strlen(buf2), buf2); bt_assert_msg(strcmp(buf2, "4294967294 42...") == 0, "Small Buffer(%zu): '%s'", strlen(buf2), buf2);
rfree(lp); tmp_flush();
return 1; return 1;
} }
@ -116,11 +111,8 @@ count_asn_in_array(const u32 *array, u32 asn)
static int static int
t_path_include(void) t_path_include(void)
{ {
resource_init();
struct adata empty_as_path = {}; struct adata empty_as_path = {};
struct adata *as_path = &empty_as_path; struct adata *as_path = &empty_as_path;
struct linpool *lp = lp_new_default(&root_pool);
u32 as_nums[AS_PATH_LENGTH] = {}; u32 as_nums[AS_PATH_LENGTH] = {};
int i; int i;
@ -128,7 +120,7 @@ t_path_include(void)
{ {
u32 val = bt_random(); u32 val = bt_random();
as_nums[i] = val; as_nums[i] = val;
as_path = as_path_prepend(lp, as_path, val); as_path = as_path_prepend(tmp_linpool, as_path, val);
} }
for (i = 0; i < AS_PATH_LENGTH; i++) for (i = 0; i < AS_PATH_LENGTH; i++)
@ -136,8 +128,9 @@ t_path_include(void)
int counts_of_contains = count_asn_in_array(as_nums, as_nums[i]); int counts_of_contains = count_asn_in_array(as_nums, as_nums[i]);
bt_assert_msg(as_path_contains(as_path, as_nums[i], counts_of_contains), "AS Path should contains %d-times number %d", counts_of_contains, as_nums[i]); bt_assert_msg(as_path_contains(as_path, as_nums[i], counts_of_contains), "AS Path should contains %d-times number %d", counts_of_contains, as_nums[i]);
bt_assert(as_path_filter(lp, as_path, NULL, as_nums[i], 0) != NULL); struct f_val v = { .type = T_INT, .val.i = as_nums[i] };
bt_assert(as_path_filter(lp, as_path, NULL, as_nums[i], 1) != NULL); bt_assert(as_path_filter(tmp_linpool, as_path, &v, 0) != NULL);
bt_assert(as_path_filter(tmp_linpool, as_path, &v, 1) != NULL);
} }
for (i = 0; i < 10000; i++) for (i = 0; i < 10000; i++)
@ -152,7 +145,7 @@ t_path_include(void)
bt_assert_msg(result == 0, "As path should not contain the number %u", test_val); bt_assert_msg(result == 0, "As path should not contain the number %u", test_val);
} }
rfree(lp); tmp_flush();
return 1; return 1;
} }
@ -161,16 +154,13 @@ t_path_include(void)
static int static int
t_as_path_converting(void) t_as_path_converting(void)
{ {
resource_init();
struct adata empty_as_path = {}; struct adata empty_as_path = {};
struct adata *as_path = &empty_as_path; struct adata *as_path = &empty_as_path;
struct linpool *lp = lp_new_default(&root_pool);
#define AS_PATH_LENGTH_FOR_CONVERTING_TEST 10 #define AS_PATH_LENGTH_FOR_CONVERTING_TEST 10
int i; int i;
for (i = 0; i < AS_PATH_LENGTH_FOR_CONVERTING_TEST; i++) for (i = 0; i < AS_PATH_LENGTH_FOR_CONVERTING_TEST; i++)
as_path = as_path_prepend(lp, as_path, i); as_path = as_path_prepend(tmp_linpool, as_path, i);
bt_debug("data length: %u \n", as_path->length); bt_debug("data length: %u \n", as_path->length);

View file

@ -693,3 +693,51 @@ lc_set_max(const struct adata *list, lcomm *val)
*val = (lcomm) { res[0], res[1], res[2] }; *val = (lcomm) { res[0], res[1], res[2] };
return 1; return 1;
} }
int
int_set_walk(const struct adata *list, uint *pos, uint *val)
{
if (!list)
return 0;
if (*pos >= (uint) int_set_get_size(list))
return 0;
u32 *res = int_set_get_data(list) + *pos;
*val = *res;
*pos += 1;
return 1;
}
int
ec_set_walk(const struct adata *list, uint *pos, u64 *val)
{
if (!list)
return 0;
if (*pos >= (uint) int_set_get_size(list))
return 0;
u32 *res = int_set_get_data(list) + *pos;
*val = ec_generic(res[0], res[1]);
*pos += 2;
return 1;
}
int
lc_set_walk(const struct adata *list, uint *pos, lcomm *val)
{
if (!list)
return 0;
if (*pos >= (uint) int_set_get_size(list))
return 0;
u32 *res = int_set_get_data(list) + *pos;
*val = (lcomm) { res[0], res[1], res[2] };
*pos += 3;
return 1;
}

View file

@ -25,8 +25,6 @@ static byte buf[BUFFER_SIZE] = {};
#define SET_SIZE_FOR_FORMAT_OUTPUT 10 #define SET_SIZE_FOR_FORMAT_OUTPUT 10
struct linpool *lp;
enum set_type enum set_type
{ {
SET_TYPE_INT, SET_TYPE_INT,
@ -38,24 +36,23 @@ generate_set_sequence(enum set_type type, int len)
{ {
struct adata empty_as_path = {}; struct adata empty_as_path = {};
set_sequence = set_sequence_same = set_sequence_higher = set_random = &empty_as_path; set_sequence = set_sequence_same = set_sequence_higher = set_random = &empty_as_path;
lp = lp_new_default(&root_pool);
int i; int i;
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
{ {
if (type == SET_TYPE_INT) if (type == SET_TYPE_INT)
{ {
set_sequence = int_set_add(lp, set_sequence, i); set_sequence = int_set_add(tmp_linpool, set_sequence, i);
set_sequence_same = int_set_add(lp, set_sequence_same, i); set_sequence_same = int_set_add(tmp_linpool, set_sequence_same, i);
set_sequence_higher = int_set_add(lp, set_sequence_higher, i + SET_SIZE); set_sequence_higher = int_set_add(tmp_linpool, set_sequence_higher, i + SET_SIZE);
set_random = int_set_add(lp, set_random, bt_random()); set_random = int_set_add(tmp_linpool, set_random, bt_random());
} }
else if (type == SET_TYPE_EC) else if (type == SET_TYPE_EC)
{ {
set_sequence = ec_set_add(lp, set_sequence, i); set_sequence = ec_set_add(tmp_linpool, set_sequence, i);
set_sequence_same = ec_set_add(lp, set_sequence_same, i); set_sequence_same = ec_set_add(tmp_linpool, set_sequence_same, i);
set_sequence_higher = ec_set_add(lp, set_sequence_higher, i + SET_SIZE); set_sequence_higher = ec_set_add(tmp_linpool, set_sequence_higher, i + SET_SIZE);
set_random = ec_set_add(lp, set_random, (bt_random() << 32 | bt_random())); set_random = ec_set_add(tmp_linpool, set_random, (bt_random() << 32 | bt_random()));
} }
else else
bt_abort_msg("This should be unreachable"); bt_abort_msg("This should be unreachable");
@ -71,7 +68,6 @@ t_set_int_contains(void)
{ {
int i; int i;
resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE); generate_set_sequence(SET_TYPE_INT, SET_SIZE);
bt_assert(int_set_get_size(set_sequence) == SET_SIZE); bt_assert(int_set_get_size(set_sequence) == SET_SIZE);
@ -85,33 +81,29 @@ t_set_int_contains(void)
for (i = 0; i < SET_SIZE; i++) for (i = 0; i < SET_SIZE; i++)
bt_assert_msg(data[i] == i, "(data[i] = %d) == i = %d)", data[i], i); bt_assert_msg(data[i] == i, "(data[i] = %d) == i = %d)", data[i], i);
rfree(lp);
return 1; return 1;
} }
static int static int
t_set_int_union(void) t_set_int_union(void)
{ {
resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE); generate_set_sequence(SET_TYPE_INT, SET_SIZE);
const struct adata *set_union; const struct adata *set_union;
set_union = int_set_union(lp, set_sequence, set_sequence_same); set_union = int_set_union(tmp_linpool, set_sequence, set_sequence_same);
bt_assert(int_set_get_size(set_union) == SET_SIZE); bt_assert(int_set_get_size(set_union) == SET_SIZE);
bt_assert(int_set_format(set_union, 0, 2, buf, BUFFER_SIZE) == 0); bt_assert(int_set_format(set_union, 0, 2, buf, BUFFER_SIZE) == 0);
set_union = int_set_union(lp, set_sequence, set_sequence_higher); set_union = int_set_union(tmp_linpool, set_sequence, set_sequence_higher);
bt_assert_msg(int_set_get_size(set_union) == SET_SIZE*2, "int_set_get_size(set_union) %d, SET_SIZE*2 %d", int_set_get_size(set_union), SET_SIZE*2); bt_assert_msg(int_set_get_size(set_union) == SET_SIZE*2, "int_set_get_size(set_union) %d, SET_SIZE*2 %d", int_set_get_size(set_union), SET_SIZE*2);
bt_assert(int_set_format(set_union, 0, 2, buf, BUFFER_SIZE) == 0); bt_assert(int_set_format(set_union, 0, 2, buf, BUFFER_SIZE) == 0);
rfree(lp);
return 1; return 1;
} }
static int static int
t_set_int_format(void) t_set_int_format(void)
{ {
resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE_FOR_FORMAT_OUTPUT); generate_set_sequence(SET_TYPE_INT, SET_SIZE_FOR_FORMAT_OUTPUT);
bt_assert(int_set_format(set_sequence, 0, 0, buf, BUFFER_SIZE) == 0); bt_assert(int_set_format(set_sequence, 0, 0, buf, BUFFER_SIZE) == 0);
@ -125,21 +117,19 @@ t_set_int_format(void)
bt_assert(int_set_format(set_sequence, 1, 0, buf, BUFFER_SIZE) == 0); bt_assert(int_set_format(set_sequence, 1, 0, buf, BUFFER_SIZE) == 0);
bt_assert(strcmp(buf, "(0,0) (0,1) (0,2) (0,3) (0,4) (0,5) (0,6) (0,7) (0,8) (0,9)") == 0); bt_assert(strcmp(buf, "(0,0) (0,1) (0,2) (0,3) (0,4) (0,5) (0,6) (0,7) (0,8) (0,9)") == 0);
rfree(lp);
return 1; return 1;
} }
static int static int
t_set_int_delete(void) t_set_int_delete(void)
{ {
resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE); generate_set_sequence(SET_TYPE_INT, SET_SIZE);
const struct adata *deleting_sequence = set_sequence; const struct adata *deleting_sequence = set_sequence;
u32 i; u32 i;
for (i = 0; i < SET_SIZE; i++) for (i = 0; i < SET_SIZE; i++)
{ {
deleting_sequence = int_set_del(lp, deleting_sequence, i); deleting_sequence = int_set_del(tmp_linpool, deleting_sequence, i);
bt_assert_msg(int_set_get_size(deleting_sequence) == (int) (SET_SIZE-1-i), bt_assert_msg(int_set_get_size(deleting_sequence) == (int) (SET_SIZE-1-i),
"int_set_get_size(deleting_sequence) %d == SET_SIZE-1-i %d", "int_set_get_size(deleting_sequence) %d == SET_SIZE-1-i %d",
int_set_get_size(deleting_sequence), int_set_get_size(deleting_sequence),
@ -160,7 +150,6 @@ t_set_ec_contains(void)
{ {
u32 i; u32 i;
resource_init();
generate_set_sequence(SET_TYPE_EC, SET_SIZE); generate_set_sequence(SET_TYPE_EC, SET_SIZE);
bt_assert(ec_set_get_size(set_sequence) == SET_SIZE); bt_assert(ec_set_get_size(set_sequence) == SET_SIZE);
@ -174,62 +163,54 @@ t_set_ec_contains(void)
// for (i = 0; i < SET_SIZE; i++) // for (i = 0; i < SET_SIZE; i++)
// bt_assert_msg(data[i] == (SET_SIZE-1-i), "(data[i] = %d) == ((SET_SIZE-1-i) = %d)", data[i], SET_SIZE-1-i); // bt_assert_msg(data[i] == (SET_SIZE-1-i), "(data[i] = %d) == ((SET_SIZE-1-i) = %d)", data[i], SET_SIZE-1-i);
rfree(lp);
return 1; return 1;
} }
static int static int
t_set_ec_union(void) t_set_ec_union(void)
{ {
resource_init();
generate_set_sequence(SET_TYPE_EC, SET_SIZE); generate_set_sequence(SET_TYPE_EC, SET_SIZE);
const struct adata *set_union; const struct adata *set_union;
set_union = ec_set_union(lp, set_sequence, set_sequence_same); set_union = ec_set_union(tmp_linpool, set_sequence, set_sequence_same);
bt_assert(ec_set_get_size(set_union) == SET_SIZE); bt_assert(ec_set_get_size(set_union) == SET_SIZE);
bt_assert(ec_set_format(set_union, 0, buf, BUFFER_SIZE) == 0); bt_assert(ec_set_format(set_union, 0, buf, BUFFER_SIZE) == 0);
set_union = ec_set_union(lp, set_sequence, set_sequence_higher); set_union = ec_set_union(tmp_linpool, set_sequence, set_sequence_higher);
bt_assert_msg(ec_set_get_size(set_union) == SET_SIZE*2, "ec_set_get_size(set_union) %d, SET_SIZE*2 %d", ec_set_get_size(set_union), SET_SIZE*2); bt_assert_msg(ec_set_get_size(set_union) == SET_SIZE*2, "ec_set_get_size(set_union) %d, SET_SIZE*2 %d", ec_set_get_size(set_union), SET_SIZE*2);
bt_assert(ec_set_format(set_union, 0, buf, BUFFER_SIZE) == 0); bt_assert(ec_set_format(set_union, 0, buf, BUFFER_SIZE) == 0);
rfree(lp);
return 1; return 1;
} }
static int static int
t_set_ec_format(void) t_set_ec_format(void)
{ {
resource_init();
const struct adata empty_as_path = {}; const struct adata empty_as_path = {};
set_sequence = set_sequence_same = set_sequence_higher = set_random = &empty_as_path; set_sequence = set_sequence_same = set_sequence_higher = set_random = &empty_as_path;
lp = lp_new_default(&root_pool);
u64 i = 0; u64 i = 0;
set_sequence = ec_set_add(lp, set_sequence, i); set_sequence = ec_set_add(tmp_linpool, set_sequence, i);
for (i = 1; i < SET_SIZE_FOR_FORMAT_OUTPUT; i++) for (i = 1; i < SET_SIZE_FOR_FORMAT_OUTPUT; i++)
set_sequence = ec_set_add(lp, set_sequence, i + ((i%2) ? ((u64)EC_RO << 48) : ((u64)EC_RT << 48))); set_sequence = ec_set_add(tmp_linpool, set_sequence, i + ((i%2) ? ((u64)EC_RO << 48) : ((u64)EC_RT << 48)));
bt_assert(ec_set_format(set_sequence, 0, buf, BUFFER_SIZE) == 0); bt_assert(ec_set_format(set_sequence, 0, buf, BUFFER_SIZE) == 0);
bt_assert_msg(strcmp(buf, "(unknown 0x0, 0, 0) (ro, 0, 1) (rt, 0, 2) (ro, 0, 3) (rt, 0, 4) (ro, 0, 5) (rt, 0, 6) (ro, 0, 7) (rt, 0, 8) (ro, 0, 9)") == 0, bt_assert_msg(strcmp(buf, "(unknown 0x0, 0, 0) (ro, 0, 1) (rt, 0, 2) (ro, 0, 3) (rt, 0, 4) (ro, 0, 5) (rt, 0, 6) (ro, 0, 7) (rt, 0, 8) (ro, 0, 9)") == 0,
"ec_set_format() returns '%s'", buf); "ec_set_format() returns '%s'", buf);
rfree(lp);
return 1; return 1;
} }
static int static int
t_set_ec_delete(void) t_set_ec_delete(void)
{ {
resource_init();
generate_set_sequence(SET_TYPE_EC, SET_SIZE); generate_set_sequence(SET_TYPE_EC, SET_SIZE);
const struct adata *deleting_sequence = set_sequence; const struct adata *deleting_sequence = set_sequence;
u32 i; u32 i;
for (i = 0; i < SET_SIZE; i++) for (i = 0; i < SET_SIZE; i++)
{ {
deleting_sequence = ec_set_del(lp, deleting_sequence, i); deleting_sequence = ec_set_del(tmp_linpool, deleting_sequence, i);
bt_assert_msg(ec_set_get_size(deleting_sequence) == (int) (SET_SIZE-1-i), bt_assert_msg(ec_set_get_size(deleting_sequence) == (int) (SET_SIZE-1-i),
"ec_set_get_size(deleting_sequence) %d == SET_SIZE-1-i %d", "ec_set_get_size(deleting_sequence) %d == SET_SIZE-1-i %d",
ec_set_get_size(deleting_sequence), SET_SIZE-1-i); ec_set_get_size(deleting_sequence), SET_SIZE-1-i);

View file

@ -28,6 +28,7 @@
* to 16bit slot (like in 16bit AS_PATH). See RFC 4893 for details * to 16bit slot (like in 16bit AS_PATH). See RFC 4893 for details
*/ */
struct f_val;
struct f_tree; struct f_tree;
int as_path_valid(byte *data, uint len, int bs, int sets, int confed, char *err, uint elen); int as_path_valid(byte *data, uint len, int bs, int sets, int confed, char *err, uint elen);
@ -49,7 +50,8 @@ int as_path_get_last(const struct adata *path, u32 *last_as);
u32 as_path_get_last_nonaggregated(const struct adata *path); u32 as_path_get_last_nonaggregated(const struct adata *path);
int as_path_contains(const struct adata *path, u32 as, int min); int as_path_contains(const struct adata *path, u32 as, int min);
int as_path_match_set(const struct adata *path, const struct f_tree *set); int as_path_match_set(const struct adata *path, const struct f_tree *set);
const struct adata *as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tree *set, u32 key, int pos); const struct adata *as_path_filter(struct linpool *pool, const struct adata *path, const struct f_val *set, int pos);
int as_path_walk(const struct adata *path, uint *pos, uint *val);
static inline struct adata *as_path_prepend(struct linpool *pool, const struct adata *path, u32 as) static inline struct adata *as_path_prepend(struct linpool *pool, const struct adata *path, u32 as)
{ return as_path_prepend2(pool, path, AS_PATH_SEQUENCE, as); } { return as_path_prepend2(pool, path, AS_PATH_SEQUENCE, as); }
@ -136,6 +138,11 @@ static inline const char *ec_subtype_str(const enum ec_subtype ecs) {
} }
} }
/* Check for EC_RT subtype within different types (0-2) */
static inline int ec_type_is_rt(uint type)
{ return (type == EC_RT) || (type == (0x0100 | EC_RT)) || (type == (0x0200 | EC_RT)); }
/* Transitive bit (for first u32 half of EC) */ /* Transitive bit (for first u32 half of EC) */
#define EC_TBIT 0x40000000 #define EC_TBIT 0x40000000
@ -155,9 +162,13 @@ static inline u32 *int_set_get_data(const struct adata *list)
static inline u32 ec_hi(u64 ec) { return ec >> 32; } static inline u32 ec_hi(u64 ec) { return ec >> 32; }
static inline u32 ec_lo(u64 ec) { return ec; } static inline u32 ec_lo(u64 ec) { return ec; }
static inline u64 ec_get(const u32 *l, int i) static inline u64 ec_get(const u32 *l, int i)
{ return (((u64) l[i]) << 32) | l[i+1]; } { return (((u64) l[i]) << 32) | l[i+1]; }
static inline void ec_put(u32 *l, int i, u64 val)
{ l[i] = ec_hi(val); l[i+1] = ec_lo(val); }
/* RFC 4360 3.1. Two-Octet AS Specific Extended Community */ /* RFC 4360 3.1. Two-Octet AS Specific Extended Community */
static inline u64 ec_as2(enum ec_subtype kind, u64 key, u64 val) static inline u64 ec_as2(enum ec_subtype kind, u64 key, u64 val)
{ return (((u64) kind | 0x0000) << 48) | (key << 32) | val; } { return (((u64) kind | 0x0000) << 48) | (key << 32) | val; }
@ -224,6 +235,9 @@ int lc_set_min(const struct adata *list, lcomm *val);
int int_set_max(const struct adata *list, u32 *val); int int_set_max(const struct adata *list, u32 *val);
int ec_set_max(const struct adata *list, u64 *val); int ec_set_max(const struct adata *list, u64 *val);
int lc_set_max(const struct adata *list, lcomm *val); int lc_set_max(const struct adata *list, lcomm *val);
int int_set_walk(const struct adata *list, uint *pos, u32 *val);
int ec_set_walk(const struct adata *list, uint *pos, u64 *val);
int lc_set_walk(const struct adata *list, uint *pos, lcomm *val);
void ec_set_sort_x(struct adata *set); /* Sort in place */ void ec_set_sort_x(struct adata *set); /* Sort in place */

View file

@ -29,7 +29,7 @@ typedef struct cli {
node n; /* Node in list of all log hooks */ node n; /* Node in list of all log hooks */
pool *pool; pool *pool;
void *priv; /* Private to sysdep layer */ void *priv; /* Private to sysdep layer */
byte *rx_buf, *rx_pos, *rx_aux; /* sysdep */ byte *rx_buf, *rx_pos; /* sysdep */
struct cli_out *tx_buf, *tx_pos, *tx_write; struct cli_out *tx_buf, *tx_pos, *tx_write;
event *event; event *event;
void (*cont)(struct cli *c); void (*cont)(struct cli *c);

View file

@ -108,6 +108,7 @@ print_size(char *dsc, struct resmem vals)
extern pool *rt_table_pool; extern pool *rt_table_pool;
extern pool *rta_pool; extern pool *rta_pool;
extern uint *pages_kept;
void void
cmd_show_memory(void) cmd_show_memory(void)
@ -117,10 +118,11 @@ cmd_show_memory(void)
print_size("Routing tables:", rmemsize(rt_table_pool)); print_size("Routing tables:", rmemsize(rt_table_pool));
print_size("Route attributes:", rmemsize(rta_pool)); print_size("Route attributes:", rmemsize(rta_pool));
print_size("Protocols:", rmemsize(proto_pool)); print_size("Protocols:", rmemsize(proto_pool));
print_size("Current config:", rmemsize(config_pool));
struct resmem total = rmemsize(&root_pool); struct resmem total = rmemsize(&root_pool);
#ifdef HAVE_MMAP #ifdef HAVE_MMAP
print_size("Standby memory:", (struct resmem) { .overhead = get_page_size() * pages_kept }); print_size("Standby memory:", (struct resmem) { .overhead = page_size * *pages_kept });
total.overhead += get_page_size() * pages_kept; total.overhead += page_size * *pages_kept;
#endif #endif
print_size("Total:", total); print_size("Total:", total);
cli_msg(0, ""); cli_msg(0, "");

View file

@ -130,7 +130,7 @@ CF_KEYWORDS(SORTED, TRIE, MIN, MAX, SETTLE, TIME, GC, THRESHOLD, PERIOD)
/* For r_args_channel */ /* 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) 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)
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, CF_ENUM(T_ENUM_RTS, RTS_, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL) RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL)
CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED) 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_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
@ -165,7 +165,7 @@ rtrid:
idval: idval:
NUM { $$ = $1; } NUM { $$ = $1; }
| '(' term ')' { $$ = f_eval_int(f_linearize($2)); } | '(' term ')' { $$ = f_eval_int(f_linearize($2, 1)); }
| IP4 { $$ = ip4_to_u32($1); } | IP4 { $$ = ip4_to_u32($1); }
| CF_SYM_KNOWN { | CF_SYM_KNOWN {
if ($1->class == (SYM_CONSTANT | T_INT) || $1->class == (SYM_CONSTANT | T_QUAD)) if ($1->class == (SYM_CONSTANT | T_INT) || $1->class == (SYM_CONSTANT | T_QUAD))
@ -632,7 +632,7 @@ CF_CLI(SHOW INTERFACES SUMMARY,,, [[Show summary of network interfaces]])
{ if_show_summary(); } ; { if_show_summary(); } ;
CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]]) CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]])
CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filter <f>|where <cond>] [all] [primary] [filtered] [(export|preexport|noexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]]) CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [(import|export) table <p>.<c>] [filter <f>|where <cond>] [all] [primary] [filtered] [(export|preexport|noexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]])
{ rt_show($3); } ; { rt_show($3); } ;
r_args: r_args:
@ -860,7 +860,7 @@ CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]])
{ filters_dump_all(); cli_msg(0, ""); } ; { filters_dump_all(); cli_msg(0, ""); } ;
CF_CLI(EVAL, term, <expr>, [[Evaluate an expression]]) CF_CLI(EVAL, term, <expr>, [[Evaluate an expression]])
{ cmd_eval(f_linearize($2)); } ; { cmd_eval(f_linearize($2, 1)); } ;
CF_CLI_HELP(ECHO, ..., [[Control echoing of log messages]]) CF_CLI_HELP(ECHO, ..., [[Control echoing of log messages]])
CF_CLI(ECHO, echo_mask echo_size, (all | off | { debug|trace|info|remote|warning|error|auth [, ...] }) [<buffer-size>], [[Control echoing of log messages]]) { CF_CLI(ECHO, echo_mask echo_size, (all | off | { debug|trace|info|remote|warning|error|auth [, ...] }) [<buffer-size>], [[Control echoing of log messages]]) {

View file

@ -345,7 +345,7 @@ neigh_free(neighbor *n)
{ {
rem_node(&n->n); rem_node(&n->n);
rem_node(&n->if_n); rem_node(&n->if_n);
sl_free(neigh_slab, n); sl_free(n);
} }
/** /**

View file

@ -75,16 +75,6 @@ int reconfigure(struct proto *p, struct proto_config *c)
void dump(struct proto *p) void dump(struct proto *p)
{ DUMMY; } { DUMMY; }
/**
* dump_attrs - dump protocol-dependent attributes
* @e: a route entry
*
* This hook dumps all attributes in the &rte which belong to this
* protocol to the debug output.
*/
void dump_attrs(rte *e)
{ DUMMY; }
/** /**
* start - request instance startup * start - request instance startup
* @p: protocol instance * @p: protocol instance
@ -227,36 +217,6 @@ void rt_notify(struct proto *p, net *net, rte *new, rte *old, ea_list *attrs)
void neigh_notify(neighbor *neigh) void neigh_notify(neighbor *neigh)
{ DUMMY; } { DUMMY; }
/**
* make_tmp_attrs - convert embedded attributes to temporary ones
* @e: route entry
* @pool: linear pool to allocate attribute memory in
*
* This hook is called by the routing table functions if they need
* to convert the protocol attributes embedded directly in the &rte
* to temporary extended attributes in order to distribute them
* to other protocols or to filters. make_tmp_attrs() creates
* an &ea_list in the linear pool @pool, fills it with values of the
* temporary attributes and returns a pointer to it.
*/
ea_list *make_tmp_attrs(rte *e, struct linpool *pool)
{ DUMMY; }
/**
* store_tmp_attrs - convert temporary attributes to embedded ones
* @e: route entry
* @attrs: temporary attributes to be converted
*
* This hook is an exact opposite of make_tmp_attrs() -- it takes
* a list of extended attributes and converts them to attributes
* embedded in the &rte corresponding to this protocol.
*
* You must be prepared for any of the attributes being missing
* from the list and use default values instead.
*/
void store_tmp_attrs(rte *e, ea_list *attrs)
{ DUMMY; }
/** /**
* preexport - pre-filtering decisions before route export * preexport - pre-filtering decisions before route export
* @p: protocol instance the route is going to be exported to * @p: protocol instance the route is going to be exported to

View file

@ -23,9 +23,9 @@
#include "filter/f-inst.h" #include "filter/f-inst.h"
pool *proto_pool; pool *proto_pool;
list proto_list; list STATIC_LIST_INIT(proto_list);
static list protocol_list; static list STATIC_LIST_INIT(protocol_list);
struct protocol *class_to_protocol[PROTOCOL__MAX]; struct protocol *class_to_protocol[PROTOCOL__MAX];
#define CD(c, msg, args...) ({ if (c->debug & D_STATES) log(L_TRACE "%s.%s: " msg, c->proto->name, c->name ?: "?", ## args); }) #define CD(c, msg, args...) ({ if (c->debug & D_STATES) log(L_TRACE "%s.%s: " msg, c->proto->name, c->name ?: "?", ## args); })
@ -804,6 +804,7 @@ channel_config_get(const struct channel_class *cc, const char *name, uint net_ty
cf_error("Multiple %s channels", name); cf_error("Multiple %s channels", name);
cf->parent = proto; cf->parent = proto;
cf->copy = 1;
return cf; return cf;
} }
@ -829,6 +830,9 @@ static int reconfigure_type; /* Hack to propagate type info to channel_reconfig
int int
channel_reconfigure(struct channel *c, struct channel_config *cf) channel_reconfigure(struct channel *c, struct channel_config *cf)
{ {
/* Touched by reconfiguration */
c->stale = 0;
/* FIXME: better handle these changes, also handle in_keep_filtered */ /* FIXME: better handle these changes, also handle in_keep_filtered */
if ((c->table != cf->table->table) || (cf->ra_mode && (c->ra_mode != cf->ra_mode))) if ((c->table != cf->table->table) || (cf->ra_mode && (c->ra_mode != cf->ra_mode)))
return 0; return 0;
@ -1651,6 +1655,8 @@ proto_build(struct protocol *p)
/* FIXME: convert this call to some protocol hook */ /* FIXME: convert this call to some protocol hook */
extern void bfd_init_all(void); extern void bfd_init_all(void);
void protos_build_gen(void);
/** /**
* protos_build - build a protocol list * protos_build - build a protocol list
* *
@ -1663,44 +1669,7 @@ extern void bfd_init_all(void);
void void
protos_build(void) protos_build(void)
{ {
init_list(&proto_list); protos_build_gen();
init_list(&protocol_list);
proto_build(&proto_device);
#ifdef CONFIG_RADV
proto_build(&proto_radv);
#endif
#ifdef CONFIG_RIP
proto_build(&proto_rip);
#endif
#ifdef CONFIG_STATIC
proto_build(&proto_static);
#endif
#ifdef CONFIG_MRT
proto_build(&proto_mrt);
#endif
#ifdef CONFIG_OSPF
proto_build(&proto_ospf);
#endif
#ifdef CONFIG_PIPE
proto_build(&proto_pipe);
#endif
#ifdef CONFIG_BGP
proto_build(&proto_bgp);
#endif
#ifdef CONFIG_BFD
proto_build(&proto_bfd);
bfd_init_all();
#endif
#ifdef CONFIG_BABEL
proto_build(&proto_babel);
#endif
#ifdef CONFIG_RPKI
proto_build(&proto_rpki);
#endif
#ifdef CONFIG_PERF
proto_build(&proto_perf);
#endif
proto_pool = rp_new(&root_pool, "Protocols"); proto_pool = rp_new(&root_pool, "Protocols");
proto_shutdown_timer = tm_new(proto_pool); proto_shutdown_timer = tm_new(proto_pool);
@ -2243,8 +2212,13 @@ proto_apply_cmd_symbol(const struct symbol *s, void (* cmd)(struct proto *, uint
return; return;
} }
cmd(s->proto->proto, arg, 0); if (s->proto->proto)
cli_msg(0, ""); {
cmd(s->proto->proto, arg, 0);
cli_msg(0, "");
}
else
cli_msg(9002, "%s does not exist", s->name);
} }
static void static void

View file

@ -74,7 +74,6 @@ struct protocol {
struct proto * (*init)(struct proto_config *); /* Create new instance */ struct proto * (*init)(struct proto_config *); /* Create new instance */
int (*reconfigure)(struct proto *, struct proto_config *); /* Try to reconfigure instance, returns success */ int (*reconfigure)(struct proto *, struct proto_config *); /* Try to reconfigure instance, returns success */
void (*dump)(struct proto *); /* Debugging dump */ void (*dump)(struct proto *); /* Debugging dump */
void (*dump_attrs)(struct rte *); /* Dump protocol-dependent attributes */
int (*start)(struct proto *); /* Start the instance */ int (*start)(struct proto *); /* Start the instance */
int (*shutdown)(struct proto *); /* Stop the instance */ int (*shutdown)(struct proto *); /* Stop the instance */
void (*cleanup)(struct proto *); /* Called after shutdown when protocol became hungry/down */ void (*cleanup)(struct proto *); /* Called after shutdown when protocol became hungry/down */
@ -85,8 +84,8 @@ struct protocol {
void (*copy_config)(struct proto_config *, struct proto_config *); /* Copy config from given protocol instance */ void (*copy_config)(struct proto_config *, struct proto_config *); /* Copy config from given protocol instance */
}; };
void protos_build(void); void protos_build(void); /* Called from sysdep to initialize protocols */
void proto_build(struct protocol *); void proto_build(struct protocol *); /* Called from protocol to register itself */
void protos_preconfig(struct config *); void protos_preconfig(struct config *);
void protos_commit(struct config *new, struct config *old, int force_restart, int type); void protos_commit(struct config *new, struct config *old, int force_restart, int type);
struct proto * proto_spawn(struct proto_config *cf, uint disabled); struct proto * proto_spawn(struct proto_config *cf, uint disabled);
@ -198,12 +197,11 @@ struct proto {
* ifa_notify Notify protocol about interface address changes. * ifa_notify Notify protocol about interface address changes.
* rt_notify Notify protocol about routing table updates. * rt_notify Notify protocol about routing table updates.
* neigh_notify Notify protocol about neighbor cache events. * neigh_notify Notify protocol about neighbor cache events.
* make_tmp_attrs Add attributes to rta from from private attrs stored in rte. The route and rta MUST NOT be cached. * preexport Called as the first step of the route exporting process.
* store_tmp_attrs Store private attrs back to rte and undef added attributes. The route and rta MUST NOT be cached. * It can decide whether the route shall be exported:
* preexport Called as the first step of the route exporting process. * -1 = reject,
* It can construct a new rte, add private attributes and * 0 = continue to export filter
* decide whether the route shall be exported: 1=yes, -1=no, * 1 = accept immediately
* 0=process it through the export filter set by the user.
* reload_routes Request channel to reload all its routes to the core * reload_routes Request channel to reload all its routes to the core
* (using rte_update()). Returns: 0=reload cannot be done, * (using rte_update()). Returns: 0=reload cannot be done,
* 1= reload is scheduled and will happen (asynchronously). * 1= reload is scheduled and will happen (asynchronously).
@ -215,9 +213,7 @@ struct proto {
void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a); void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a);
void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old); void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old);
void (*neigh_notify)(struct neighbor *neigh); void (*neigh_notify)(struct neighbor *neigh);
void (*make_tmp_attrs)(struct rte *rt, struct linpool *pool); int (*preexport)(struct channel *, struct rte *rt);
void (*store_tmp_attrs)(struct rte *rt, struct linpool *pool);
int (*preexport)(struct proto *, struct rte **rt, struct linpool *pool);
void (*reload_routes)(struct channel *); void (*reload_routes)(struct channel *);
void (*feed_begin)(struct channel *, int initial); void (*feed_begin)(struct channel *, int initial);
void (*feed_end)(struct channel *); void (*feed_end)(struct channel *);
@ -235,11 +231,11 @@ struct proto {
int (*rte_recalculate)(struct rtable *, struct network *, struct rte *, struct rte *, struct rte *); int (*rte_recalculate)(struct rtable *, struct network *, struct rte *, struct rte *, struct rte *);
int (*rte_better)(struct rte *, struct rte *); int (*rte_better)(struct rte *, struct rte *);
int (*rte_same)(struct rte *, struct rte *);
int (*rte_mergable)(struct rte *, struct rte *); int (*rte_mergable)(struct rte *, struct rte *);
struct rte * (*rte_modify)(struct rte *, struct linpool *); struct rte * (*rte_modify)(struct rte *, struct linpool *);
void (*rte_insert)(struct network *, struct rte *); void (*rte_insert)(struct network *, struct rte *);
void (*rte_remove)(struct network *, struct rte *); void (*rte_remove)(struct network *, struct rte *);
u32 (*rte_igp_metric)(struct rte *);
/* Hic sunt protocol-specific data */ /* Hic sunt protocol-specific data */
}; };
@ -469,7 +465,6 @@ struct channel_class {
void (*dump)(struct proto *); /* Debugging dump */ void (*dump)(struct proto *); /* Debugging dump */
void (*dump_attrs)(struct rte *); /* Dump protocol-dependent attributes */
void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */ 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) */ void (*get_route_info)(struct rte *, byte *buf); /* Get route information (for `show route' command) */
@ -498,6 +493,7 @@ struct channel_config {
u8 ra_mode; /* Mode of received route advertisements (RA_*) */ u8 ra_mode; /* Mode of received route advertisements (RA_*) */
u16 preference; /* Default route preference */ u16 preference; /* Default route preference */
u32 debug; /* Debugging flags (D_*) */ u32 debug; /* Debugging flags (D_*) */
u8 copy; /* Value from channel_config_get() is new (0) or from template (1) */
u8 merge_limit; /* Maximal number of nexthops for RA_MERGED */ u8 merge_limit; /* Maximal number of nexthops for RA_MERGED */
u8 in_keep_filtered; /* Routes rejected in import filter are kept */ u8 in_keep_filtered; /* Routes rejected in import filter are kept */
u8 rpki_reload; /* RPKI changes trigger channel reload */ u8 rpki_reload; /* RPKI changes trigger channel reload */

View file

@ -256,50 +256,13 @@ struct hostentry {
typedef struct rte { typedef struct rte {
struct rte *next; struct rte *next;
net *net; /* Network this RTE belongs to */ net *net; /* Network this RTE belongs to */
struct rte_src *src; /* Route source that created the route */
struct channel *sender; /* Channel used to send the route to the routing table */ struct channel *sender; /* Channel used to send the route to the routing table */
struct rta *attrs; /* Attributes of this route */ struct rta *attrs; /* Attributes of this route */
u32 id; /* Table specific route id */ u32 id; /* Table specific route id */
byte flags; /* Flags (REF_...) */ byte flags; /* Flags (REF_...) */
byte pflags; /* Protocol-specific flags */ byte pflags; /* Protocol-specific flags */
word pref; /* Route preference */
btime lastmod; /* Last modified */ btime lastmod; /* Last modified */
union { /* Protocol-dependent data (metrics etc.) */
#ifdef CONFIG_RIP
struct {
struct iface *from; /* Incoming iface */
u8 metric; /* RIP metric */
u16 tag; /* External route tag */
} rip;
#endif
#ifdef CONFIG_OSPF
struct {
u32 metric1, metric2; /* OSPF Type 1 and Type 2 metrics */
u32 tag; /* External route tag */
u32 router_id; /* Router that originated this route */
} ospf;
#endif
#ifdef CONFIG_BGP
struct {
u8 suppressed; /* Used for deterministic MED comparison */
s8 stale; /* Route is LLGR_STALE, -1 if unknown */
struct rtable *base_table; /* Base table for Flowspec validation */
} bgp;
#endif
#ifdef CONFIG_BABEL
struct {
u16 seqno; /* Babel seqno */
u16 metric; /* Babel metric */
u64 router_id; /* Babel router id */
} babel;
#endif
struct { /* Routes generated by krt sync (both temporary and inherited ones) */
s8 src; /* Alleged route source (see krt.h) */
u8 proto; /* Kernel source protocol ID */
u8 seen; /* Seen during last scan */
u8 best; /* Best route in network, propagated to core */
u32 metric; /* Kernel metric */
} krt;
} u;
} rte; } rte;
#define REF_COW 1 /* Copy this rte on write */ #define REF_COW 1 /* Copy this rte on write */
@ -354,10 +317,10 @@ net *net_get(rtable *tab, const net_addr *addr);
net *net_route(rtable *tab, const net_addr *n); net *net_route(rtable *tab, const net_addr *n);
int net_roa_check(rtable *tab, const net_addr *n, u32 asn); int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
rte *rte_find(net *net, struct rte_src *src); rte *rte_find(net *net, struct rte_src *src);
rte *rte_get_temp(struct rta *); rte *rte_get_temp(struct rta *, struct rte_src *src);
void rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src); void rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
/* rte_update() moved to protocol.h to avoid dependency conflicts */ /* rte_update() moved to protocol.h to avoid dependency conflicts */
int rt_examine(rtable *t, net_addr *a, struct proto *p, const struct filter *filter); int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter);
rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent); rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent);
void rt_refresh_begin(rtable *t, struct channel *c); void rt_refresh_begin(rtable *t, struct channel *c);
void rt_refresh_end(rtable *t, struct channel *c); void rt_refresh_end(rtable *t, struct channel *c);
@ -368,10 +331,6 @@ void rte_free(rte *);
rte *rte_do_cow(rte *); rte *rte_do_cow(rte *);
static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r) : r; } static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r) : r; }
rte *rte_cow_rta(rte *r, linpool *lp); rte *rte_cow_rta(rte *r, linpool *lp);
void rte_init_tmp_attrs(struct rte *r, linpool *lp, uint max);
void rte_make_tmp_attr(struct rte *r, uint id, uint type, uintptr_t val);
void rte_make_tmp_attrs(struct rte **r, struct linpool *pool, struct rta **old_attrs);
uintptr_t rte_store_tmp_attr(struct rte *r, uint id);
void rt_dump(rtable *); void rt_dump(rtable *);
void rt_dump_all(void); void rt_dump_all(void);
int rt_feed_channel(struct channel *c); int rt_feed_channel(struct channel *c);
@ -489,18 +448,17 @@ typedef struct rta {
u32 uc; /* Use count */ u32 uc; /* Use count */
u32 hash_key; /* Hash over important fields */ u32 hash_key; /* Hash over important fields */
struct ea_list *eattrs; /* Extended Attribute chain */ struct ea_list *eattrs; /* Extended Attribute chain */
struct rte_src *src; /* Route source that created the route */
struct hostentry *hostentry; /* Hostentry for recursive next-hops */ struct hostentry *hostentry; /* Hostentry for recursive next-hops */
ip_addr from; /* Advertising router */ ip_addr from; /* Advertising router */
u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */ u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */
u8 source; /* Route source (RTS_...) */ u16 cached:1; /* Are attributes cached? */
u8 scope; /* Route scope (SCOPE_... -- see ip.h) */ u16 source:7; /* Route source (RTS_...) */
u8 dest; /* Route destination type (RTD_...) */ u16 scope:4; /* Route scope (SCOPE_... -- see ip.h) */
u8 aflags; u16 dest:4; /* Route destination type (RTD_...) */
word pref;
struct nexthop nh; /* Next hop */ struct nexthop nh; /* Next hop */
} rta; } rta;
#define RTS_DUMMY 0 /* Dummy route to be removed soon */
#define RTS_STATIC 1 /* Normal static route */ #define RTS_STATIC 1 /* Normal static route */
#define RTS_INHERIT 2 /* Route inherited from kernel */ #define RTS_INHERIT 2 /* Route inherited from kernel */
#define RTS_DEVICE 3 /* Device route */ #define RTS_DEVICE 3 /* Device route */
@ -518,11 +476,6 @@ typedef struct rta {
#define RTS_PERF 15 /* Perf checker */ #define RTS_PERF 15 /* Perf checker */
#define RTS_MAX 16 #define RTS_MAX 16
#define RTC_UNICAST 0
#define RTC_BROADCAST 1
#define RTC_MULTICAST 2
#define RTC_ANYCAST 3 /* IPv6 Anycast */
#define RTD_NONE 0 /* Undefined next hop */ #define RTD_NONE 0 /* Undefined next hop */
#define RTD_UNICAST 1 /* Next hop is neighbor router */ #define RTD_UNICAST 1 /* Next hop is neighbor router */
#define RTD_BLACKHOLE 2 /* Silently drop packets */ #define RTD_BLACKHOLE 2 /* Silently drop packets */
@ -530,8 +483,6 @@ typedef struct rta {
#define RTD_PROHIBIT 4 /* Administratively prohibited */ #define RTD_PROHIBIT 4 /* Administratively prohibited */
#define RTD_MAX 5 #define RTD_MAX 5
#define RTAF_CACHED 1 /* This is a cached rta */
#define IGP_METRIC_UNKNOWN 0x80000000 /* Default igp_metric used when no other #define IGP_METRIC_UNKNOWN 0x80000000 /* Default igp_metric used when no other
protocol-specific metric is availabe */ protocol-specific metric is availabe */
@ -553,10 +504,13 @@ static inline int rte_is_reachable(rte *r)
typedef struct eattr { typedef struct eattr {
word id; /* EA_CODE(PROTOCOL_..., protocol-dependent ID) */ word id; /* EA_CODE(PROTOCOL_..., protocol-dependent ID) */
byte flags; /* Protocol-dependent flags */ byte flags; /* Protocol-dependent flags */
byte type; /* Attribute type and several flags (EAF_...) */ byte type:5; /* Attribute type */
byte originated:1; /* The attribute has originated locally */
byte fresh:1; /* An uncached attribute (e.g. modified in export filter) */
byte undef:1; /* Explicitly undefined */
union { union {
u32 data; uintptr_t data;
const struct adata *ptr; /* Attribute data elsewhere */ const struct adata *ptr; /* Attribute data elsewhere */
} u; } u;
} eattr; } eattr;
@ -564,7 +518,6 @@ typedef struct eattr {
#define EA_CODE(proto,id) (((proto) << 8) | (id)) #define EA_CODE(proto,id) (((proto) << 8) | (id))
#define EA_ID(ea) ((ea) & 0xff) #define EA_ID(ea) ((ea) & 0xff)
#define EA_PROTO(ea) ((ea) >> 8) #define EA_PROTO(ea) ((ea) >> 8)
#define EA_ID_FLAG(ea) (1 << EA_ID(ea))
#define EA_CUSTOM(id) ((id) | EA_CUSTOM_BIT) #define EA_CUSTOM(id) ((id) | EA_CUSTOM_BIT)
#define EA_IS_CUSTOM(ea) ((ea) & EA_CUSTOM_BIT) #define EA_IS_CUSTOM(ea) ((ea) & EA_CUSTOM_BIT)
#define EA_CUSTOM_ID(ea) ((ea) & ~EA_CUSTOM_BIT) #define EA_CUSTOM_ID(ea) ((ea) & ~EA_CUSTOM_BIT)
@ -589,11 +542,9 @@ const char *ea_custom_name(uint ea);
#define EAF_TYPE_INT_SET 0x0a /* Set of u32's (e.g., a community list) */ #define EAF_TYPE_INT_SET 0x0a /* Set of u32's (e.g., a community list) */
#define EAF_TYPE_EC_SET 0x0e /* Set of pairs of u32's - ext. community list */ #define EAF_TYPE_EC_SET 0x0e /* Set of pairs of u32's - ext. community list */
#define EAF_TYPE_LC_SET 0x12 /* Set of triplets of u32's - large community list */ #define EAF_TYPE_LC_SET 0x12 /* Set of triplets of u32's - large community list */
#define EAF_TYPE_UNDEF 0x1f /* `force undefined' entry */ #define EAF_TYPE_IFACE 0x16 /* Interface pointer stored in adata */
#define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */ #define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */
#define EAF_VAR_LENGTH 0x02 /* Attribute length is variable (part of type spec) */ #define EAF_VAR_LENGTH 0x02 /* Attribute length is variable (part of type spec) */
#define EAF_ORIGINATED 0x20 /* The attribute has originated locally */
#define EAF_FRESH 0x40 /* An uncached attribute (e.g. modified in export filter) */
typedef struct adata { typedef struct adata {
uint length; /* Length of data */ uint length; /* Length of data */
@ -625,7 +576,6 @@ typedef struct ea_list {
#define EALF_SORTED 1 /* Attributes are sorted by code */ #define EALF_SORTED 1 /* Attributes are sorted by code */
#define EALF_BISECT 2 /* Use interval bisection for searching */ #define EALF_BISECT 2 /* Use interval bisection for searching */
#define EALF_CACHED 4 /* Attributes belonging to cached rta */ #define EALF_CACHED 4 /* Attributes belonging to cached rta */
#define EALF_TEMP 8 /* Temporary ea_list added by make_tmp_attrs hooks */
struct rte_src *rt_find_source(struct proto *p, u32 id); struct rte_src *rt_find_source(struct proto *p, u32 id);
struct rte_src *rt_get_source(struct proto *p, u32 id); struct rte_src *rt_get_source(struct proto *p, u32 id);
@ -641,7 +591,7 @@ struct ea_walk_state {
eattr *ea_find(ea_list *, unsigned ea); eattr *ea_find(ea_list *, unsigned ea);
eattr *ea_walk(struct ea_walk_state *s, uint id, uint max); eattr *ea_walk(struct ea_walk_state *s, uint id, uint max);
int ea_get_int(ea_list *, unsigned ea, int def); uintptr_t ea_get_int(ea_list *, unsigned ea, uintptr_t def);
void ea_dump(ea_list *); void ea_dump(ea_list *);
void ea_sort(ea_list *); /* Sort entries in all sub-lists */ void ea_sort(ea_list *); /* Sort entries in all sub-lists */
unsigned ea_scan(ea_list *); /* How many bytes do we need for merged ea_list */ unsigned ea_scan(ea_list *); /* How many bytes do we need for merged ea_list */
@ -662,27 +612,50 @@ void ea_format_bitfield(const struct eattr *a, byte *buf, int bufsize, const cha
ea = NULL; \ ea = NULL; \
} while(0) \ } while(0) \
struct ea_one_attr_list {
ea_list l;
eattr a;
};
static inline eattr * static inline eattr *
ea_set_attr(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, uintptr_t val) ea_set_attr(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, uintptr_t val)
{ {
ea_list *a = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr)); struct ea_one_attr_list *ea = lp_alloc(pool, sizeof(*ea));
eattr *e = &a->attrs[0]; *ea = (struct ea_one_attr_list) {
.l.flags = EALF_SORTED,
.l.count = 1,
.l.next = *to,
a->flags = EALF_SORTED; .a.id = id,
a->count = 1; .a.type = type,
a->next = *to; .a.flags = flags,
*to = a; };
e->id = id;
e->type = type;
e->flags = flags;
if (type & EAF_EMBEDDED) if (type & EAF_EMBEDDED)
e->u.data = (u32) val; ea->a.u.data = val;
else else
e->u.ptr = (struct adata *) val; ea->a.u.ptr = (struct adata *) val;
return e; *to = &ea->l;
return &ea->a;
}
static inline void
ea_unset_attr(ea_list **to, struct linpool *pool, _Bool local, uint code)
{
struct ea_one_attr_list *ea = lp_alloc(pool, sizeof(*ea));
*ea = (struct ea_one_attr_list) {
.l.flags = EALF_SORTED,
.l.count = 1,
.l.next = *to,
.a.id = code,
.a.fresh = local,
.a.originated = local,
.a.undef = 1,
};
*to = &ea->l;
} }
static inline void static inline void
@ -720,7 +693,7 @@ void rta_init(void);
static inline size_t rta_size(const rta *a) { return sizeof(rta) + sizeof(u32)*a->nh.labels; } static inline size_t rta_size(const rta *a) { return sizeof(rta) + sizeof(u32)*a->nh.labels; }
#define RTA_MAX_SIZE (sizeof(rta) + sizeof(u32)*MPLS_MAX_LABEL_STACK) #define RTA_MAX_SIZE (sizeof(rta) + sizeof(u32)*MPLS_MAX_LABEL_STACK)
rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */ rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */
static inline int rta_is_cached(rta *r) { return r->aflags & RTAF_CACHED; } static inline int rta_is_cached(rta *r) { return r->cached; }
static inline rta *rta_clone(rta *r) { r->uc++; return r; } static inline rta *rta_clone(rta *r) { r->uc++; return r; }
void rta__free(rta *r); void rta__free(rta *r);
static inline void rta_free(rta *r) { if (r && !--r->uc) rta__free(r); } static inline void rta_free(rta *r) { if (r && !--r->uc) rta__free(r); }

View file

@ -61,7 +61,6 @@
const adata null_adata; /* adata of length 0 */ const adata null_adata; /* adata of length 0 */
const char * const rta_src_names[RTS_MAX] = { const char * const rta_src_names[RTS_MAX] = {
[RTS_DUMMY] = "",
[RTS_STATIC] = "static", [RTS_STATIC] = "static",
[RTS_INHERIT] = "inherit", [RTS_INHERIT] = "inherit",
[RTS_DEVICE] = "device", [RTS_DEVICE] = "device",
@ -155,7 +154,7 @@ rt_prune_sources(void)
{ {
HASH_DO_REMOVE(src_hash, RSH, sp); HASH_DO_REMOVE(src_hash, RSH, sp);
idm_free(&src_ids, src->global_id); idm_free(&src_ids, src->global_id);
sl_free(rte_src_slab, src); sl_free(src);
} }
} }
HASH_WALK_FILTER_END; HASH_WALK_FILTER_END;
@ -392,7 +391,7 @@ nexthop_free(struct nexthop *o)
while (o) while (o)
{ {
n = o->next; n = o->next;
sl_free(nexthop_slab(o), o); sl_free(o);
o = n; o = n;
} }
} }
@ -449,8 +448,7 @@ ea_find(ea_list *e, unsigned id)
{ {
eattr *a = ea__find(e, id & EA_CODE_MASK); eattr *a = ea__find(e, id & EA_CODE_MASK);
if (a && (a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF && if (a && a->undef && !(id & EA_ALLOW_UNDEF))
!(id & EA_ALLOW_UNDEF))
return NULL; return NULL;
return a; return a;
} }
@ -517,7 +515,7 @@ ea_walk(struct ea_walk_state *s, uint id, uint max)
BIT32_SET(s->visited, n); BIT32_SET(s->visited, n);
if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF) if (a->undef)
continue; continue;
s->eattrs = e; s->eattrs = e;
@ -541,8 +539,8 @@ ea_walk(struct ea_walk_state *s, uint id, uint max)
* by calling ea_find() to find the attribute, extracting its value or returning * by calling ea_find() to find the attribute, extracting its value or returning
* a provided default if no such attribute is present. * a provided default if no such attribute is present.
*/ */
int uintptr_t
ea_get_int(ea_list *e, unsigned id, int def) ea_get_int(ea_list *e, unsigned id, uintptr_t def)
{ {
eattr *a = ea_find(e, id); eattr *a = ea_find(e, id);
if (!a) if (!a)
@ -617,14 +615,17 @@ ea_do_prune(ea_list *e)
/* Now s0 is the most recent version, s[-1] the oldest one */ /* Now s0 is the most recent version, s[-1] the oldest one */
/* Drop undefs */ /* Drop undefs */
if ((s0->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF) if (s0->undef)
continue; continue;
/* Copy the newest version to destination */ /* Copy the newest version to destination */
*d = *s0; *d = *s0;
/* Preserve info whether it originated locally */ /* Preserve info whether it originated locally */
d->type = (d->type & ~(EAF_ORIGINATED|EAF_FRESH)) | (s[-1].type & EAF_ORIGINATED); d->originated = s[-1].originated;
/* Not fresh any more, we prefer surstroemming */
d->fresh = 0;
/* Next destination */ /* Next destination */
d++; d++;
@ -738,6 +739,9 @@ ea_same(ea_list *x, ea_list *y)
if (a->id != b->id || if (a->id != b->id ||
a->flags != b->flags || a->flags != b->flags ||
a->type != b->type || a->type != b->type ||
a->originated != b->originated ||
a->fresh != b->fresh ||
a->undef != b->undef ||
((a->type & EAF_EMBEDDED) ? a->u.data != b->u.data : !adata_same(a->u.ptr, b->u.ptr))) ((a->type & EAF_EMBEDDED) ? a->u.data != b->u.data : !adata_same(a->u.ptr, b->u.ptr)))
return 0; return 0;
} }
@ -940,6 +944,10 @@ ea_show(struct cli *c, const eattr *e)
{ {
*pos++ = ':'; *pos++ = ':';
*pos++ = ' '; *pos++ = ' ';
if (e->undef)
bsprintf(pos, "undefined");
else
switch (e->type & EAF_TYPE_MASK) switch (e->type & EAF_TYPE_MASK)
{ {
case EAF_TYPE_INT: case EAF_TYPE_INT:
@ -969,7 +977,6 @@ ea_show(struct cli *c, const eattr *e)
case EAF_TYPE_LC_SET: case EAF_TYPE_LC_SET:
ea_show_lc_set(c, ad, pos, buf, end); ea_show_lc_set(c, ad, pos, buf, end);
return; return;
case EAF_TYPE_UNDEF:
default: default:
bsprintf(pos, "<type %02x>", e->type); bsprintf(pos, "<type %02x>", e->type);
} }
@ -1005,7 +1012,7 @@ ea_dump(ea_list *e)
eattr *a = &e->attrs[i]; eattr *a = &e->attrs[i];
debug(" %02x:%02x.%02x", EA_PROTO(a->id), EA_ID(a->id), a->flags); debug(" %02x:%02x.%02x", EA_PROTO(a->id), EA_ID(a->id), a->flags);
debug("=%c", "?iO?I?P???S?????" [a->type & EAF_TYPE_MASK]); debug("=%c", "?iO?I?P???S?????" [a->type & EAF_TYPE_MASK]);
if (a->type & EAF_ORIGINATED) if (a->originated)
debug("o"); debug("o");
if (a->type & EAF_EMBEDDED) if (a->type & EAF_EMBEDDED)
debug(":%08x", a->u.data); debug(":%08x", a->u.data);
@ -1104,13 +1111,14 @@ rta_hash(rta *a)
u64 h; u64 h;
mem_hash_init(&h); mem_hash_init(&h);
#define MIX(f) mem_hash_mix(&h, &(a->f), sizeof(a->f)); #define MIX(f) mem_hash_mix(&h, &(a->f), sizeof(a->f));
MIX(src); #define BMIX(f) mem_hash_mix_num(&h, a->f);
MIX(hostentry); MIX(hostentry);
MIX(from); MIX(from);
MIX(igp_metric); MIX(igp_metric);
MIX(source); BMIX(source);
MIX(scope); BMIX(scope);
MIX(dest); BMIX(dest);
MIX(pref);
#undef MIX #undef MIX
return mem_hash_value(&h) ^ nexthop_hash(&(a->nh)) ^ ea_hash(a->eattrs); return mem_hash_value(&h) ^ nexthop_hash(&(a->nh)) ^ ea_hash(a->eattrs);
@ -1119,8 +1127,7 @@ rta_hash(rta *a)
static inline int static inline int
rta_same(rta *x, rta *y) rta_same(rta *x, rta *y)
{ {
return (x->src == y->src && return (x->source == y->source &&
x->source == y->source &&
x->scope == y->scope && x->scope == y->scope &&
x->dest == y->dest && x->dest == y->dest &&
x->igp_metric == y->igp_metric && x->igp_metric == y->igp_metric &&
@ -1198,7 +1205,7 @@ rta_lookup(rta *o)
rta *r; rta *r;
uint h; uint h;
ASSERT(!(o->aflags & RTAF_CACHED)); ASSERT(!o->cached);
if (o->eattrs) if (o->eattrs)
ea_normalize(o->eattrs); ea_normalize(o->eattrs);
@ -1209,8 +1216,7 @@ rta_lookup(rta *o)
r = rta_copy(o); r = rta_copy(o);
r->hash_key = h; r->hash_key = h;
r->aflags = RTAF_CACHED; r->cached = 1;
rt_lock_source(r->src);
rt_lock_hostentry(r->hostentry); rt_lock_hostentry(r->hostentry);
rta_insert(r); rta_insert(r);
@ -1223,18 +1229,17 @@ rta_lookup(rta *o)
void void
rta__free(rta *a) rta__free(rta *a)
{ {
ASSERT(rta_cache_count && (a->aflags & RTAF_CACHED)); ASSERT(rta_cache_count && a->cached);
rta_cache_count--; rta_cache_count--;
*a->pprev = a->next; *a->pprev = a->next;
if (a->next) if (a->next)
a->next->pprev = a->pprev; a->next->pprev = a->pprev;
rt_unlock_hostentry(a->hostentry); rt_unlock_hostentry(a->hostentry);
rt_unlock_source(a->src);
if (a->nh.next) if (a->nh.next)
nexthop_free(a->nh.next); nexthop_free(a->nh.next);
ea_free(a->eattrs); ea_free(a->eattrs);
a->aflags = 0; /* Poison the entry */ a->cached = 0;
sl_free(rta_slab(a), a); sl_free(a);
} }
rta * rta *
@ -1248,7 +1253,7 @@ rta_do_cow(rta *o, linpool *lp)
memcpy(*nhn, nho, nexthop_size(nho)); memcpy(*nhn, nho, nexthop_size(nho));
nhn = &((*nhn)->next); nhn = &((*nhn)->next);
} }
r->aflags = 0; r->cached = 0;
r->uc = 0; r->uc = 0;
return r; return r;
} }
@ -1262,16 +1267,16 @@ rta_do_cow(rta *o, linpool *lp)
void void
rta_dump(rta *a) rta_dump(rta *a)
{ {
static char *rts[] = { "RTS_DUMMY", "RTS_STATIC", "RTS_INHERIT", "RTS_DEVICE", static char *rts[] = { "", "RTS_STATIC", "RTS_INHERIT", "RTS_DEVICE",
"RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP", "RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP",
"RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1", "RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1",
"RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL" }; "RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL" };
static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" }; static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };
debug("p=%s uc=%d %s %s%s h=%04x", debug("pref=%d uc=%d %s %s%s h=%04x",
a->src->proto->name, a->uc, rts[a->source], ip_scope_text(a->scope), a->pref, a->uc, rts[a->source], ip_scope_text(a->scope),
rtd[a->dest], a->hash_key); rtd[a->dest], a->hash_key);
if (!(a->aflags & RTAF_CACHED)) if (!a->cached)
debug(" !CACHED"); debug(" !CACHED");
debug(" <-%I", a->from); debug(" <-%I", a->from);
if (a->dest == RTD_UNICAST) if (a->dest == RTD_UNICAST)

View file

@ -83,7 +83,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
struct rte_src *src = rt_get_source(P, ad->iface->index); struct rte_src *src = rt_get_source(P, ad->iface->index);
rta a0 = { rta a0 = {
.src = src, .pref = c->preference,
.source = RTS_DEVICE, .source = RTS_DEVICE,
.scope = SCOPE_UNIVERSE, .scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST, .dest = RTD_UNICAST,
@ -91,7 +91,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
}; };
a = rta_lookup(&a0); a = rta_lookup(&a0);
e = rte_get_temp(a); e = rte_get_temp(a, src);
e->pflags = 0; e->pflags = 0;
rte_update2(c, net, e, src); rte_update2(c, net, e, src);
} }
@ -195,3 +195,9 @@ struct protocol proto_device = {
.reconfigure = dev_reconfigure, .reconfigure = dev_reconfigure,
.copy_config = dev_copy_config .copy_config = dev_copy_config
}; };
void
dev_build(void)
{
proto_build(&proto_device);
}

View file

@ -475,7 +475,7 @@ fib_delete(struct fib *f, void *E)
} }
if (f->fib_slab) if (f->fib_slab)
sl_free(f->fib_slab, E); sl_free(E);
else else
mb_free(E); mb_free(E);

View file

@ -57,17 +57,17 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
if (d->verbose && !rta_is_cached(a) && a->eattrs) if (d->verbose && !rta_is_cached(a) && a->eattrs)
ea_normalize(a->eattrs); ea_normalize(a->eattrs);
get_route_info = a->src->proto->proto->get_route_info; get_route_info = e->src->proto->proto->get_route_info;
if (get_route_info) if (get_route_info)
get_route_info(e, info); get_route_info(e, info);
else else
bsprintf(info, " (%d)", e->pref); bsprintf(info, " (%d)", a->pref);
if (d->last_table != d->tab) if (d->last_table != d->tab)
rt_show_table(c, d); rt_show_table(c, d);
cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest), cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest),
a->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info); e->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
if (a->dest == RTD_UNICAST) if (a->dest == RTD_UNICAST)
for (nh = &(a->nh); nh; nh = nh->next) for (nh = &(a->nh); nh; nh = nh->next)
@ -127,7 +127,6 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
continue; continue;
ee = e; ee = e;
rte_make_tmp_attrs(&e, c->show_pool, NULL);
/* Export channel is down, do not try to export routes to it */ /* Export channel is down, do not try to export routes to it */
if (ec && (ec->export_state == ES_DOWN)) if (ec && (ec->export_state == ES_DOWN))
@ -154,7 +153,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
else if (d->export_mode) else if (d->export_mode)
{ {
struct proto *ep = ec->proto; struct proto *ep = ec->proto;
int ic = ep->preexport ? ep->preexport(ep, &e, c->show_pool) : 0; int ic = ep->preexport ? ep->preexport(ec, e) : 0;
if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED) if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED)
pass = 1; pass = 1;
@ -180,7 +179,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
} }
} }
if (d->show_protocol && (d->show_protocol != e->attrs->src->proto)) if (d->show_protocol && (d->show_protocol != e->src->proto))
goto skip; goto skip;
if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT) if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT)
@ -393,7 +392,7 @@ rt_show_get_default_tables(struct rt_show_data *d)
} }
for (int i=1; i<NET_MAX; i++) for (int i=1; i<NET_MAX; i++)
if (config->def_tables[i]) if (config->def_tables[i] && config->def_tables[i]->table)
rt_show_add_table(d, config->def_tables[i]->table); rt_show_add_table(d, config->def_tables[i]->table);
} }

View file

@ -549,7 +549,7 @@ rte_find(net *net, struct rte_src *src)
{ {
rte *e = net->routes; rte *e = net->routes;
while (e && e->attrs->src != src) while (e && e->src != src)
e = e->next; e = e->next;
return e; return e;
} }
@ -564,14 +564,14 @@ rte_find(net *net, struct rte_src *src)
* the protocol. * the protocol.
*/ */
rte * rte *
rte_get_temp(rta *a) rte_get_temp(rta *a, struct rte_src *src)
{ {
rte *e = sl_alloc(rte_slab); rte *e = sl_alloc(rte_slab);
e->attrs = a; e->attrs = a;
e->id = 0; e->id = 0;
e->flags = 0; e->flags = 0;
e->pref = 0; rt_lock_source(e->src = src);
return e; return e;
} }
@ -581,6 +581,8 @@ rte_do_cow(rte *r)
rte *e = sl_alloc(rte_slab); rte *e = sl_alloc(rte_slab);
memcpy(e, r, sizeof(rte)); memcpy(e, r, sizeof(rte));
rt_lock_source(e->src);
e->attrs = rta_clone(r->attrs); e->attrs = rta_clone(r->attrs);
e->flags = 0; e->flags = 0;
return e; return e;
@ -618,176 +620,6 @@ rte_cow_rta(rte *r, linpool *lp)
return r; return r;
} }
/**
* rte_init_tmp_attrs - initialize temporary ea_list for route
* @r: route entry to be modified
* @lp: linpool from which to allocate attributes
* @max: maximum number of added temporary attribus
*
* This function is supposed to be called from make_tmp_attrs() and
* store_tmp_attrs() hooks before rte_make_tmp_attr() / rte_store_tmp_attr()
* functions. It allocates &ea_list with length for @max items for temporary
* attributes and puts it on top of eattrs stack.
*/
void
rte_init_tmp_attrs(rte *r, linpool *lp, uint max)
{
struct ea_list *e = lp_alloc(lp, sizeof(struct ea_list) + max * sizeof(eattr));
e->next = r->attrs->eattrs;
e->flags = EALF_SORTED | EALF_TEMP;
e->count = 0;
r->attrs->eattrs = e;
}
/**
* rte_make_tmp_attr - make temporary eattr from private route fields
* @r: route entry to be modified
* @id: attribute ID
* @type: attribute type
* @val: attribute value (u32 or adata ptr)
*
* This function is supposed to be called from make_tmp_attrs() hook for
* each temporary attribute, after temporary &ea_list was initialized by
* rte_init_tmp_attrs(). It checks whether temporary attribute is supposed to
* be defined (based on route pflags) and if so then it fills &eattr field in
* preallocated temporary &ea_list on top of route @r eattrs stack.
*
* Note that it may require free &eattr in temporary &ea_list, so it must not be
* called more times than @max argument of rte_init_tmp_attrs().
*/
void
rte_make_tmp_attr(rte *r, uint id, uint type, uintptr_t val)
{
if (r->pflags & EA_ID_FLAG(id))
{
ea_list *e = r->attrs->eattrs;
eattr *a = &e->attrs[e->count++];
a->id = id;
a->type = type;
a->flags = 0;
if (type & EAF_EMBEDDED)
a->u.data = (u32) val;
else
a->u.ptr = (struct adata *) val;
}
}
/**
* rte_store_tmp_attr - store temporary eattr to private route fields
* @r: route entry to be modified
* @id: attribute ID
*
* This function is supposed to be called from store_tmp_attrs() hook for
* each temporary attribute, after temporary &ea_list was initialized by
* rte_init_tmp_attrs(). It checks whether temporary attribute is defined in
* route @r eattrs stack, updates route pflags accordingly, undefines it by
* filling &eattr field in preallocated temporary &ea_list on top of the eattrs
* stack, and returns the value. Caller is supposed to store it in the
* appropriate private field.
*
* Note that it may require free &eattr in temporary &ea_list, so it must not be
* called more times than @max argument of rte_init_tmp_attrs()
*/
uintptr_t
rte_store_tmp_attr(rte *r, uint id)
{
ea_list *e = r->attrs->eattrs;
eattr *a = ea_find(e->next, id);
if (a)
{
e->attrs[e->count++] = (struct eattr) { .id = id, .type = EAF_TYPE_UNDEF };
r->pflags |= EA_ID_FLAG(id);
return (a->type & EAF_EMBEDDED) ? a->u.data : (uintptr_t) a->u.ptr;
}
else
{
r->pflags &= ~EA_ID_FLAG(id);
return 0;
}
}
/**
* rte_make_tmp_attrs - prepare route by adding all relevant temporary route attributes
* @r: route entry to be modified (may be replaced if COW)
* @lp: linpool from which to allocate attributes
* @old_attrs: temporary ref to old &rta (may be NULL)
*
* This function expands privately stored protocol-dependent route attributes
* to a uniform &eattr / &ea_list representation. It is essentially a wrapper
* around protocol make_tmp_attrs() hook, which does some additional work like
* ensuring that route @r is writable.
*
* The route @r may be read-only (with %REF_COW flag), in that case rw copy is
* obtained by rte_cow() and @r is replaced. If @rte is originally rw, it may be
* directly modified (and it is never copied).
*
* If the @old_attrs ptr is supplied, the function obtains another reference of
* old cached &rta, that is necessary in some cases (see rte_cow_rta() for
* details). It is freed by rte_store_tmp_attrs(), or manually by rta_free().
*
* Generally, if caller ensures that @r is read-only (e.g. in route export) then
* it may ignore @old_attrs (and set it to NULL), but must handle replacement of
* @r. If caller ensures that @r is writable (e.g. in route import) then it may
* ignore replacement of @r, but it must handle @old_attrs.
*/
void
rte_make_tmp_attrs(rte **r, linpool *lp, rta **old_attrs)
{
void (*make_tmp_attrs)(rte *r, linpool *lp);
make_tmp_attrs = (*r)->attrs->src->proto->make_tmp_attrs;
if (!make_tmp_attrs)
return;
/* We may need to keep ref to old attributes, will be freed in rte_store_tmp_attrs() */
if (old_attrs)
*old_attrs = rta_is_cached((*r)->attrs) ? rta_clone((*r)->attrs) : NULL;
*r = rte_cow_rta(*r, lp);
make_tmp_attrs(*r, lp);
}
/**
* rte_store_tmp_attrs - store temporary route attributes back to private route fields
* @r: route entry to be modified
* @lp: linpool from which to allocate attributes
* @old_attrs: temporary ref to old &rta
*
* This function stores temporary route attributes that were expanded by
* rte_make_tmp_attrs() back to private route fields and also undefines them.
* It is essentially a wrapper around protocol store_tmp_attrs() hook, which
* does some additional work like shortcut if there is no change and cleanup
* of @old_attrs reference obtained by rte_make_tmp_attrs().
*/
static void
rte_store_tmp_attrs(rte *r, linpool *lp, rta *old_attrs)
{
void (*store_tmp_attrs)(rte *rt, linpool *lp);
store_tmp_attrs = r->attrs->src->proto->store_tmp_attrs;
if (!store_tmp_attrs)
return;
ASSERT(!rta_is_cached(r->attrs));
/* If there is no new ea_list, we just skip the temporary ea_list */
ea_list *ea = r->attrs->eattrs;
if (ea && (ea->flags & EALF_TEMP))
r->attrs->eattrs = ea->next;
else
store_tmp_attrs(r, lp);
/* Free ref we got in rte_make_tmp_attrs(), have to do rta_lookup() first */
r->attrs = rta_lookup(r->attrs);
rta_free(old_attrs);
}
static int /* Actually better or at least as good as */ static int /* Actually better or at least as good as */
rte_better(rte *new, rte *old) rte_better(rte *new, rte *old)
{ {
@ -798,20 +630,20 @@ rte_better(rte *new, rte *old)
if (!rte_is_valid(new)) if (!rte_is_valid(new))
return 0; return 0;
if (new->pref > old->pref) if (new->attrs->pref > old->attrs->pref)
return 1; return 1;
if (new->pref < old->pref) if (new->attrs->pref < old->attrs->pref)
return 0; return 0;
if (new->attrs->src->proto->proto != old->attrs->src->proto->proto) if (new->src->proto->proto != old->src->proto->proto)
{ {
/* /*
* If the user has configured protocol preferences, so that two different protocols * If the user has configured protocol preferences, so that two different protocols
* have the same preference, try to break the tie by comparing addresses. Not too * have the same preference, try to break the tie by comparing addresses. Not too
* useful, but keeps the ordering of routes unambiguous. * useful, but keeps the ordering of routes unambiguous.
*/ */
return new->attrs->src->proto->proto > old->attrs->src->proto->proto; return new->src->proto->proto > old->src->proto->proto;
} }
if (better = new->attrs->src->proto->rte_better) if (better = new->src->proto->rte_better)
return better(new, old); return better(new, old);
return 0; return 0;
} }
@ -824,13 +656,13 @@ rte_mergable(rte *pri, rte *sec)
if (!rte_is_valid(pri) || !rte_is_valid(sec)) if (!rte_is_valid(pri) || !rte_is_valid(sec))
return 0; return 0;
if (pri->pref != sec->pref) if (pri->attrs->pref != sec->attrs->pref)
return 0; return 0;
if (pri->attrs->src->proto->proto != sec->attrs->src->proto->proto) if (pri->src->proto->proto != sec->src->proto->proto)
return 0; return 0;
if (mergable = pri->attrs->src->proto->rte_mergable) if (mergable = pri->src->proto->rte_mergable)
return mergable(pri, sec); return mergable(pri, sec);
return 0; return 0;
@ -839,8 +671,8 @@ rte_mergable(rte *pri, rte *sec)
static void static void
rte_trace(struct channel *c, rte *e, int dir, char *msg) rte_trace(struct channel *c, rte *e, int dir, char *msg)
{ {
log(L_TRACE "%s.%s %c %s %N %s", log(L_TRACE "%s.%s %c %s %N %uL %uG %s",
c->proto->name, c->name ?: "?", dir, msg, e->net->n.addr, c->proto->name, c->name ?: "?", dir, msg, e->net->n.addr, e->src->private_id, e->src->global_id,
rta_dest_name(e->attrs->dest)); rta_dest_name(e->attrs->dest));
} }
@ -870,7 +702,7 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int si
rt = rt0; rt = rt0;
*rt_free = NULL; *rt_free = NULL;
v = p->preexport ? p->preexport(p, &rt, pool) : 0; v = p->preexport ? p->preexport(c, rt) : 0;
if (v < 0) if (v < 0)
{ {
if (silent) if (silent)
@ -888,8 +720,6 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int si
goto accept; goto accept;
} }
rte_make_tmp_attrs(&rt, pool, NULL);
v = filter && ((filter == FILTER_REJECT) || v = filter && ((filter == FILTER_REJECT) ||
(f_run(filter, &rt, pool, (f_run(filter, &rt, pool,
(silent ? FF_SILENT : 0)) > F_ACCEPT)); (silent ? FF_SILENT : 0)) > F_ACCEPT));
@ -903,12 +733,6 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int si
goto reject; goto reject;
} }
#ifdef CONFIG_PIPE
/* Pipes need rte with stored tmpattrs, remaining protocols need expanded tmpattrs */
if (p->proto == &proto_pipe)
rte_store_tmp_attrs(rt, pool, NULL);
#endif
accept: accept:
if (rt != rt0) if (rt != rt0)
*rt_free = rt; *rt_free = rt;
@ -1288,7 +1112,20 @@ rte_announce(rtable *tab, uint type, net *net, rte *new, rte *old,
break; break;
case RA_ACCEPTED: case RA_ACCEPTED:
rt_notify_accepted(c, net, new, old, 0); /*
* The (new != old) condition is problematic here, as it would break
* the second usage pattern (announcement after bulk change, used in
* rt_next_hop_update_net(), which sends both new and old as NULL).
*
* But recursive next hops do not work with sorted tables anyways,
* such configuration is forbidden in BGP and not supported in
* rt_notify_accepted().
*
* The condition is needed to eliminate spurious announcements where
* both old and new routes are not valid (so they are NULL).
*/
if (new != old)
rt_notify_accepted(c, net, new, old, 0);
break; break;
case RA_MERGED: case RA_MERGED:
@ -1351,16 +1188,18 @@ rte_validate(rte *e)
void void
rte_free(rte *e) rte_free(rte *e)
{ {
rt_unlock_source(e->src);
if (rta_is_cached(e->attrs)) if (rta_is_cached(e->attrs))
rta_free(e->attrs); rta_free(e->attrs);
sl_free(rte_slab, e); sl_free(e);
} }
static inline void static inline void
rte_free_quick(rte *e) rte_free_quick(rte *e)
{ {
rt_unlock_source(e->src);
rta_free(e->attrs); rta_free(e->attrs);
sl_free(rte_slab, e); sl_free(e);
} }
static int static int
@ -1370,8 +1209,7 @@ rte_same(rte *x, rte *y)
return return
x->attrs == y->attrs && x->attrs == y->attrs &&
x->pflags == y->pflags && x->pflags == y->pflags &&
x->pref == y->pref && x->src == y->src &&
(!x->attrs->src->proto->rte_same || x->attrs->src->proto->rte_same(x, y)) &&
rte_is_filtered(x) == rte_is_filtered(y); rte_is_filtered(x) == rte_is_filtered(y);
} }
@ -1392,7 +1230,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
k = &net->routes; /* Find and remove original route from the same protocol */ k = &net->routes; /* Find and remove original route from the same protocol */
while (old = *k) while (old = *k)
{ {
if (old->attrs->src == src) if (old->src == src)
{ {
/* If there is the same route in the routing table but from /* If there is the same route in the routing table but from
* a different sender, then there are two paths from the * a different sender, then there are two paths from the
@ -1674,26 +1512,6 @@ rte_update_unlock(void)
lp_flush(rte_update_pool); lp_flush(rte_update_pool);
} }
static inline void
rte_hide_dummy_routes(net *net, rte **dummy)
{
if (net->routes && net->routes->attrs->source == RTS_DUMMY)
{
*dummy = net->routes;
net->routes = (*dummy)->next;
}
}
static inline void
rte_unhide_dummy_routes(net *net, rte **dummy)
{
if (*dummy)
{
(*dummy)->next = net->routes;
net->routes = *dummy;
}
}
/** /**
* rte_update - enter a new update to a routing table * rte_update - enter a new update to a routing table
* @table: table to be updated * @table: table to be updated
@ -1742,7 +1560,6 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
// struct proto *p = c->proto; // struct proto *p = c->proto;
struct proto_stats *stats = &c->stats; struct proto_stats *stats = &c->stats;
const struct filter *filter = c->in_filter; const struct filter *filter = c->in_filter;
rte *dummy = NULL;
net *nn; net *nn;
ASSERT(c->channel_state == CS_UP); ASSERT(c->channel_state == CS_UP);
@ -1758,9 +1575,6 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
new->net = nn; new->net = nn;
new->sender = c; new->sender = c;
if (!new->pref)
new->pref = c->preference;
stats->imp_updates_received++; stats->imp_updates_received++;
if (!rte_validate(new)) if (!rte_validate(new))
{ {
@ -1782,9 +1596,6 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
} }
else if (filter) else if (filter)
{ {
rta *old_attrs = NULL;
rte_make_tmp_attrs(&new, rte_update_pool, &old_attrs);
int fr = f_run(filter, &new, rte_update_pool, 0); int fr = f_run(filter, &new, rte_update_pool, 0);
if (fr > F_ACCEPT) if (fr > F_ACCEPT)
{ {
@ -1792,15 +1603,10 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
rte_trace_in(D_FILTERS, c, new, "filtered out"); rte_trace_in(D_FILTERS, c, new, "filtered out");
if (! c->in_keep_filtered) if (! c->in_keep_filtered)
{
rta_free(old_attrs);
goto drop; goto drop;
}
new->flags |= REF_FILTERED; new->flags |= REF_FILTERED;
} }
rte_store_tmp_attrs(new, rte_update_pool, old_attrs);
} }
if (!rta_is_cached(new->attrs)) /* Need to copy attributes */ if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
new->attrs = rta_lookup(new->attrs); new->attrs = rta_lookup(new->attrs);
@ -1824,9 +1630,7 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
recalc: recalc:
/* And recalculate the best route */ /* And recalculate the best route */
rte_hide_dummy_routes(nn, &dummy);
rte_recalculate(c, nn, new, src); rte_recalculate(c, nn, new, src);
rte_unhide_dummy_routes(nn, &dummy);
rte_update_unlock(); rte_update_unlock();
return; return;
@ -1855,7 +1659,7 @@ static inline void
rte_discard(rte *old) /* Non-filtered route deletion, used during garbage collection */ rte_discard(rte *old) /* Non-filtered route deletion, used during garbage collection */
{ {
rte_update_lock(); rte_update_lock();
rte_recalculate(old->sender, old->net, NULL, old->attrs->src); rte_recalculate(old->sender, old->net, NULL, old->src);
rte_update_unlock(); rte_update_unlock();
} }
@ -1875,7 +1679,7 @@ rte_modify(rte *old)
new->flags = (old->flags & ~REF_MODIFY) | REF_COW; new->flags = (old->flags & ~REF_MODIFY) | REF_COW;
} }
rte_recalculate(old->sender, old->net, new, old->attrs->src); rte_recalculate(old->sender, old->net, new, old->src);
} }
rte_update_unlock(); rte_update_unlock();
@ -1883,8 +1687,9 @@ rte_modify(rte *old)
/* Check rtable for best route to given net whether it would be exported do p */ /* Check rtable for best route to given net whether it would be exported do p */
int int
rt_examine(rtable *t, net_addr *a, struct proto *p, const struct filter *filter) rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter)
{ {
struct proto *p = c->proto;
net *n = net_find(t, a); net *n = net_find(t, a);
rte *rt = n ? n->routes : NULL; rte *rt = n ? n->routes : NULL;
@ -1894,12 +1699,9 @@ rt_examine(rtable *t, net_addr *a, struct proto *p, const struct filter *filter)
rte_update_lock(); rte_update_lock();
/* Rest is stripped down export_filter() */ /* Rest is stripped down export_filter() */
int v = p->preexport ? p->preexport(p, &rt, rte_update_pool) : 0; int v = p->preexport ? p->preexport(c, rt) : 0;
if (v == RIC_PROCESS) if (v == RIC_PROCESS)
{
rte_make_tmp_attrs(&rt, rte_update_pool, NULL);
v = (f_run(filter, &rt, rte_update_pool, FF_SILENT) <= F_ACCEPT); v = (f_run(filter, &rt, rte_update_pool, FF_SILENT) <= F_ACCEPT);
}
/* Discard temporary rte */ /* Discard temporary rte */
if (rt != n->routes) if (rt != n->routes)
@ -1999,10 +1801,8 @@ rte_dump(rte *e)
{ {
net *n = e->net; net *n = e->net;
debug("%-1N ", n->n.addr); debug("%-1N ", n->n.addr);
debug("PF=%02x pref=%d ", e->pflags, e->pref); debug("PF=%02x ", e->pflags);
rta_dump(e->attrs); rta_dump(e->attrs);
if (e->attrs->src->proto->proto->dump_attrs)
e->attrs->src->proto->proto->dump_attrs(e);
debug("\n"); debug("\n");
} }
@ -2322,12 +2122,7 @@ static struct resclass rt_class = {
rtable * rtable *
rt_setup(pool *pp, struct rtable_config *cf) rt_setup(pool *pp, struct rtable_config *cf)
{ {
int ns = strlen("Routing table ") + strlen(cf->name) + 1; pool *p = rp_newf(pp, "Routing table %s", cf->name);
void *nb = mb_alloc(pp, ns);
ASSERT_DIE(ns - 1 == bsnprintf(nb, ns, "Routing table %s", cf->name));
pool *p = rp_new(pp, nb);
mb_move(nb, p);
rtable *t = ralloc(p, &rt_class); rtable *t = ralloc(p, &rt_class);
t->rp = p; t->rp = p;
@ -2754,11 +2549,12 @@ rt_next_hop_update_rte(rtable *tab UNUSED, rte *old)
memcpy(mls.stack, &a->nh.label[a->nh.labels - mls.len], mls.len * sizeof(u32)); memcpy(mls.stack, &a->nh.label[a->nh.labels - mls.len], mls.len * sizeof(u32));
rta_apply_hostentry(a, old->attrs->hostentry, &mls); rta_apply_hostentry(a, old->attrs->hostentry, &mls);
a->aflags = 0; a->cached = 0;
rte *e = sl_alloc(rte_slab); rte *e = sl_alloc(rte_slab);
memcpy(e, old, sizeof(rte)); memcpy(e, old, sizeof(rte));
e->attrs = rta_lookup(a); e->attrs = rta_lookup(a);
rt_lock_source(e->src);
return e; return e;
} }
@ -2882,12 +2678,16 @@ static rte *
rt_flowspec_update_rte(rtable *tab, rte *r) rt_flowspec_update_rte(rtable *tab, rte *r)
{ {
#ifdef CONFIG_BGP #ifdef CONFIG_BGP
if ((r->attrs->source != RTS_BGP) || !r->u.bgp.base_table) if ((r->attrs->source != RTS_BGP) || (r->sender->proto != r->src->proto))
return NULL;
struct bgp_channel *bc = (struct bgp_channel *) r->sender;
if (!bc->base_table)
return NULL; return NULL;
const net_addr *n = r->net->n.addr; const net_addr *n = r->net->n.addr;
struct bgp_proto *p = (void *) r->attrs->src->proto; struct bgp_proto *p = (void *) r->src->proto;
int valid = rt_flowspec_check(r->u.bgp.base_table, tab, n, r->attrs, p->is_interior); int valid = rt_flowspec_check(bc->base_table, tab, n, r->attrs, p->is_interior);
int dest = valid ? RTD_NONE : RTD_UNREACHABLE; int dest = valid ? RTD_NONE : RTD_UNREACHABLE;
if (dest == r->attrs->dest) if (dest == r->attrs->dest)
@ -2896,11 +2696,12 @@ rt_flowspec_update_rte(rtable *tab, rte *r)
rta *a = alloca(RTA_MAX_SIZE); rta *a = alloca(RTA_MAX_SIZE);
memcpy(a, r->attrs, rta_size(r->attrs)); memcpy(a, r->attrs, rta_size(r->attrs));
a->dest = dest; a->dest = dest;
a->aflags = 0; a->cached = 0;
rte *new = sl_alloc(rte_slab); rte *new = sl_alloc(rte_slab);
memcpy(new, r, sizeof(rte)); memcpy(new, r, sizeof(rte));
new->attrs = rta_lookup(a); new->attrs = rta_lookup(a);
rt_lock_source(new->src);
return new; return new;
#else #else
@ -2936,8 +2737,8 @@ rt_next_hop_update_net(rtable *tab, net *n)
/* Call a pre-comparison hook */ /* Call a pre-comparison hook */
/* Not really an efficient way to compute this */ /* Not really an efficient way to compute this */
if (e->attrs->src->proto->rte_recalculate) if (e->src->proto->rte_recalculate)
e->attrs->src->proto->rte_recalculate(tab, n, new, e, NULL); e->src->proto->rte_recalculate(tab, n, new, e, NULL);
if (e != old_best) if (e != old_best)
rte_free_quick(e); rte_free_quick(e);
@ -3270,9 +3071,6 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
{ {
net = net_get(tab, n); net = net_get(tab, n);
if (!new->pref)
new->pref = c->preference;
if (!rta_is_cached(new->attrs)) if (!rta_is_cached(new->attrs))
new->attrs = rta_lookup(new->attrs); new->attrs = rta_lookup(new->attrs);
} }
@ -3286,7 +3084,7 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
/* Find the old rte */ /* Find the old rte */
for (pos = &net->routes; old = *pos; pos = &old->next) for (pos = &net->routes; old = *pos; pos = &old->next)
if (old->attrs->src == src) if (old->src == src)
{ {
if (new && rte_same(old, new)) if (new && rte_same(old, new))
{ {
@ -3391,7 +3189,7 @@ rt_reload_channel(struct channel *c)
return 0; return 0;
} }
rte_update2(c, e->net->n.addr, rte_do_cow(e), e->attrs->src); rte_update2(c, e->net->n.addr, rte_do_cow(e), e->src);
} }
c->reload_next_rte = NULL; c->reload_next_rte = NULL;
@ -3474,9 +3272,7 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int re
if (new) if (new)
{ {
net = net_get(tab, n); net = net_get(tab, n);
src = new->attrs->src; src = new->src;
rte_store_tmp_attrs(new, rte_update_pool, NULL);
if (!rta_is_cached(new->attrs)) if (!rta_is_cached(new->attrs))
new->attrs = rta_lookup(new->attrs); new->attrs = rta_lookup(new->attrs);
@ -3484,7 +3280,7 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int re
else else
{ {
net = net_find(tab, n); net = net_find(tab, n);
src = old0->attrs->src; src = old0->src;
if (!net) if (!net)
goto drop_withdraw; goto drop_withdraw;
@ -3492,7 +3288,7 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int re
/* Find the old rte */ /* Find the old rte */
for (pos = &net->routes; old = *pos; pos = &old->next) for (pos = &net->routes; old = *pos; pos = &old->next)
if ((c->ra_mode != RA_ANY) || (old->attrs->src == src)) if ((c->ra_mode != RA_ANY) || (old->src == src))
{ {
if (new && rte_same(old, new)) if (new && rte_same(old, new))
{ {
@ -3641,7 +3437,7 @@ hc_delete_hostentry(struct hostcache *hc, pool *p, struct hostentry *he)
rem_node(&he->ln); rem_node(&he->ln);
hc_remove(hc, he); hc_remove(hc, he);
sl_free(hc->slab, he); sl_free(he);
hc->hash_items--; hc->hash_items--;
if (hc->hash_items < hc->hash_min) if (hc->hash_items < hc->hash_min)
@ -3658,7 +3454,7 @@ rt_init_hostcache(rtable *tab)
hc_alloc_table(hc, tab->rp, HC_DEF_ORDER); hc_alloc_table(hc, tab->rp, HC_DEF_ORDER);
hc->slab = sl_new(tab->rp, sizeof(struct hostentry)); hc->slab = sl_new(tab->rp, sizeof(struct hostentry));
hc->lp = lp_new(tab->rp, LP_GOOD_SIZE(1024)); hc->lp = lp_new(tab->rp);
hc->trie = f_new_trie(hc->lp, 0); hc->trie = f_new_trie(hc->lp, 0);
tab->hostcache = hc; tab->hostcache = hc;
@ -3717,36 +3513,12 @@ rt_get_igp_metric(rte *rt)
if (ea) if (ea)
return ea->u.data; return ea->u.data;
rta *a = rt->attrs; if (rt->attrs->source == RTS_DEVICE)
#ifdef CONFIG_OSPF
if ((a->source == RTS_OSPF) ||
(a->source == RTS_OSPF_IA) ||
(a->source == RTS_OSPF_EXT1))
return rt->u.ospf.metric1;
#endif
#ifdef CONFIG_RIP
if (a->source == RTS_RIP)
return rt->u.rip.metric;
#endif
#ifdef CONFIG_BGP
if (a->source == RTS_BGP)
{
u64 metric = bgp_total_aigp_metric(rt);
return (u32) MIN(metric, (u64) IGP_METRIC_UNKNOWN);
}
#endif
#ifdef CONFIG_BABEL
if (a->source == RTS_BABEL)
return rt->u.babel.metric;
#endif
if (a->source == RTS_DEVICE)
return 0; return 0;
if (rt->src->proto->rte_igp_metric)
return rt->src->proto->rte_igp_metric(rt);
return IGP_METRIC_UNKNOWN; return IGP_METRIC_UNKNOWN;
} }
@ -3770,9 +3542,10 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
{ {
rte *e = n->routes; rte *e = n->routes;
rta *a = e->attrs; rta *a = e->attrs;
pxlen = n->n.addr->pxlen; word pref = a->pref;
if (a->hostentry) for (rte *ee = n->routes; ee; ee = ee->next)
if ((ee->attrs->pref >= pref) && ee->attrs->hostentry)
{ {
/* Recursive route should not depend on another recursive route */ /* Recursive route should not depend on another recursive route */
log(L_WARN "Next hop address %I resolvable through recursive route for %N", log(L_WARN "Next hop address %I resolvable through recursive route for %N",
@ -3780,6 +3553,8 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
goto done; goto done;
} }
pxlen = n->n.addr->pxlen;
if (a->dest == RTD_UNICAST) if (a->dest == RTD_UNICAST)
{ {
for (struct nexthop *nh = &(a->nh); nh; nh = nh->next) for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
@ -3841,6 +3616,7 @@ rt_update_hostcache(rtable *tab)
struct hostentry * struct hostentry *
rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep) rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
{ {
ip_addr link = ipa_zero(ll) ? a : ll;
struct hostentry *he; struct hostentry *he;
if (!tab->hostcache) if (!tab->hostcache)
@ -3849,10 +3625,10 @@ rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
u32 k = hc_hash(a, dep); u32 k = hc_hash(a, dep);
struct hostcache *hc = tab->hostcache; struct hostcache *hc = tab->hostcache;
for (he = hc->hash_table[k >> hc->hash_shift]; he != NULL; he = he->next) for (he = hc->hash_table[k >> hc->hash_shift]; he != NULL; he = he->next)
if (ipa_equal(he->addr, a) && (he->tab == dep)) if (ipa_equal(he->addr, a) && ipa_equal(he->link, link) && (he->tab == dep))
return he; return he;
he = hc_new_hostentry(hc, tab->rp, a, ipa_zero(ll) ? a : ll, dep, k); he = hc_new_hostentry(hc, tab->rp, a, link, dep, k);
rt_update_hostentry(tab, he); rt_update_hostentry(tab, he);
return he; return he;
} }

View file

@ -119,7 +119,7 @@ babel_get_source(struct babel_proto *p, struct babel_entry *e, u64 router_id)
} }
static void static void
babel_expire_sources(struct babel_proto *p, struct babel_entry *e) babel_expire_sources(struct babel_proto *p UNUSED, struct babel_entry *e)
{ {
struct babel_source *n, *nx; struct babel_source *n, *nx;
btime now_ = current_time(); btime now_ = current_time();
@ -129,11 +129,108 @@ babel_expire_sources(struct babel_proto *p, struct babel_entry *e)
if (n->expires && n->expires <= now_) if (n->expires && n->expires <= now_)
{ {
rem_node(NODE n); rem_node(NODE n);
sl_free(p->source_slab, n); sl_free(n);
} }
} }
} }
static u16
babel_calc_smoothed_metric(struct babel_proto *p, struct babel_route *r, u8 update)
{
struct babel_config *cf = (void *) p->p.cf;
uint metric = r->metric, smoothed_metric = r->smoothed_metric;
btime smoothed_time = r->smoothed_time, now = current_time();
if (!cf->metric_decay || metric == BABEL_INFINITY ||
metric == smoothed_metric || !smoothed_time)
{
smoothed_metric = metric;
smoothed_time = now;
goto out;
}
int diff = metric - smoothed_metric;
/*
* The decay defines the half-life of the metric convergence, so first iterate
* in halving steps
*/
while (smoothed_time < now - cf->metric_decay && diff) {
smoothed_metric += diff/2;
smoothed_time += cf->metric_decay;
diff = metric - smoothed_metric;
}
/*
* Then, update remainder in BABEL_SMOOTHING_STEP intervals using the
* exponential function (approximated via the pre-computed reciprocal).
*/
while (smoothed_time < now - BABEL_SMOOTHING_STEP && diff) {
smoothed_metric += (BABEL_SMOOTHING_STEP * diff *
(cf->smooth_recp - BABEL_SMOOTHING_UNIT) / BABEL_SMOOTHING_UNIT);
smoothed_time += BABEL_SMOOTHING_STEP;
diff = metric - smoothed_metric;
}
/* Consider the metric converged once we're close enough */
if (ABS(diff) < BABEL_SMOOTHING_MIN_DIFF)
smoothed_metric = metric;
out:
if (update) {
r->smoothed_metric = smoothed_metric;
r->smoothed_time = smoothed_time;
}
return smoothed_metric;
}
static u16
babel_update_smoothed_metric(struct babel_proto *p, struct babel_route *r)
{
if (!r->metric)
return 0;
if (!r->smoothed_metric) {
r->smoothed_metric = r->metric;
r->smoothed_time = current_time();
return r->smoothed_metric;
}
u16 smoothed = babel_calc_smoothed_metric(p, r, 1);
DBG("Updated smoothed metric for prefix %N: router-id %lR metric %d/%d\n",
r->e->n.addr, r->router_id, r->metric, smoothed);
return smoothed;
}
static u16
babel_smoothed_metric(struct babel_proto *p, struct babel_route *r)
{
return babel_calc_smoothed_metric(p, r, 0);
}
static void
babel_update_metric(struct babel_proto *p, struct babel_route *r, u16 metric)
{
babel_update_smoothed_metric(p, r);
r->metric = metric;
}
static inline u8
babel_route_better(struct babel_proto *p, struct babel_route *mod,
struct babel_route *best)
{
if (!mod->feasible)
return 0;
if (!best)
return mod->metric < BABEL_INFINITY;
return (mod->metric < best->metric &&
babel_smoothed_metric(p, mod) < babel_smoothed_metric(p, best));
}
static struct babel_route * static struct babel_route *
babel_find_route(struct babel_entry *e, struct babel_neighbor *n) babel_find_route(struct babel_entry *e, struct babel_neighbor *n)
{ {
@ -151,8 +248,10 @@ babel_get_route(struct babel_proto *p, struct babel_entry *e, struct babel_neigh
{ {
struct babel_route *r = babel_find_route(e, nbr); struct babel_route *r = babel_find_route(e, nbr);
if (r) if (r) {
babel_update_smoothed_metric(p, r);
return r; return r;
}
r = sl_allocz(p->route_slab); r = sl_allocz(p->route_slab);
@ -174,7 +273,7 @@ babel_retract_route(struct babel_proto *p, struct babel_route *r)
} }
static void static void
babel_flush_route(struct babel_proto *p, struct babel_route *r) babel_flush_route(struct babel_proto *p UNUSED, struct babel_route *r)
{ {
DBG("Babel: Flush route %N router_id %lR neigh %I\n", DBG("Babel: Flush route %N router_id %lR neigh %I\n",
r->e->n.addr, r->router_id, r->neigh->addr); r->e->n.addr, r->router_id, r->neigh->addr);
@ -185,7 +284,7 @@ babel_flush_route(struct babel_proto *p, struct babel_route *r)
if (r->e->selected == r) if (r->e->selected == r)
r->e->selected = NULL; r->e->selected = NULL;
sl_free(p->route_slab, r); sl_free(r);
} }
static void static void
@ -304,7 +403,7 @@ babel_add_seqno_request(struct babel_proto *p, struct babel_entry *e,
struct babel_seqno_request *sr; struct babel_seqno_request *sr;
WALK_LIST(sr, e->requests) WALK_LIST(sr, e->requests)
if (sr->router_id == router_id) if (sr->router_id == router_id && sr->nbr == nbr)
{ {
/* Found matching or newer */ /* Found matching or newer */
if (ge_mod64k(sr->seqno, seqno) && seqno_request_valid(sr)) if (ge_mod64k(sr->seqno, seqno) && seqno_request_valid(sr))
@ -338,30 +437,33 @@ found:
} }
static void static void
babel_remove_seqno_request(struct babel_proto *p, struct babel_seqno_request *sr) babel_remove_seqno_request(struct babel_proto *p UNUSED, struct babel_seqno_request *sr)
{ {
if (sr->nbr) if (sr->nbr)
rem_node(&sr->nbr_node); rem_node(&sr->nbr_node);
rem_node(NODE sr); rem_node(NODE sr);
sl_free(p->seqno_slab, sr); sl_free(sr);
} }
static int static int
babel_satisfy_seqno_request(struct babel_proto *p, struct babel_entry *e, babel_satisfy_seqno_request(struct babel_proto *p, struct babel_entry *e,
u64 router_id, u16 seqno) u64 router_id, u16 seqno)
{ {
struct babel_seqno_request *sr; struct babel_seqno_request *sr, *srx;
int ret = 0;
WALK_LIST(sr, e->requests) WALK_LIST_DELSAFE(sr, srx, e->requests)
if ((sr->router_id == router_id) && ge_mod64k(seqno, sr->seqno)) if ((sr->router_id == router_id) && ge_mod64k(seqno, sr->seqno))
{ {
/* Found the request, remove it */ /* Found a matching request, remove it; there may be multiple outstanding
* requests, so continue looping
*/
babel_remove_seqno_request(p, sr); babel_remove_seqno_request(p, sr);
return 1; ret = 1;
} }
return 0; return ret;
} }
static void static void
@ -434,6 +536,10 @@ babel_get_neighbor(struct babel_iface *ifa, ip_addr addr)
init_list(&nbr->requests); init_list(&nbr->requests);
add_tail(&ifa->neigh_list, NODE nbr); add_tail(&ifa->neigh_list, NODE nbr);
nbr->srtt_pool = mb_allocz(ifa->pool, ifa->cf->rtt_win_len*sizeof(btime));
nbr->srtt_pool_sorted = mb_allocz(ifa->pool, ifa->cf->rtt_win_len*sizeof(btime));
nbr->srtt_poll_idx = nbr->srtt_pool_len = 0;
return nbr; return nbr;
} }
@ -458,6 +564,8 @@ babel_flush_neighbor(struct babel_proto *p, struct babel_neighbor *nbr)
nbr->ifa = NULL; nbr->ifa = NULL;
rem_node(NODE nbr); rem_node(NODE nbr);
mb_free(nbr->srtt_pool);
mb_free(nbr->srtt_pool_sorted);
mb_free(nbr); mb_free(nbr);
} }
@ -569,6 +677,7 @@ babel_update_cost(struct babel_neighbor *nbr)
switch (cf->type) switch (cf->type)
{ {
case BABEL_IFACE_TYPE_WIRED: case BABEL_IFACE_TYPE_WIRED:
case BABEL_IFACE_TYPE_TUNNEL:
/* k-out-of-j selection - Appendix 2.1 in the RFC. */ /* k-out-of-j selection - Appendix 2.1 in the RFC. */
/* Link is bad if less than cf->limit/16 of expected hellos were received */ /* Link is bad if less than cf->limit/16 of expected hellos were received */
@ -597,6 +706,25 @@ babel_update_cost(struct babel_neighbor *nbr)
break; break;
} }
if (cf->rtt_cost && nbr->srtt > cf->rtt_min)
{
uint rtt_cost = cf->rtt_cost;
if (nbr->srtt < cf->rtt_max)
{
uint rtt_interval = cf->rtt_max TO_US - cf->rtt_min TO_US;
uint rtt_diff = (nbr->srtt TO_US - cf->rtt_min TO_US);
rtt_cost = (rtt_cost * rtt_diff) / rtt_interval;
}
txcost = MIN(txcost + rtt_cost, BABEL_INFINITY);
rxcost = MIN(rxcost + rtt_cost, BABEL_INFINITY);
TRACE(D_EVENTS, "Added RTT cost %u to nbr %I on %s with srtt %u.%03u ms",
rtt_cost, nbr->addr, nbr->ifa->iface->name, nbr->srtt/1000, nbr->srtt%1000);
}
done: done:
/* If RX cost changed, send IHU with next Hello */ /* If RX cost changed, send IHU with next Hello */
if (rxcost != nbr->rxcost) if (rxcost != nbr->rxcost)
@ -616,7 +744,7 @@ done:
struct babel_route *r; node *n; struct babel_route *r; node *n;
WALK_LIST2(r, n, nbr->routes, neigh_route) WALK_LIST2(r, n, nbr->routes, neigh_route)
{ {
r->metric = babel_compute_metric(nbr, r->advert_metric); babel_update_metric(p, r, babel_compute_metric(nbr, r->advert_metric));
babel_select_route(p, r->e, r); babel_select_route(p, r->e, r);
} }
} }
@ -640,13 +768,36 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
if (r) if (r)
{ {
rta a0 = { rta a0 = {
.src = p->p.main_source,
.source = RTS_BABEL, .source = RTS_BABEL,
.scope = SCOPE_UNIVERSE, .scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST, .dest = RTD_UNICAST,
.pref = c->preference,
.from = r->neigh->addr, .from = r->neigh->addr,
.nh.gw = r->next_hop, .nh.gw = r->next_hop,
.nh.iface = r->neigh->ifa->iface, .nh.iface = r->neigh->ifa->iface,
.eattrs = alloca(sizeof(ea_list) + 3*sizeof(eattr)),
};
*a0.eattrs = (ea_list) { .count = 3 };
a0.eattrs->attrs[0] = (eattr) {
.id = EA_BABEL_METRIC,
.type = EAF_TYPE_INT,
.u.data = r->metric,
};
struct adata *ad = alloca(sizeof(struct adata) + sizeof(u64));
ad->length = sizeof(u64);
memcpy(ad->data, &(r->router_id), sizeof(u64));
a0.eattrs->attrs[1] = (eattr) {
.id = EA_BABEL_ROUTER_ID,
.type = EAF_TYPE_OPAQUE,
.u.ptr = ad,
};
a0.eattrs->attrs[2] = (eattr) {
.id = EA_BABEL_SEQNO,
.type = EAF_TYPE_INT,
.u.data = r->seqno,
}; };
/* /*
@ -658,11 +809,7 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
a0.nh.flags = RNF_ONLINK; a0.nh.flags = RNF_ONLINK;
rta *a = rta_lookup(&a0); rta *a = rta_lookup(&a0);
rte *rte = rte_get_temp(a); rte *rte = rte_get_temp(a, p->p.main_source);
rte->u.babel.seqno = r->seqno;
rte->u.babel.metric = r->metric;
rte->u.babel.router_id = r->router_id;
rte->pflags = EA_ID_FLAG(EA_BABEL_METRIC) | EA_ID_FLAG(EA_BABEL_ROUTER_ID);
e->unreachable = 0; e->unreachable = 0;
rte_update2(c, e->n.addr, rte, p->p.main_source); rte_update2(c, e->n.addr, rte, p->p.main_source);
@ -671,17 +818,15 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
{ {
/* Unreachable */ /* Unreachable */
rta a0 = { rta a0 = {
.src = p->p.main_source,
.source = RTS_BABEL, .source = RTS_BABEL,
.scope = SCOPE_UNIVERSE, .scope = SCOPE_UNIVERSE,
.dest = RTD_UNREACHABLE, .dest = RTD_UNREACHABLE,
.pref = 1,
}; };
rta *a = rta_lookup(&a0); rta *a = rta_lookup(&a0);
rte *rte = rte_get_temp(a); rte *rte = rte_get_temp(a, p->p.main_source);
memset(&rte->u.babel, 0, sizeof(rte->u.babel));
rte->pflags = 0; rte->pflags = 0;
rte->pref = 1;
e->unreachable = 1; e->unreachable = 1;
rte_update2(c, e->n.addr, rte, p->p.main_source); rte_update2(c, e->n.addr, rte, p->p.main_source);
@ -740,8 +885,7 @@ babel_select_route(struct babel_proto *p, struct babel_entry *e, struct babel_ro
/* Shortcut if only non-best was modified */ /* Shortcut if only non-best was modified */
if (mod && (mod != best)) if (mod && (mod != best))
{ {
/* Either select modified route, or keep old best route */ if (babel_route_better(p, mod, best))
if ((mod->metric < (best ? best->metric : BABEL_INFINITY)) && mod->feasible)
best = mod; best = mod;
else else
return; return;
@ -752,17 +896,24 @@ babel_select_route(struct babel_proto *p, struct babel_entry *e, struct babel_ro
if (!best || (best->metric == BABEL_INFINITY) || !best->feasible) if (!best || (best->metric == BABEL_INFINITY) || !best->feasible)
best = NULL; best = NULL;
/* best will be compared to many routes below, make sure it's up-to-date */
if (best)
babel_update_smoothed_metric(p, best);
/* Find the best feasible route from all routes */ /* Find the best feasible route from all routes */
WALK_LIST(r, e->routes) WALK_LIST(r, e->routes)
if ((r->metric < (best ? best->metric : BABEL_INFINITY)) && r->feasible) if (babel_route_better(p, r, best))
best = r; best = r;
} }
if (best) if (best)
{ {
if (best != e->selected) if (best != e->selected)
TRACE(D_EVENTS, "Picked new route for prefix %N: router-id %lR metric %d", {
e->n.addr, best->router_id, best->metric); u16 smoothed = babel_update_smoothed_metric(p, best);
TRACE(D_EVENTS, "Picked new route for prefix %N: router-id %lR metric %d/%d",
e->n.addr, best->router_id, best->metric, smoothed);
}
} }
else if (e->selected) else if (e->selected)
{ {
@ -811,6 +962,12 @@ babel_build_ihu(union babel_msg *msg, struct babel_iface *ifa, struct babel_neig
msg->ihu.rxcost = n->rxcost; msg->ihu.rxcost = n->rxcost;
msg->ihu.interval = ifa->cf->ihu_interval; msg->ihu.interval = ifa->cf->ihu_interval;
if (n->last_tstamp_rcvd && ifa->cf->rtt_send)
{
msg->ihu.tstamp = n->last_tstamp;
msg->ihu.tstamp_rcvd = n->last_tstamp_rcvd TO_US;
}
TRACE(D_PACKETS, "Sending IHU for %I with rxcost %d interval %t", TRACE(D_PACKETS, "Sending IHU for %I with rxcost %d interval %t",
msg->ihu.addr, msg->ihu.rxcost, (btime) msg->ihu.interval); msg->ihu.addr, msg->ihu.rxcost, (btime) msg->ihu.interval);
} }
@ -850,6 +1007,9 @@ babel_send_hello(struct babel_iface *ifa, uint interval)
msg.hello.seqno = ifa->hello_seqno++; msg.hello.seqno = ifa->hello_seqno++;
msg.hello.interval = interval ?: ifa->cf->hello_interval; msg.hello.interval = interval ?: ifa->cf->hello_interval;
if (ifa->cf->rtt_send)
msg.hello.tstamp = 1; /* real timestamp will be set on TLV write */
TRACE(D_PACKETS, "Sending hello on %s with seqno %d interval %t", TRACE(D_PACKETS, "Sending hello on %s with seqno %d interval %t",
ifa->ifname, msg.hello.seqno, (btime) msg.hello.interval); ifa->ifname, msg.hello.seqno, (btime) msg.hello.interval);
@ -1145,14 +1305,41 @@ babel_handle_hello(union babel_msg *m, struct babel_iface *ifa)
msg->seqno, (btime) msg->interval); msg->seqno, (btime) msg->interval);
struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender); struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender);
struct babel_iface_config *cf = n->ifa->cf;
int first_hello = !n->hello_cnt; int first_hello = !n->hello_cnt;
if (msg->tstamp)
{
n->last_tstamp = msg->tstamp;
n->last_tstamp_rcvd = msg->pkt_received;
}
babel_update_hello_history(n, msg->seqno, msg->interval); babel_update_hello_history(n, msg->seqno, msg->interval);
babel_update_cost(n); babel_update_cost(n);
/* Speed up session establishment by sending IHU immediately */ /* Speed up session establishment by sending IHU immediately */
if (first_hello) if (first_hello)
babel_send_ihu(ifa, n); {
/* if using RTT, all IHUs must be paired with hellos */
if(cf->rtt_send)
babel_send_hello(ifa, 0);
else
babel_send_ihu(ifa, n);
}
}
static int
babel_btime_cmp(const void *b1, const void *b2)
{
btime r;
switch (r = *(btime*)b1 - *(btime*)b2)
{
case 0:
return 0;
break;
default:
return r > (btime)0 ? 1 : -1;
break;
}
} }
void void
@ -1171,6 +1358,51 @@ babel_handle_ihu(union babel_msg *m, struct babel_iface *ifa)
struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender); struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender);
n->txcost = msg->rxcost; n->txcost = msg->rxcost;
n->ihu_expiry = current_time() + BABEL_IHU_EXPIRY_FACTOR(msg->interval); n->ihu_expiry = current_time() + BABEL_IHU_EXPIRY_FACTOR(msg->interval);
if (msg->tstamp)
{
u32 rtt_sample = 0, pkt_received = msg->pkt_received TO_US;
int remote_time, full_time;
/* processing time reported by peer */
remote_time = (n->last_tstamp - msg->tstamp_rcvd);
/* time since we sent the last timestamp - RTT including remote time */
full_time = (pkt_received - msg->tstamp);
/* sanity checks */
if (remote_time < 0 || full_time < 0 ||
remote_time US_ > BABEL_RTT_MAX_VALUE || full_time US_ > BABEL_RTT_MAX_VALUE)
goto out;
if (remote_time < full_time)
rtt_sample = full_time - remote_time;
if (n->srtt)
{
u16 pool_idx = n->srtt_poll_idx % n->ifa->cf->rtt_win_len;
n->srtt_poll_idx = pool_idx + 1;
*(n->srtt_pool + pool_idx) = rtt_sample;
if (n->srtt_pool_len < n->ifa->cf->rtt_win_len)
n->srtt_pool_len++;
memcpy(n->srtt_pool_sorted, n->srtt_pool, (size_t)n->srtt_pool_len*sizeof(btime));
// use qsort for now for testing
qsort(n->srtt_pool_sorted, (size_t)n->srtt_pool_len, sizeof(btime), babel_btime_cmp);
int mid = n->srtt_pool_len / (u16)2;
if (!n->srtt_pool_len || n->srtt_pool_len % (u16)2)
n->srtt = *(n->srtt_pool_sorted + mid);
else
n->srtt = (*(n->srtt_pool_sorted + mid - (u16)1) + *(n->srtt_pool_sorted + mid)) / (u16)2;
}
else {
*(n->srtt_pool) = n->srtt = rtt_sample;
n->srtt_poll_idx = n->srtt_pool_len = 1;
}
TRACE(D_EVENTS, "RTT sample for neighbour %I on %s: %u us (srtt %u.%03u ms)",
n->addr, ifa->ifname, rtt_sample, n->srtt/1000, n->srtt%1000);
}
out:
babel_update_cost(n); babel_update_cost(n);
} }
@ -1287,9 +1519,9 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
return; return;
/* Last paragraph above - update the entry */ /* Last paragraph above - update the entry */
babel_update_metric(p, r, metric);
r->feasible = feasible; r->feasible = feasible;
r->seqno = msg->seqno; r->seqno = msg->seqno;
r->metric = metric;
r->advert_metric = msg->metric; r->advert_metric = msg->metric;
r->router_id = msg->router_id; r->router_id = msg->router_id;
r->next_hop = msg->next_hop; r->next_hop = msg->next_hop;
@ -2009,7 +2241,13 @@ babel_dump(struct proto *P)
static void static void
babel_get_route_info(rte *rte, byte *buf) babel_get_route_info(rte *rte, byte *buf)
{ {
buf += bsprintf(buf, " (%d/%d) [%lR]", rte->pref, rte->u.babel.metric, rte->u.babel.router_id); u64 rid = 0;
eattr *e = ea_find(rte->attrs->eattrs, EA_BABEL_ROUTER_ID);
if (e)
memcpy(&rid, e->u.ptr->data, sizeof(u64));
buf += bsprintf(buf, " (%d/%d) [%lR]", rte->attrs->pref,
ea_get_int(rte->attrs->eattrs, EA_BABEL_METRIC, BABEL_INFINITY), rid);
} }
static int static int
@ -2017,6 +2255,9 @@ babel_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
{ {
switch (a->id) switch (a->id)
{ {
case EA_BABEL_SEQNO:
return GA_FULL;
case EA_BABEL_METRIC: case EA_BABEL_METRIC:
bsprintf(buf, "metric: %d", a->u.data); bsprintf(buf, "metric: %d", a->u.data);
return GA_FULL; return GA_FULL;
@ -2086,8 +2327,8 @@ babel_show_neighbors(struct proto *P, const char *iff)
} }
cli_msg(-1024, "%s:", p->p.name); cli_msg(-1024, "%s:", p->p.name);
cli_msg(-1024, "%-25s %-10s %6s %6s %6s %7s %4s", cli_msg(-1024, "%-25s %-10s %6s %6s %6s %7s %4s %11s",
"IP address", "Interface", "Metric", "Routes", "Hellos", "Expires", "Auth"); "IP address", "Interface", "Metric", "Routes", "Hellos", "Expires", "Auth", "RTT");
WALK_LIST(ifa, p->interfaces) WALK_LIST(ifa, p->interfaces)
{ {
@ -2102,9 +2343,10 @@ babel_show_neighbors(struct proto *P, const char *iff)
uint hellos = u32_popcount(n->hello_map); uint hellos = u32_popcount(n->hello_map);
btime timer = (n->hello_expiry ?: n->init_expiry) - current_time(); btime timer = (n->hello_expiry ?: n->init_expiry) - current_time();
cli_msg(-1024, "%-25I %-10s %6u %6u %6u %7t %-4s", cli_msg(-1024, "%-25I %-10s %6u %6u %6u %7t %-4s %5u.%03ums",
n->addr, ifa->iface->name, n->cost, rts, hellos, MAX(timer, 0), n->addr, ifa->iface->name, n->cost, rts, hellos, MAX(timer, 0),
n->auth_passed ? "Yes" : "No"); n->auth_passed ? "Yes" : "No",
n->srtt/1000, n->srtt%1000);
} }
} }
} }
@ -2230,38 +2472,16 @@ babel_kick_timer(struct babel_proto *p)
static int static int
babel_preexport(struct proto *P, struct rte **new, struct linpool *pool UNUSED) babel_preexport(struct channel *C, struct rte *new)
{ {
struct rta *a = (*new)->attrs; struct rta *a = new->attrs;
/* Reject our own unreachable routes */ /* Reject our own unreachable routes */
if ((a->dest == RTD_UNREACHABLE) && (a->src->proto == P)) if ((a->dest == RTD_UNREACHABLE) && (new->src->proto == C->proto))
return -1; return -1;
return 0; return 0;
} }
static void
babel_make_tmp_attrs(struct rte *rt, struct linpool *pool)
{
struct adata *id = lp_alloc_adata(pool, sizeof(u64));
memcpy(id->data, &rt->u.babel.router_id, sizeof(u64));
rte_init_tmp_attrs(rt, pool, 2);
rte_make_tmp_attr(rt, EA_BABEL_METRIC, EAF_TYPE_INT, rt->u.babel.metric);
rte_make_tmp_attr(rt, EA_BABEL_ROUTER_ID, EAF_TYPE_OPAQUE, (uintptr_t) id);
}
static void
babel_store_tmp_attrs(struct rte *rt, struct linpool *pool)
{
rte_init_tmp_attrs(rt, pool, 2);
rt->u.babel.metric = rte_store_tmp_attr(rt, EA_BABEL_METRIC);
/* EA_BABEL_ROUTER_ID is read-only, we do not really save the value */
rte_store_tmp_attr(rt, EA_BABEL_ROUTER_ID);
}
/* /*
* babel_rt_notify - core tells us about new route (possibly our own), * babel_rt_notify - core tells us about new route (possibly our own),
* so store it into our data structures. * so store it into our data structures.
@ -2276,10 +2496,22 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
if (new) if (new)
{ {
/* Update */ /* Update */
uint internal = (new->attrs->src->proto == P); uint rt_seqno;
uint rt_seqno = internal ? new->u.babel.seqno : p->update_seqno;
uint rt_metric = ea_get_int(new->attrs->eattrs, EA_BABEL_METRIC, 0); uint rt_metric = ea_get_int(new->attrs->eattrs, EA_BABEL_METRIC, 0);
u64 rt_router_id = internal ? new->u.babel.router_id : p->router_id; u64 rt_router_id = 0;
if (new->src->proto == P)
{
rt_seqno = ea_find(new->attrs->eattrs, EA_BABEL_SEQNO)->u.data;
eattr *e = ea_find(new->attrs->eattrs, EA_BABEL_ROUTER_ID);
if (e)
memcpy(&rt_router_id, e->u.ptr->data, sizeof(u64));
}
else
{
rt_seqno = p->update_seqno;
rt_router_id = p->router_id;
}
if (rt_metric > BABEL_INFINITY) if (rt_metric > BABEL_INFINITY)
{ {
@ -2322,15 +2554,16 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
static int static int
babel_rte_better(struct rte *new, struct rte *old) babel_rte_better(struct rte *new, struct rte *old)
{ {
return new->u.babel.metric < old->u.babel.metric; uint new_metric = ea_get_int(new->attrs->eattrs, EA_BABEL_METRIC, BABEL_INFINITY);
uint old_metric = ea_get_int(old->attrs->eattrs, EA_BABEL_METRIC, BABEL_INFINITY);
return new_metric < old_metric;
} }
static int static u32
babel_rte_same(struct rte *new, struct rte *old) babel_rte_igp_metric(struct rte *rt)
{ {
return ((new->u.babel.seqno == old->u.babel.seqno) && return ea_get_int(rt->attrs->eattrs, EA_BABEL_METRIC, BABEL_INFINITY);
(new->u.babel.metric == old->u.babel.metric) &&
(new->u.babel.router_id == old->u.babel.router_id));
} }
@ -2364,10 +2597,8 @@ babel_init(struct proto_config *CF)
P->if_notify = babel_if_notify; P->if_notify = babel_if_notify;
P->rt_notify = babel_rt_notify; P->rt_notify = babel_rt_notify;
P->preexport = babel_preexport; P->preexport = babel_preexport;
P->make_tmp_attrs = babel_make_tmp_attrs;
P->store_tmp_attrs = babel_store_tmp_attrs;
P->rte_better = babel_rte_better; P->rte_better = babel_rte_better;
P->rte_same = babel_rte_same; P->rte_igp_metric = babel_rte_igp_metric;
return P; return P;
} }
@ -2483,3 +2714,9 @@ struct protocol proto_babel = {
.get_route_info = babel_get_route_info, .get_route_info = babel_get_route_info,
.get_attr = babel_get_attr .get_attr = babel_get_attr
}; };
void
babel_build(void)
{
proto_build(&proto_babel);
}

View file

@ -28,6 +28,7 @@
#define EA_BABEL_METRIC EA_CODE(PROTOCOL_BABEL, 0) #define EA_BABEL_METRIC EA_CODE(PROTOCOL_BABEL, 0)
#define EA_BABEL_ROUTER_ID EA_CODE(PROTOCOL_BABEL, 1) #define EA_BABEL_ROUTER_ID EA_CODE(PROTOCOL_BABEL, 1)
#define EA_BABEL_SEQNO EA_CODE(PROTOCOL_BABEL, 2)
#define BABEL_MAGIC 42 #define BABEL_MAGIC 42
#define BABEL_VERSION 2 #define BABEL_VERSION 2
@ -50,10 +51,28 @@
#define BABEL_GARBAGE_INTERVAL (300 S_) #define BABEL_GARBAGE_INTERVAL (300 S_)
#define BABEL_RXCOST_WIRED 96 #define BABEL_RXCOST_WIRED 96
#define BABEL_RXCOST_WIRELESS 256 #define BABEL_RXCOST_WIRELESS 256
#define BABEL_RXCOST_RTT 96
#define BABEL_INITIAL_HOP_COUNT 255 #define BABEL_INITIAL_HOP_COUNT 255
#define BABEL_MAX_SEND_INTERVAL 5 /* Unused ? */ #define BABEL_MAX_SEND_INTERVAL 5 /* Unused ? */
#define BABEL_INITIAL_NEIGHBOR_TIMEOUT (60 S_) #define BABEL_INITIAL_NEIGHBOR_TIMEOUT (60 S_)
#define BABEL_RTT_MAX_VALUE (600 S_)
#define BABEL_RTT_MIN (10 MS_)
#define BABEL_RTT_MAX (120 MS_)
#define BABEL_RTT_WINLEN 100
/*
* Constants for calculating metric smoothing. Chosen so that:
* log(2) = BABEL_SMOOTHING_CONSTANT / BABEL_SMOOTHING_UNIT, which means that
* log(2)/x can be calculated as BABEL_SMOOTHING_UNIT + BABEL_SMOOTHING_CONSTANT / x
*/
#define BABEL_SMOOTHING_UNIT 0x10000000
#define BABEL_SMOOTHING_CONSTANT 186065279
#define BABEL_SMOOTHING_STEP (1 S_) /* smoothing calculated in this step size */
#define BABEL_SMOOTHING_MIN_DIFF 4 /* metric diff beneath this is converged */
#define BABEL_SMOOTHING_DECAY (4 S_)
#define BABEL_SMOOTHING_DECAY_MAX (180 S_)
/* Max interval that will not overflow when carried as 16-bit centiseconds */ /* Max interval that will not overflow when carried as 16-bit centiseconds */
#define BABEL_TIME_UNITS 10000 /* On-wire times are counted in centiseconds */ #define BABEL_TIME_UNITS 10000 /* On-wire times are counted in centiseconds */
#define BABEL_MIN_INTERVAL (0x0001 * BABEL_TIME_UNITS) #define BABEL_MIN_INTERVAL (0x0001 * BABEL_TIME_UNITS)
@ -93,6 +112,8 @@ enum babel_tlv_type {
enum babel_subtlv_type { enum babel_subtlv_type {
BABEL_SUBTLV_PAD1 = 0, BABEL_SUBTLV_PAD1 = 0,
BABEL_SUBTLV_PADN = 1, BABEL_SUBTLV_PADN = 1,
BABEL_SUBTLV_DIVERSITY = 2, /* we don't support this */
BABEL_SUBTLV_TIMESTAMP = 3,
/* Mandatory subtlvs */ /* Mandatory subtlvs */
BABEL_SUBTLV_SOURCE_PREFIX = 128, BABEL_SUBTLV_SOURCE_PREFIX = 128,
@ -103,6 +124,7 @@ enum babel_iface_type {
BABEL_IFACE_TYPE_UNDEF = 0, BABEL_IFACE_TYPE_UNDEF = 0,
BABEL_IFACE_TYPE_WIRED = 1, BABEL_IFACE_TYPE_WIRED = 1,
BABEL_IFACE_TYPE_WIRELESS = 2, BABEL_IFACE_TYPE_WIRELESS = 2,
BABEL_IFACE_TYPE_TUNNEL = 3,
BABEL_IFACE_TYPE_MAX BABEL_IFACE_TYPE_MAX
}; };
@ -118,6 +140,8 @@ enum babel_ae_type {
struct babel_config { struct babel_config {
struct proto_config c; struct proto_config c;
list iface_list; /* List of iface configs (struct babel_iface_config) */ list iface_list; /* List of iface configs (struct babel_iface_config) */
btime metric_decay;
uint smooth_recp; /* Reciprocal for exponential metric smoothing */
uint hold_time; /* Time to hold stale entries and unreachable routes */ uint hold_time; /* Time to hold stale entries and unreachable routes */
u8 randomize_router_id; u8 randomize_router_id;
@ -137,6 +161,12 @@ struct babel_iface_config {
uint ihu_interval; /* IHU interval, in us */ uint ihu_interval; /* IHU interval, in us */
uint update_interval; /* Update interval, in us */ uint update_interval; /* Update interval, in us */
btime rtt_min; /* rtt above which to start penalising metric */
btime rtt_max; /* max rtt metric penalty applied above this */
u16 rtt_cost; /* metric penalty to apply at rtt_max */
u16 rtt_win_len; /* smoothing windows length */
u8 rtt_send; /* whether to send timestamps on this interface */
u16 rx_buffer; /* RX buffer size, 0 for MTU */ u16 rx_buffer; /* RX buffer size, 0 for MTU */
u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */ u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */
int tx_tos; int tx_tos;
@ -224,6 +254,14 @@ struct babel_neighbor {
u16 next_hello_seqno; u16 next_hello_seqno;
uint last_hello_int; uint last_hello_int;
u32 last_tstamp;
btime last_tstamp_rcvd;
btime srtt;
btime *srtt_pool; // an array of btime
btime *srtt_pool_sorted;
u16 srtt_pool_len; // initial length
u16 srtt_poll_idx; // head
u32 auth_pc; u32 auth_pc;
u8 auth_passed; u8 auth_passed;
u8 auth_index_len; u8 auth_index_len;
@ -261,9 +299,11 @@ struct babel_route {
u16 seqno; u16 seqno;
u16 metric; u16 metric;
u16 advert_metric; u16 advert_metric;
u16 smoothed_metric;
u64 router_id; u64 router_id;
ip_addr next_hop; ip_addr next_hop;
btime refresh_time; btime refresh_time;
btime smoothed_time;
btime expires; btime expires;
}; };
@ -321,6 +361,8 @@ struct babel_msg_hello {
u16 seqno; u16 seqno;
uint interval; uint interval;
ip_addr sender; ip_addr sender;
u32 tstamp;
btime pkt_received;
}; };
struct babel_msg_ihu { struct babel_msg_ihu {
@ -330,6 +372,9 @@ struct babel_msg_ihu {
uint interval; uint interval;
ip_addr addr; ip_addr addr;
ip_addr sender; ip_addr sender;
u32 tstamp;
u32 tstamp_rcvd;
btime pkt_received;
}; };
struct babel_msg_update { struct babel_msg_update {

View file

@ -23,9 +23,10 @@ CF_DEFINES
CF_DECLS CF_DECLS
CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT, CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK, TYPE, WIRED, WIRELESS, TUNNEL, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK,
NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS, LINK, NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE) ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE,
RTT, MIN, MAX, DECAY, WINLEN, SEND, TIMESTAMPS)
CF_GRAMMAR CF_GRAMMAR
@ -36,6 +37,13 @@ babel_proto_start: proto_start BABEL
this_proto = proto_config_new(&proto_babel, $1); this_proto = proto_config_new(&proto_babel, $1);
init_list(&BABEL_CFG->iface_list); init_list(&BABEL_CFG->iface_list);
BABEL_CFG->hold_time = 1 S_; BABEL_CFG->hold_time = 1 S_;
BABEL_CFG->metric_decay = BABEL_SMOOTHING_DECAY;
};
babel_proto_finish:
{
if (BABEL_CFG->metric_decay)
BABEL_CFG->smooth_recp = BABEL_SMOOTHING_UNIT + BABEL_SMOOTHING_CONSTANT / BABEL_CFG->metric_decay;
}; };
babel_proto_item: babel_proto_item:
@ -43,6 +51,7 @@ babel_proto_item:
| proto_channel | proto_channel
| INTERFACE babel_iface | INTERFACE babel_iface
| RANDOMIZE ROUTER ID bool { BABEL_CFG->randomize_router_id = $4; } | RANDOMIZE ROUTER ID bool { BABEL_CFG->randomize_router_id = $4; }
| METRIC DECAY expr_us { BABEL_CFG->metric_decay = $3; if ($3 && (($3 < BABEL_SMOOTHING_STEP) || ($3 > BABEL_SMOOTHING_DECAY_MAX))) cf_error("Metric decay must be 0, or between 1-180s"); }
; ;
babel_proto_opts: babel_proto_opts:
@ -51,7 +60,7 @@ babel_proto_opts:
; ;
babel_proto: babel_proto:
babel_proto_start proto_name '{' babel_proto_opts '}'; babel_proto_start proto_name '{' babel_proto_opts '}' babel_proto_finish;
babel_iface_start: babel_iface_start:
@ -66,6 +75,10 @@ babel_iface_start:
BABEL_IFACE->limit = BABEL_HELLO_LIMIT; BABEL_IFACE->limit = BABEL_HELLO_LIMIT;
BABEL_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL; BABEL_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL;
BABEL_IFACE->tx_priority = sk_priority_control; BABEL_IFACE->tx_priority = sk_priority_control;
BABEL_IFACE->rtt_min = BABEL_RTT_MIN;
BABEL_IFACE->rtt_max = BABEL_RTT_MAX;
BABEL_IFACE->rtt_win_len = BABEL_RTT_WINLEN;
BABEL_IFACE->rtt_send = 1;
BABEL_IFACE->check_link = 1; BABEL_IFACE->check_link = 1;
}; };
@ -85,8 +98,16 @@ babel_iface_finish:
BABEL_IFACE->hello_interval = BABEL_HELLO_INTERVAL_WIRED; BABEL_IFACE->hello_interval = BABEL_HELLO_INTERVAL_WIRED;
if (!BABEL_IFACE->rxcost) if (!BABEL_IFACE->rxcost)
BABEL_IFACE->rxcost = BABEL_RXCOST_WIRED; BABEL_IFACE->rxcost = BABEL_RXCOST_WIRED;
if (BABEL_IFACE->type == BABEL_IFACE_TYPE_TUNNEL && !BABEL_IFACE->rtt_cost)
BABEL_IFACE->rtt_cost = BABEL_RXCOST_RTT;
} }
if (BABEL_IFACE->rtt_cost && !BABEL_IFACE->rtt_send)
cf_error("Can't set RTT cost when sending timestamps is disabled");
if (BABEL_IFACE->rtt_min >= BABEL_IFACE->rtt_max)
cf_error("Min RTT must be smaller than max RTT");
/* Make sure we do not overflow the 16-bit centisec fields */ /* Make sure we do not overflow the 16-bit centisec fields */
if (!BABEL_IFACE->update_interval) if (!BABEL_IFACE->update_interval)
BABEL_IFACE->update_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_UPDATE_INTERVAL_FACTOR, BABEL_MAX_INTERVAL); BABEL_IFACE->update_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_UPDATE_INTERVAL_FACTOR, BABEL_MAX_INTERVAL);
@ -134,6 +155,7 @@ babel_iface_item:
| LIMIT expr { BABEL_IFACE->limit = $2; if (($2<1) || ($2>16)) cf_error("Limit must be in range 1-16"); } | LIMIT expr { BABEL_IFACE->limit = $2; if (($2<1) || ($2>16)) cf_error("Limit must be in range 1-16"); }
| TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; } | TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; }
| TYPE WIRELESS { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRELESS; } | TYPE WIRELESS { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRELESS; }
| TYPE TUNNEL { BABEL_IFACE->type = BABEL_IFACE_TYPE_TUNNEL; }
| HELLO INTERVAL expr_us { BABEL_IFACE->hello_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Hello interval must be in range 10 ms - 655 s"); } | HELLO INTERVAL expr_us { BABEL_IFACE->hello_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Hello interval must be in range 10 ms - 655 s"); }
| UPDATE INTERVAL expr_us { BABEL_IFACE->update_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Update interval must be in range 10 ms - 655 s"); } | UPDATE INTERVAL expr_us { BABEL_IFACE->update_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Update interval must be in range 10 ms - 655 s"); }
| RX BUFFER expr { BABEL_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX buffer must be in range 256-65535"); } | RX BUFFER expr { BABEL_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX buffer must be in range 256-65535"); }
@ -146,6 +168,11 @@ babel_iface_item:
| AUTHENTICATION NONE { BABEL_IFACE->auth_type = BABEL_AUTH_NONE; } | AUTHENTICATION NONE { BABEL_IFACE->auth_type = BABEL_AUTH_NONE; }
| AUTHENTICATION MAC { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 0; } | AUTHENTICATION MAC { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 0; }
| AUTHENTICATION MAC PERMISSIVE { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 1; } | AUTHENTICATION MAC PERMISSIVE { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 1; }
| RTT MIN expr_us { BABEL_IFACE->rtt_min = $3; }
| RTT MAX expr_us { BABEL_IFACE->rtt_max = $3; }
| RTT COST expr { BABEL_IFACE->rtt_cost = $3; if ($3 >= BABEL_INFINITY) cf_error("RTT cost must be < 65535"); }
| RTT WINLEN expr { BABEL_IFACE->rtt_win_len = $3; if (($3 < 1) || ($3 > 65535)) cf_error("RTT winlen must be between 1-65535"); }
| SEND TIMESTAMPS bool { BABEL_IFACE->rtt_send = $3; }
| password_list | password_list
; ;

View file

@ -58,6 +58,13 @@ struct babel_tlv_ihu {
u8 addr[0]; u8 addr[0];
} PACKED; } PACKED;
struct babel_subtlv_timestamp {
u8 type;
u8 length;
u32 tstamp;
u32 tstamp_rcvd; /* only used in IHU */
} PACKED;
struct babel_tlv_router_id { struct babel_tlv_router_id {
u8 type; u8 type;
u8 length; u8 length;
@ -161,6 +168,7 @@ struct babel_parse_state {
const struct babel_tlv_data* (*get_subtlv_data)(u8 type); const struct babel_tlv_data* (*get_subtlv_data)(u8 type);
struct babel_proto *proto; struct babel_proto *proto;
struct babel_iface *ifa; struct babel_iface *ifa;
btime received_time;
ip_addr saddr; ip_addr saddr;
ip_addr next_hop_ip4; ip_addr next_hop_ip4;
ip_addr next_hop_ip6; ip_addr next_hop_ip6;
@ -170,6 +178,7 @@ struct babel_parse_state {
u8 router_id_seen; /* router_id field is valid */ u8 router_id_seen; /* router_id field is valid */
u8 def_ip6_prefix_seen; /* def_ip6_prefix is valid */ u8 def_ip6_prefix_seen; /* def_ip6_prefix is valid */
u8 def_ip4_prefix_seen; /* def_ip4_prefix is valid */ u8 def_ip4_prefix_seen; /* def_ip4_prefix is valid */
u8 hello_tstamp_seen; /* pkt contains a hello timestamp */
u8 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */ u8 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */
u8 sadr_enabled; u8 sadr_enabled;
u8 is_unicast; u8 is_unicast;
@ -334,6 +343,7 @@ static int babel_read_update(struct babel_tlv *hdr, union babel_msg *msg, struct
static int babel_read_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); static int babel_read_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
static int babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); static int babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
static int babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); static int babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
static int babel_read_timestamp(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
static uint babel_write_ack(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); static uint babel_write_ack(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
static uint babel_write_hello(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); static uint babel_write_hello(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
@ -342,6 +352,7 @@ static uint babel_write_update(struct babel_tlv *hdr, union babel_msg *msg, stru
static uint babel_write_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); static uint babel_write_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
static uint babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); static uint babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
static int babel_write_source_prefix(struct babel_tlv *hdr, net_addr *net, uint max_len); static int babel_write_source_prefix(struct babel_tlv *hdr, net_addr *net, uint max_len);
static int babel_write_timestamp(struct babel_tlv *hdr, u32 tstamp, u32 tstamp_rcvd, uint max_len);
static const struct babel_tlv_data tlv_data[BABEL_TLV_MAX] = { static const struct babel_tlv_data tlv_data[BABEL_TLV_MAX] = {
[BABEL_TLV_ACK_REQ] = { [BABEL_TLV_ACK_REQ] = {
@ -417,6 +428,13 @@ static const struct babel_tlv_data *get_packet_tlv_data(u8 type)
return type < sizeof(tlv_data) / sizeof(*tlv_data) ? &tlv_data[type] : NULL; return type < sizeof(tlv_data) / sizeof(*tlv_data) ? &tlv_data[type] : NULL;
} }
static const struct babel_tlv_data timestamp_tlv_data = {
sizeof(struct babel_subtlv_timestamp),
babel_read_timestamp,
NULL,
NULL
};
static const struct babel_tlv_data source_prefix_tlv_data = { static const struct babel_tlv_data source_prefix_tlv_data = {
sizeof(struct babel_subtlv_source_prefix), sizeof(struct babel_subtlv_source_prefix),
babel_read_source_prefix, babel_read_source_prefix,
@ -428,6 +446,8 @@ static const struct babel_tlv_data *get_packet_subtlv_data(u8 type)
{ {
switch (type) switch (type)
{ {
case BABEL_SUBTLV_TIMESTAMP:
return &timestamp_tlv_data;
case BABEL_SUBTLV_SOURCE_PREFIX: case BABEL_SUBTLV_SOURCE_PREFIX:
return &source_prefix_tlv_data; return &source_prefix_tlv_data;
@ -489,16 +509,34 @@ babel_read_hello(struct babel_tlv *hdr, union babel_msg *m,
static uint static uint
babel_write_hello(struct babel_tlv *hdr, union babel_msg *m, babel_write_hello(struct babel_tlv *hdr, union babel_msg *m,
struct babel_write_state *state UNUSED, uint max_len UNUSED) struct babel_write_state *state UNUSED, uint max_len)
{ {
struct babel_tlv_hello *tlv = (void *) hdr; struct babel_tlv_hello *tlv = (void *) hdr;
struct babel_msg_hello *msg = &m->hello; struct babel_msg_hello *msg = &m->hello;
uint len = sizeof(struct babel_tlv_hello);
TLV_HDR0(tlv, BABEL_TLV_HELLO); TLV_HDR0(tlv, BABEL_TLV_HELLO);
put_u16(&tlv->seqno, msg->seqno); put_u16(&tlv->seqno, msg->seqno);
put_time16(&tlv->interval, msg->interval); put_time16(&tlv->interval, msg->interval);
return sizeof(struct babel_tlv_hello); if (msg->tstamp)
{
/*
* There can be a substantial delay between when the babel_msg was created
* and when it is serialised. We don't want this included in the RTT
* measurement, so replace the timestamp with the current time to get as
* close as possible to on-wire time for the packet.
*/
u32 tstamp = current_time_now() TO_US;
int l = babel_write_timestamp(hdr, tstamp, 0, max_len);
if (l < 0)
return 0;
len += l;
}
return len;
} }
static int static int
@ -556,6 +594,7 @@ babel_write_ihu(struct babel_tlv *hdr, union babel_msg *m,
{ {
struct babel_tlv_ihu *tlv = (void *) hdr; struct babel_tlv_ihu *tlv = (void *) hdr;
struct babel_msg_ihu *msg = &m->ihu; struct babel_msg_ihu *msg = &m->ihu;
uint len = sizeof(*tlv);
if (ipa_is_link_local(msg->addr) && max_len < sizeof(struct babel_tlv_ihu) + 8) if (ipa_is_link_local(msg->addr) && max_len < sizeof(struct babel_tlv_ihu) + 8)
return 0; return 0;
@ -567,12 +606,24 @@ babel_write_ihu(struct babel_tlv *hdr, union babel_msg *m,
if (!ipa_is_link_local(msg->addr)) if (!ipa_is_link_local(msg->addr))
{ {
tlv->ae = BABEL_AE_WILDCARD; tlv->ae = BABEL_AE_WILDCARD;
return sizeof(struct babel_tlv_ihu); goto out;
} }
put_ip6_ll(&tlv->addr, msg->addr); put_ip6_ll(&tlv->addr, msg->addr);
tlv->ae = BABEL_AE_IP6_LL; tlv->ae = BABEL_AE_IP6_LL;
hdr->length += 8; hdr->length += 8;
return sizeof(struct babel_tlv_ihu) + 8; len += 8;
out:
if (msg->tstamp)
{
int l = babel_write_timestamp(hdr, msg->tstamp, msg->tstamp_rcvd, max_len);
if (l < 0)
return 0;
len += l;
}
return len;
} }
static int static int
@ -1200,6 +1251,66 @@ babel_write_source_prefix(struct babel_tlv *hdr, net_addr *n, uint max_len)
return len; return len;
} }
static int
babel_read_timestamp(struct babel_tlv *hdr, union babel_msg *msg,
struct babel_parse_state *state)
{
struct babel_subtlv_timestamp *tlv = (void *) hdr;
switch (msg->type)
{
case BABEL_TLV_HELLO:
if (tlv->length < 4)
return PARSE_ERROR;
msg->hello.tstamp = get_u32(&tlv->tstamp);
msg->hello.pkt_received = state->received_time;
state->hello_tstamp_seen = 1;
break;
case BABEL_TLV_IHU:
if (tlv->length < 8)
return PARSE_ERROR;
/* RTT calculation relies on a Hello always being present with an IHU */
if (!state->hello_tstamp_seen)
break;
msg->ihu.tstamp = get_u32(&tlv->tstamp);
msg->ihu.tstamp_rcvd = get_u32(&tlv->tstamp_rcvd);
msg->ihu.pkt_received = state->received_time;
break;
default:
return PARSE_ERROR;
}
return PARSE_SUCCESS;
}
static int
babel_write_timestamp(struct babel_tlv *hdr, u32 tstamp, u32 tstamp_rcvd, uint max_len)
{
struct babel_subtlv_timestamp *tlv = (void *) NEXT_TLV(hdr);
uint len = sizeof(*tlv);
if (hdr->type == BABEL_TLV_HELLO)
len -= 4;
if (len > max_len)
return -1;
TLV_HDR(tlv, BABEL_SUBTLV_TIMESTAMP, len);
hdr->length += len;
put_u32(&tlv->tstamp, tstamp);
if (hdr->type == BABEL_TLV_IHU)
put_u32(&tlv->tstamp_rcvd, tstamp_rcvd);
return len;
}
static inline int static inline int
babel_read_subtlvs(struct babel_tlv *hdr, babel_read_subtlvs(struct babel_tlv *hdr,
union babel_msg *msg, union babel_msg *msg,
@ -1318,7 +1429,6 @@ babel_send_to(struct babel_iface *ifa, ip_addr dest)
static uint static uint
babel_write_queue(struct babel_iface *ifa, list *queue) babel_write_queue(struct babel_iface *ifa, list *queue)
{ {
struct babel_proto *p = ifa->proto;
struct babel_write_state state = { .next_hop_ip6 = ifa->addr }; struct babel_write_state state = { .next_hop_ip6 = ifa->addr };
if (EMPTY_LIST(*queue)) if (EMPTY_LIST(*queue))
@ -1346,7 +1456,7 @@ babel_write_queue(struct babel_iface *ifa, list *queue)
pos += len; pos += len;
rem_node(NODE msg); rem_node(NODE msg);
sl_free(p->msg_slab, msg); sl_free(msg);
} }
pos += babel_auth_add_tlvs(ifa, (struct babel_tlv *) pos, end - pos); pos += babel_auth_add_tlvs(ifa, (struct babel_tlv *) pos, end - pos);
@ -1470,6 +1580,13 @@ babel_process_packet(struct babel_iface *ifa,
.saddr = saddr, .saddr = saddr,
.next_hop_ip6 = saddr, .next_hop_ip6 = saddr,
.sadr_enabled = babel_sadr_enabled(p), .sadr_enabled = babel_sadr_enabled(p),
/*
* The core updates current_time() after returning from poll(), so this is
* actually the time the packet was received, even though there may have
* been a bit of delay before we got to process it
*/
.received_time = current_time(),
}; };
if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION)) if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION))
@ -1507,13 +1624,13 @@ babel_process_packet(struct babel_iface *ifa,
else if (res == PARSE_IGNORE) else if (res == PARSE_IGNORE)
{ {
DBG("Babel: Ignoring TLV of type %d\n", tlv->type); DBG("Babel: Ignoring TLV of type %d\n", tlv->type);
sl_free(p->msg_slab, msg); sl_free(msg);
} }
else /* PARSE_ERROR */ else /* PARSE_ERROR */
{ {
LOG_PKT("Bad TLV from %I via %s type %d pos %d - parse error", LOG_PKT("Bad TLV from %I via %s type %d pos %d - parse error",
saddr, ifa->iface->name, tlv->type, (int) ((byte *)tlv - (byte *)pkt)); saddr, ifa->iface->name, tlv->type, (int) ((byte *)tlv - (byte *)pkt));
sl_free(p->msg_slab, msg); sl_free(msg);
break; break;
} }
} }
@ -1525,7 +1642,7 @@ babel_process_packet(struct babel_iface *ifa,
if (tlv_data[msg->msg.type].handle_tlv) if (tlv_data[msg->msg.type].handle_tlv)
tlv_data[msg->msg.type].handle_tlv(&msg->msg, ifa); tlv_data[msg->msg.type].handle_tlv(&msg->msg, ifa);
rem_node(NODE msg); rem_node(NODE msg);
sl_free(p->msg_slab, msg); sl_free(msg);
} }
} }

View file

@ -113,8 +113,8 @@
#define HASH_IP_EQ(a1,n1,a2,n2) ipa_equal(a1, a2) && n1 == n2 #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) #define HASH_IP_FN(a,n) ipa_hash(a) ^ u32_hash(n)
static list bfd_proto_list; static list STATIC_LIST_INIT(bfd_proto_list);
static list bfd_wait_list; static list STATIC_LIST_INIT(bfd_wait_list);
const char *bfd_state_names[] = { "AdminDown", "Down", "Init", "Up" }; const char *bfd_state_names[] = { "AdminDown", "Down", "Init", "Up" };
@ -508,7 +508,7 @@ bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
HASH_REMOVE(p->session_hash_id, HASH_ID, s); HASH_REMOVE(p->session_hash_id, HASH_ID, s);
HASH_REMOVE(p->session_hash_ip, HASH_IP, s); HASH_REMOVE(p->session_hash_ip, HASH_IP, s);
sl_free(p->session_slab, s); sl_free(s);
TRACE(D_EVENTS, "Session to %I removed", ip); TRACE(D_EVENTS, "Session to %I removed", ip);
@ -1007,13 +1007,6 @@ bfd_notify_init(struct bfd_proto *p)
* BFD protocol glue * BFD protocol glue
*/ */
void
bfd_init_all(void)
{
init_list(&bfd_proto_list);
init_list(&bfd_wait_list);
}
static struct proto * static struct proto *
bfd_init(struct proto_config *c) bfd_init(struct proto_config *c)
{ {
@ -1199,3 +1192,9 @@ struct protocol proto_bfd = {
.reconfigure = bfd_reconfigure, .reconfigure = bfd_reconfigure,
.copy_config = bfd_copy_config, .copy_config = bfd_copy_config,
}; };
void
bfd_build(void)
{
proto_build(&proto_bfd);
}

View file

@ -482,6 +482,8 @@ birdloop_main(void *arg)
birdloop_set_current(loop); birdloop_set_current(loop);
tmp_init(loop->pool);
pthread_mutex_lock(&loop->mutex); pthread_mutex_lock(&loop->mutex);
while (1) while (1)
{ {

View file

@ -106,7 +106,7 @@ bgp_set_attr(ea_list **attrs, struct linpool *pool, uint code, uint flags, uintp
({ REPORT(msg, ## args); s->err_withdraw = 1; return; }) ({ REPORT(msg, ## args); s->err_withdraw = 1; return; })
#define UNSET(a) \ #define UNSET(a) \
({ a->type = EAF_TYPE_UNDEF; return; }) ({ a->undef = 1; return; })
#define REJECT(msg, args...) \ #define REJECT(msg, args...) \
({ log(L_ERR "%s: " msg, s->proto->p.name, ## args); s->err_reject = 1; return; }) ({ log(L_ERR "%s: " msg, s->proto->p.name, ## args); s->err_reject = 1; return; })
@ -374,6 +374,13 @@ bgp_init_aigp_metric(rte *e, u64 *metric, const struct adata **ad)
return *metric < IGP_METRIC_UNKNOWN; return *metric < IGP_METRIC_UNKNOWN;
} }
u32
bgp_rte_igp_metric(struct rte *rt)
{
u64 metric = bgp_total_aigp_metric(rt);
return (u32) MIN(metric, (u64) IGP_METRIC_UNKNOWN);
}
/* /*
* Attribute hooks * Attribute hooks
@ -896,6 +903,18 @@ bgp_decode_large_community(struct bgp_parse_state *s, uint code UNUSED, uint fla
bgp_set_attr_ptr(to, s->pool, BA_LARGE_COMMUNITY, flags, ad); bgp_set_attr_ptr(to, s->pool, BA_LARGE_COMMUNITY, flags, ad);
} }
static void
bgp_decode_otc(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data UNUSED, uint len, ea_list **to)
{
if (len != 4)
WITHDRAW(BAD_LENGTH, "OTC", len);
u32 val = get_u32(data);
bgp_set_attr_u32(to, s->pool, BA_ONLY_TO_CUSTOMER, flags, val);
}
static void static void
bgp_export_mpls_label_stack(struct bgp_export_state *s, eattr *a) bgp_export_mpls_label_stack(struct bgp_export_state *s, eattr *a)
{ {
@ -1109,6 +1128,13 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
.encode = bgp_encode_u32s, .encode = bgp_encode_u32s,
.decode = bgp_decode_large_community, .decode = bgp_decode_large_community,
}, },
[BA_ONLY_TO_CUSTOMER] = {
.name = "otc",
.type = EAF_TYPE_INT,
.flags = BAF_OPTIONAL | BAF_TRANSITIVE,
.encode = bgp_encode_u32,
.decode = bgp_decode_otc,
},
[BA_MPLS_LABEL_STACK] = { [BA_MPLS_LABEL_STACK] = {
.name = "mpls_label_stack", .name = "mpls_label_stack",
.type = EAF_TYPE_INT_SET, .type = EAF_TYPE_INT_SET,
@ -1146,7 +1172,7 @@ bgp_export_attr(struct bgp_export_state *s, eattr *a, ea_list *to)
a->flags = (a->flags & BAF_PARTIAL) | desc->flags; a->flags = (a->flags & BAF_PARTIAL) | desc->flags;
/* Set partial bit if new opt-trans attribute is attached to non-local route */ /* Set partial bit if new opt-trans attribute is attached to non-local route */
if ((s->src != NULL) && (a->type & EAF_ORIGINATED) && if ((s->src != NULL) && (a->originated) &&
(a->flags & BAF_OPTIONAL) && (a->flags & BAF_TRANSITIVE)) (a->flags & BAF_OPTIONAL) && (a->flags & BAF_TRANSITIVE))
a->flags |= BAF_PARTIAL; a->flags |= BAF_PARTIAL;
@ -1154,7 +1180,7 @@ bgp_export_attr(struct bgp_export_state *s, eattr *a, ea_list *to)
CALL(desc->export, s, a); CALL(desc->export, s, a);
/* Attribute might become undefined in hook */ /* Attribute might become undefined in hook */
if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF) if (a->undef)
return; return;
} }
else else
@ -1445,6 +1471,29 @@ bgp_finish_attrs(struct bgp_parse_state *s, rta *a)
REPORT("Discarding AIGP attribute received on non-AIGP session"); REPORT("Discarding AIGP attribute received on non-AIGP session");
bgp_unset_attr(&a->eattrs, s->pool, BA_AIGP); bgp_unset_attr(&a->eattrs, s->pool, BA_AIGP);
} }
/* Handle OTC ingress procedure, RFC 9234 */
if (bgp_channel_is_role_applicable(s->channel))
{
struct bgp_proto *p = s->proto;
eattr *e = bgp_find_attr(a->eattrs, BA_ONLY_TO_CUSTOMER);
/* Reject routes from downstream if they are leaked */
if (e && (p->cf->local_role == BGP_ROLE_PROVIDER ||
p->cf->local_role == BGP_ROLE_RS_SERVER))
WITHDRAW("Route leak detected - OTC attribute from downstream");
/* Reject routes from peers if they are leaked */
if (e && (p->cf->local_role == BGP_ROLE_PEER) && (e->u.data != p->cf->remote_as))
WITHDRAW("Route leak detected - OTC attribute with mismatched ASN (%u)",
(uint) e->u.data);
/* Mark routes from upstream if it did not happened before */
if (!e && (p->cf->local_role == BGP_ROLE_CUSTOMER ||
p->cf->local_role == BGP_ROLE_PEER ||
p->cf->local_role == BGP_ROLE_RS_CLIENT))
bgp_set_attr_u32(&a->eattrs, s->pool, BA_ONLY_TO_CUSTOMER, 0, p->cf->remote_as);
}
} }
@ -1658,7 +1707,7 @@ bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px)
HASH_REMOVE2(c->prefix_hash, PXH, c->pool, px); HASH_REMOVE2(c->prefix_hash, PXH, c->pool, px);
if (c->prefix_slab) if (c->prefix_slab)
sl_free(c->prefix_slab, px); sl_free(px);
else else
mb_free(px); mb_free(px);
} }
@ -1669,12 +1718,16 @@ bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px)
*/ */
int int
bgp_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED) bgp_preexport(struct channel *C, rte *e)
{ {
rte *e = *new; struct proto *SRC = e->src->proto;
struct proto *SRC = e->attrs->src->proto; struct bgp_proto *p = (struct bgp_proto *) C->proto;
struct bgp_proto *p = (struct bgp_proto *) P;
struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (struct bgp_proto *) SRC : NULL; struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (struct bgp_proto *) SRC : NULL;
struct bgp_channel *c = (struct bgp_channel *) C;
/* Ignore non-BGP channels */
if (C->channel != &channel_bgp)
return -1;
/* Reject our routes */ /* Reject our routes */
if (src == p) if (src == p)
@ -1702,11 +1755,11 @@ bgp_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
} }
/* Handle well-known communities, RFC 1997 */ /* Handle well-known communities, RFC 1997 */
struct eattr *c; struct eattr *a;
if (p->cf->interpret_communities && if (p->cf->interpret_communities &&
(c = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY)))) (a = bgp_find_attr(e->attrs->eattrs, BA_COMMUNITY)))
{ {
const struct adata *d = c->u.ptr; const struct adata *d = a->u.ptr;
/* Do not export anywhere */ /* Do not export anywhere */
if (int_set_contains(d, BGP_COMM_NO_ADVERTISE)) if (int_set_contains(d, BGP_COMM_NO_ADVERTISE))
@ -1725,13 +1778,23 @@ bgp_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
return -1; return -1;
} }
/* Do not export routes marked with OTC to upstream, RFC 9234 */
if (bgp_channel_is_role_applicable(c))
{
a = bgp_find_attr(e->attrs->eattrs, BA_ONLY_TO_CUSTOMER);
if (a && (p->cf->local_role==BGP_ROLE_CUSTOMER ||
p->cf->local_role==BGP_ROLE_PEER ||
p->cf->local_role==BGP_ROLE_RS_CLIENT))
return -1;
}
return 0; return 0;
} }
static ea_list * static ea_list *
bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *attrs0, struct linpool *pool) bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *attrs0, struct linpool *pool)
{ {
struct proto *SRC = e->attrs->src->proto; struct proto *SRC = e->src->proto;
struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (void *) SRC : NULL; struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (void *) SRC : NULL;
struct bgp_export_state s = { .proto = p, .channel = c, .pool = pool, .src = src, .route = e, .mpls = c->desc->mpls }; struct bgp_export_state s = { .proto = p, .channel = c, .pool = pool, .src = src, .route = e, .mpls = c->desc->mpls };
ea_list *attrs = attrs0; ea_list *attrs = attrs0;
@ -1771,7 +1834,7 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
/* MULTI_EXIT_DESC attribute - accept only if set in export filter */ /* MULTI_EXIT_DESC attribute - accept only if set in export filter */
a = bgp_find_attr(attrs0, BA_MULTI_EXIT_DISC); a = bgp_find_attr(attrs0, BA_MULTI_EXIT_DISC);
if (a && !(a->type & EAF_FRESH)) if (a && !(a->fresh))
bgp_unset_attr(&attrs, pool, BA_MULTI_EXIT_DISC); bgp_unset_attr(&attrs, pool, BA_MULTI_EXIT_DISC);
} }
@ -1834,6 +1897,16 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
} }
} }
/* Mark routes for downstream with OTC, RFC 9234 */
if (bgp_channel_is_role_applicable(c))
{
a = bgp_find_attr(attrs, BA_ONLY_TO_CUSTOMER);
if (!a && (p->cf->local_role == BGP_ROLE_PROVIDER ||
p->cf->local_role == BGP_ROLE_PEER ||
p->cf->local_role == BGP_ROLE_RS_SERVER))
bgp_set_attr_u32(&attrs, pool, BA_ONLY_TO_CUSTOMER, 0, p->public_as);
}
/* /*
* Presence of mandatory attributes ORIGIN and AS_PATH is ensured by above * Presence of mandatory attributes ORIGIN and AS_PATH is ensured by above
* conditions. Presence and validity of quasi-mandatory NEXT_HOP attribute * conditions. Presence and validity of quasi-mandatory NEXT_HOP attribute
@ -1853,9 +1926,13 @@ bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old)
struct bgp_prefix *px; struct bgp_prefix *px;
u32 path; u32 path;
/* Ignore non-BGP channels */
if (C->channel != &channel_bgp)
return;
if (new) if (new)
{ {
struct ea_list *attrs = bgp_update_attrs(p, c, new, new->attrs->eattrs, bgp_linpool2); struct ea_list *attrs = bgp_update_attrs(p, c, new, new->attrs->eattrs, tmp_linpool);
/* Error during attribute processing */ /* Error during attribute processing */
if (!attrs) if (!attrs)
@ -1863,14 +1940,12 @@ bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old)
/* If attributes are invalid, we fail back to withdraw */ /* If attributes are invalid, we fail back to withdraw */
buck = attrs ? bgp_get_bucket(c, attrs) : bgp_get_withdraw_bucket(c); buck = attrs ? bgp_get_bucket(c, attrs) : bgp_get_withdraw_bucket(c);
path = new->attrs->src->global_id; path = new->src->global_id;
lp_flush(bgp_linpool2);
} }
else else
{ {
buck = bgp_get_withdraw_bucket(c); buck = bgp_get_withdraw_bucket(c);
path = old->attrs->src->global_id; path = old->src->global_id;
} }
px = bgp_get_prefix(c, n->n.addr, c->add_path_tx ? path : 0); px = bgp_get_prefix(c, n->n.addr, c->add_path_tx ? path : 0);
@ -1890,34 +1965,44 @@ bgp_get_neighbor(rte *r)
return as; return as;
/* If AS_PATH is not defined, we treat rte as locally originated */ /* If AS_PATH is not defined, we treat rte as locally originated */
struct bgp_proto *p = (void *) r->attrs->src->proto; struct bgp_proto *p = (void *) r->src->proto;
return p->cf->confederation ?: p->local_as; return p->cf->confederation ?: p->local_as;
} }
static inline int static inline int
rte_stale(rte *r) rte_stale(rte *r)
{ {
if (r->u.bgp.stale < 0) if (r->pflags & BGP_REF_STALE)
{ return 1;
/* If staleness is unknown, compute and cache it */
eattr *a = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
r->u.bgp.stale = a && int_set_contains(a->u.ptr, BGP_COMM_LLGR_STALE);
}
return r->u.bgp.stale; if (r->pflags & BGP_REF_NOT_STALE)
return 0;
/* If staleness is unknown, compute and cache it */
eattr *a = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
if (a && int_set_contains(a->u.ptr, BGP_COMM_LLGR_STALE))
{
r->pflags |= BGP_REF_STALE;
return 1;
}
else
{
r->pflags |= BGP_REF_NOT_STALE;
return 0;
}
} }
int int
bgp_rte_better(rte *new, rte *old) bgp_rte_better(rte *new, rte *old)
{ {
struct bgp_proto *new_bgp = (struct bgp_proto *) new->attrs->src->proto; struct bgp_proto *new_bgp = (struct bgp_proto *) new->src->proto;
struct bgp_proto *old_bgp = (struct bgp_proto *) old->attrs->src->proto; struct bgp_proto *old_bgp = (struct bgp_proto *) old->src->proto;
eattr *x, *y; eattr *x, *y;
u32 n, o; u32 n, o;
/* Skip suppressed routes (see bgp_rte_recalculate()) */ /* Skip suppressed routes (see bgp_rte_recalculate()) */
n = new->u.bgp.suppressed; n = new->pflags & BGP_REF_SUPPRESSED;
o = old->u.bgp.suppressed; o = old->pflags & BGP_REF_SUPPRESSED;
if (n > o) if (n > o)
return 0; return 0;
if (n < o) if (n < o)
@ -2055,13 +2140,13 @@ bgp_rte_better(rte *new, rte *old)
int int
bgp_rte_mergable(rte *pri, rte *sec) bgp_rte_mergable(rte *pri, rte *sec)
{ {
struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->attrs->src->proto; struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->src->proto;
struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->attrs->src->proto; struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->src->proto;
eattr *x, *y; eattr *x, *y;
u32 p, s; u32 p, s;
/* Skip suppressed routes (see bgp_rte_recalculate()) */ /* Skip suppressed routes (see bgp_rte_recalculate()) */
if (pri->u.bgp.suppressed != sec->u.bgp.suppressed) if ((pri->pflags ^ sec->pflags) & BGP_REF_SUPPRESSED)
return 0; return 0;
/* RFC 4271 9.1.2.1. Route resolvability test */ /* RFC 4271 9.1.2.1. Route resolvability test */
@ -2134,13 +2219,13 @@ bgp_rte_mergable(rte *pri, rte *sec)
static inline int static inline int
same_group(rte *r, u32 lpref, u32 lasn) same_group(rte *r, u32 lpref, u32 lasn)
{ {
return (r->pref == lpref) && (bgp_get_neighbor(r) == lasn); return (r->attrs->pref == lpref) && (bgp_get_neighbor(r) == lasn);
} }
static inline int static inline int
use_deterministic_med(rte *r) use_deterministic_med(rte *r)
{ {
struct proto *P = r->attrs->src->proto; struct proto *P = r->src->proto;
return (P->proto == &proto_bgp) && ((struct bgp_proto *) P)->cf->deterministic_med; return (P->proto == &proto_bgp) && ((struct bgp_proto *) P)->cf->deterministic_med;
} }
@ -2149,9 +2234,9 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
{ {
rte *r, *s; rte *r, *s;
rte *key = new ? new : old; rte *key = new ? new : old;
u32 lpref = key->pref; u32 lpref = key->attrs->pref;
u32 lasn = bgp_get_neighbor(key); u32 lasn = bgp_get_neighbor(key);
int old_suppressed = old ? old->u.bgp.suppressed : 0; int old_suppressed = old ? !!(old->pflags & BGP_REF_SUPPRESSED) : 0;
/* /*
* Proper RFC 4271 path selection is a bit complicated, it cannot be * Proper RFC 4271 path selection is a bit complicated, it cannot be
@ -2203,11 +2288,11 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
*/ */
if (new) if (new)
new->u.bgp.suppressed = 1; new->pflags |= BGP_REF_SUPPRESSED;
if (old) if (old)
{ {
old->u.bgp.suppressed = 1; old->pflags |= BGP_REF_SUPPRESSED;
/* The fast case - replace not best with worse (or remove not best) */ /* The fast case - replace not best with worse (or remove not best) */
if (old_suppressed && !(new && bgp_rte_better(new, old))) if (old_suppressed && !(new && bgp_rte_better(new, old)))
@ -2219,7 +2304,7 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
for (s=net->routes; rte_is_valid(s); s=s->next) for (s=net->routes; rte_is_valid(s); s=s->next)
if (use_deterministic_med(s) && same_group(s, lpref, lasn)) if (use_deterministic_med(s) && same_group(s, lpref, lasn))
{ {
s->u.bgp.suppressed = 1; s->pflags |= BGP_REF_SUPPRESSED;
if (!r || bgp_rte_better(s, r)) if (!r || bgp_rte_better(s, r))
r = s; r = s;
} }
@ -2230,16 +2315,16 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
/* Found if new is mergable with best-in-group */ /* Found if new is mergable with best-in-group */
if (new && (new != r) && bgp_rte_mergable(r, new)) if (new && (new != r) && bgp_rte_mergable(r, new))
new->u.bgp.suppressed = 0; new->pflags &= ~BGP_REF_SUPPRESSED;
/* Found all existing routes mergable with best-in-group */ /* Found all existing routes mergable with best-in-group */
for (s=net->routes; rte_is_valid(s); s=s->next) for (s=net->routes; rte_is_valid(s); s=s->next)
if (use_deterministic_med(s) && same_group(s, lpref, lasn)) if (use_deterministic_med(s) && same_group(s, lpref, lasn))
if ((s != r) && bgp_rte_mergable(r, s)) if ((s != r) && bgp_rte_mergable(r, s))
s->u.bgp.suppressed = 0; s->pflags &= ~BGP_REF_SUPPRESSED;
/* Found best-in-group */ /* Found best-in-group */
r->u.bgp.suppressed = 0; r->pflags &= ~BGP_REF_SUPPRESSED;
/* /*
* There are generally two reasons why we have to force * There are generally two reasons why we have to force
@ -2287,7 +2372,7 @@ bgp_rte_modify_stale(struct rte *r, struct linpool *pool)
r = rte_cow_rta(r, pool); r = rte_cow_rta(r, pool);
bgp_set_attr_ptr(&(r->attrs->eattrs), pool, BA_COMMUNITY, flags, bgp_set_attr_ptr(&(r->attrs->eattrs), pool, BA_COMMUNITY, flags,
int_set_add(pool, ad, BGP_COMM_LLGR_STALE)); int_set_add(pool, ad, BGP_COMM_LLGR_STALE));
r->u.bgp.stale = 1; r->pflags |= BGP_REF_STALE;
return r; return r;
} }
@ -2372,9 +2457,9 @@ bgp_get_route_info(rte *e, byte *buf)
eattr *o = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGIN)); eattr *o = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGIN));
u32 origas; u32 origas;
buf += bsprintf(buf, " (%d", e->pref); buf += bsprintf(buf, " (%d", e->attrs->pref);
if (e->u.bgp.suppressed) if (e->pflags & BGP_REF_SUPPRESSED)
buf += bsprintf(buf, "-"); buf += bsprintf(buf, "-");
if (rte_stale(e)) if (rte_stale(e))

View file

@ -102,6 +102,7 @@
* RFC 8212 - Default EBGP Route Propagation Behavior without Policies * RFC 8212 - Default EBGP Route Propagation Behavior without Policies
* RFC 8654 - Extended Message Support for BGP * RFC 8654 - Extended Message Support for BGP
* RFC 9117 - Revised Validation Procedure for BGP Flow Specifications * RFC 9117 - Revised Validation Procedure for BGP Flow Specifications
* RFC 9234 - Route Leak Prevention and Detection Using Roles
* draft-ietf-idr-ext-opt-param-07 * draft-ietf-idr-ext-opt-param-07
* draft-uttaro-idr-bgp-persistence-04 * draft-uttaro-idr-bgp-persistence-04
* draft-walton-bgp-hostname-capability-02 * draft-walton-bgp-hostname-capability-02
@ -126,9 +127,7 @@
#include "bgp.h" #include "bgp.h"
struct linpool *bgp_linpool; /* Global temporary pool */ static list STATIC_LIST_INIT(bgp_sockets); /* Global list of listening sockets */
struct linpool *bgp_linpool2; /* Global temporary pool for bgp_rt_notify() */
static list bgp_sockets; /* Global list of listening sockets */
static void bgp_connect(struct bgp_proto *p); static void bgp_connect(struct bgp_proto *p);
@ -161,10 +160,6 @@ bgp_open(struct bgp_proto *p)
uint flags = p->cf->free_bind ? SKF_FREEBIND : 0; uint flags = p->cf->free_bind ? SKF_FREEBIND : 0;
uint flag_mask = SKF_FREEBIND; uint flag_mask = SKF_FREEBIND;
/* FIXME: Add some global init? */
if (!bgp_linpool)
init_list(&bgp_sockets);
/* We assume that cf->iface is defined iff cf->local_ip is link-local */ /* We assume that cf->iface is defined iff cf->local_ip is link-local */
WALK_LIST(bs, bgp_sockets) WALK_LIST(bs, bgp_sockets)
@ -204,12 +199,6 @@ bgp_open(struct bgp_proto *p)
add_tail(&bgp_sockets, &bs->n); add_tail(&bgp_sockets, &bs->n);
if (!bgp_linpool)
{
bgp_linpool = lp_new_default(proto_pool);
bgp_linpool2 = lp_new_default(proto_pool);
}
return 0; return 0;
err: err:
@ -238,15 +227,6 @@ bgp_close(struct bgp_proto *p)
rfree(bs->sk); rfree(bs->sk);
rem_node(&bs->n); rem_node(&bs->n);
mb_free(bs); mb_free(bs);
if (!EMPTY_LIST(bgp_sockets))
return;
rfree(bgp_linpool);
bgp_linpool = NULL;
rfree(bgp_linpool2);
bgp_linpool2 = NULL;
} }
static inline int static inline int
@ -280,7 +260,7 @@ static inline struct bgp_channel *
bgp_find_channel(struct bgp_proto *p, u32 afi) bgp_find_channel(struct bgp_proto *p, u32 afi)
{ {
struct bgp_channel *c; struct bgp_channel *c;
WALK_LIST(c, p->p.channels) BGP_WALK_CHANNELS(p, c)
if (c->afi == afi) if (c->afi == afi)
return c; return c;
@ -606,7 +586,7 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
/* Summary state of ADD_PATH RX for active channels */ /* Summary state of ADD_PATH RX for active channels */
uint summary_add_path_rx = 0; uint summary_add_path_rx = 0;
WALK_LIST(c, p->p.channels) BGP_WALK_CHANNELS(p, c)
{ {
const struct bgp_af_caps *loc = bgp_find_af_caps(local, c->afi); const struct bgp_af_caps *loc = bgp_find_af_caps(local, c->afi);
const struct bgp_af_caps *rem = bgp_find_af_caps(peer, c->afi); const struct bgp_af_caps *rem = bgp_find_af_caps(peer, c->afi);
@ -688,7 +668,7 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
p->channel_count = num; p->channel_count = num;
p->summary_add_path_rx = summary_add_path_rx; p->summary_add_path_rx = summary_add_path_rx;
WALK_LIST(c, p->p.channels) BGP_WALK_CHANNELS(p, c)
{ {
if (c->c.disabled) if (c->c.disabled)
continue; continue;
@ -767,7 +747,7 @@ bgp_handle_graceful_restart(struct bgp_proto *p)
p->gr_active_num = 0; p->gr_active_num = 0;
struct bgp_channel *c; struct bgp_channel *c;
WALK_LIST(c, p->p.channels) BGP_WALK_CHANNELS(p, c)
{ {
/* FIXME: perhaps check for channel state instead of disabled flag? */ /* FIXME: perhaps check for channel state instead of disabled flag? */
if (c->c.disabled) if (c->c.disabled)
@ -862,7 +842,7 @@ bgp_graceful_restart_timeout(timer *t)
if (p->llgr_ready) if (p->llgr_ready)
{ {
struct bgp_channel *c; struct bgp_channel *c;
WALK_LIST(c, p->p.channels) BGP_WALK_CHANNELS(p, c)
{ {
/* Channel is not in GR and is already flushed */ /* Channel is not in GR and is already flushed */
if (!c->gr_active) if (!c->gr_active)
@ -1414,6 +1394,10 @@ bgp_reload_routes(struct channel *C)
struct bgp_proto *p = (void *) C->proto; struct bgp_proto *p = (void *) C->proto;
struct bgp_channel *c = (void *) C; struct bgp_channel *c = (void *) C;
/* Ignore non-BGP channels */
if (C->channel != &channel_bgp)
return;
ASSERT(p->conn && (p->route_refresh || c->c.in_table)); ASSERT(p->conn && (p->route_refresh || c->c.in_table));
if (c->c.in_table) if (c->c.in_table)
@ -1428,6 +1412,10 @@ bgp_feed_begin(struct channel *C, int initial)
struct bgp_proto *p = (void *) C->proto; struct bgp_proto *p = (void *) C->proto;
struct bgp_channel *c = (void *) C; struct bgp_channel *c = (void *) C;
/* Ignore non-BGP channels */
if (C->channel != &channel_bgp)
return;
/* This should not happen */ /* This should not happen */
if (!p->conn) if (!p->conn)
return; return;
@ -1453,6 +1441,10 @@ bgp_feed_end(struct channel *C)
struct bgp_proto *p = (void *) C->proto; struct bgp_proto *p = (void *) C->proto;
struct bgp_channel *c = (void *) C; struct bgp_channel *c = (void *) C;
/* Ignore non-BGP channels */
if (C->channel != &channel_bgp)
return;
/* This should not happen */ /* This should not happen */
if (!p->conn) if (!p->conn)
return; return;
@ -1567,7 +1559,7 @@ bgp_start(struct proto *P)
if (p->p.gr_recovery && p->cf->gr_mode) if (p->p.gr_recovery && p->cf->gr_mode)
{ {
struct bgp_channel *c; struct bgp_channel *c;
WALK_LIST(c, p->p.channels) BGP_WALK_CHANNELS(p, c)
channel_graceful_restart_lock(&c->c); channel_graceful_restart_lock(&c->c);
} }
@ -1700,6 +1692,7 @@ bgp_init(struct proto_config *CF)
P->rte_mergable = bgp_rte_mergable; P->rte_mergable = bgp_rte_mergable;
P->rte_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL; P->rte_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL;
P->rte_modify = bgp_rte_modify_stale; P->rte_modify = bgp_rte_modify_stale;
P->rte_igp_metric = bgp_rte_igp_metric;
p->cf = cf; p->cf = cf;
p->is_internal = (cf->local_as == cf->remote_as); p->is_internal = (cf->local_as == cf->remote_as);
@ -1720,7 +1713,7 @@ bgp_init(struct proto_config *CF)
/* Add all channels */ /* Add all channels */
struct bgp_channel_config *cc; struct bgp_channel_config *cc;
WALK_LIST(cc, CF->channels) BGP_CF_WALK_CHANNELS(cf, cc)
proto_add_channel(P, &cc->c); proto_add_channel(P, &cc->c);
return P; return P;
@ -1866,7 +1859,7 @@ bgp_find_channel_config(struct bgp_config *cf, u32 afi)
{ {
struct bgp_channel_config *cc; struct bgp_channel_config *cc;
WALK_LIST(cc, cf->c.channels) BGP_CF_WALK_CHANNELS(cf, cc)
if (cc->afi == afi) if (cc->afi == afi)
return cc; return cc;
@ -1983,6 +1976,15 @@ bgp_postconfig(struct proto_config *CF)
if (internal && cf->rs_client) if (internal && cf->rs_client)
cf_error("Only external neighbor can be RS client"); cf_error("Only external neighbor can be RS client");
if (internal && (cf->local_role != BGP_ROLE_UNDEFINED))
cf_error("Local role cannot be set on IBGP sessions");
if (interior && (cf->local_role != BGP_ROLE_UNDEFINED))
log(L_WARN "BGP roles are not recommended to be used within AS confederations");
if (cf->require_roles && (cf->local_role == BGP_ROLE_UNDEFINED))
cf_error("Local role must be set if roles are required");
if (!cf->confederation && cf->confederation_member) if (!cf->confederation && cf->confederation_member)
cf_error("Confederation ID must be set for member sessions"); cf_error("Confederation ID must be set for member sessions");
@ -2005,9 +2007,24 @@ bgp_postconfig(struct proto_config *CF)
if (internal && cf->enforce_first_as) if (internal && cf->enforce_first_as)
cf_error("Enforce first AS check is requires EBGP sessions"); cf_error("Enforce first AS check is requires EBGP sessions");
if (cf->keepalive_time > cf->hold_time)
cf_error("Keepalive time must be at most hold time");
if (cf->keepalive_time > (cf->hold_time / 2))
log(L_WARN "Keepalive time should be at most 1/2 of hold time");
if (cf->min_hold_time > cf->hold_time)
cf_error("Min hold time (%u) exceeds hold time (%u)",
cf->min_hold_time, cf->hold_time);
uint keepalive_time = cf->keepalive_time ?: cf->hold_time / 3;
if (cf->min_keepalive_time > keepalive_time)
cf_error("Min keepalive time (%u) exceeds keepalive time (%u)",
cf->min_keepalive_time, keepalive_time);
struct bgp_channel_config *cc; struct bgp_channel_config *cc;
WALK_LIST(cc, CF->channels) BGP_CF_WALK_CHANNELS(cf, cc)
{ {
/* Handle undefined import filter */ /* Handle undefined import filter */
if (cc->c.in_filter == FILTER_UNDEF) if (cc->c.in_filter == FILTER_UNDEF)
@ -2035,6 +2052,10 @@ bgp_postconfig(struct proto_config *CF)
if (!cc->gw_mode) if (!cc->gw_mode)
cc->gw_mode = cf->multihop ? GW_RECURSIVE : GW_DIRECT; cc->gw_mode = cf->multihop ? GW_RECURSIVE : GW_DIRECT;
/* Different default for next_hop_prefer */
if (!cc->next_hop_prefer)
cc->next_hop_prefer = (cc->gw_mode == GW_DIRECT) ? NHP_GLOBAL : NHP_LOCAL;
/* Defaults based on proto config */ /* Defaults based on proto config */
if (cc->gr_able == 0xff) if (cc->gr_able == 0xff)
cc->gr_able = (cf->gr_mode == BGP_GR_ABLE); cc->gr_able = (cf->gr_mode == BGP_GR_ABLE);
@ -2114,20 +2135,16 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF)
WALK_LIST(C, p->p.channels) WALK_LIST(C, p->p.channels)
C->stale = 1; C->stale = 1;
WALK_LIST(cc, new->c.channels) BGP_CF_WALK_CHANNELS(new, cc)
{ {
C = (struct channel *) bgp_find_channel(p, cc->afi); C = (struct channel *) bgp_find_channel(p, cc->afi);
same = proto_configure_channel(P, &C, &cc->c) && same; same = proto_configure_channel(P, &C, &cc->c) && same;
if (C)
C->stale = 0;
} }
WALK_LIST_DELSAFE(C, C2, p->p.channels) WALK_LIST_DELSAFE(C, C2, p->p.channels)
if (C->stale) if (C->stale)
same = proto_configure_channel(P, &C, NULL) && same; same = proto_configure_channel(P, &C, NULL) && same;
if (same && (p->start_state > BSS_PREPARE)) if (same && (p->start_state > BSS_PREPARE))
bgp_update_bfd(p, new->bfd); bgp_update_bfd(p, new->bfd);
@ -2169,6 +2186,7 @@ bgp_channel_reconfigure(struct channel *C, struct channel_config *CC, int *impor
return 0; return 0;
if ((new->gw_mode != old->gw_mode) || if ((new->gw_mode != old->gw_mode) ||
(new->next_hop_prefer != old->next_hop_prefer) ||
(new->aigp != old->aigp) || (new->aigp != old->aigp) ||
(new->cost != old->cost)) (new->cost != old->cost))
{ {
@ -2345,6 +2363,15 @@ bgp_show_afis(int code, char *s, u32 *afis, uint count)
cli_msg(code, b.start); cli_msg(code, b.start);
} }
static const char *
bgp_format_role_name(u8 role)
{
static const char *bgp_role_names[] = { "provider", "rs_server", "rs_client", "customer", "peer" };
if (role == BGP_ROLE_UNDEFINED) return "undefined";
if (role < ARRAY_SIZE(bgp_role_names)) return bgp_role_names[role];
return "?";
}
static void static void
bgp_show_capabilities(struct bgp_proto *p UNUSED, struct bgp_caps *caps) bgp_show_capabilities(struct bgp_proto *p UNUSED, struct bgp_caps *caps)
{ {
@ -2473,6 +2500,9 @@ bgp_show_capabilities(struct bgp_proto *p UNUSED, struct bgp_caps *caps)
if (caps->hostname) if (caps->hostname)
cli_msg(-1006, " Hostname: %s", caps->hostname); cli_msg(-1006, " Hostname: %s", caps->hostname);
if (caps->role != BGP_ROLE_UNDEFINED)
cli_msg(-1006, " Role: %s", bgp_format_role_name(caps->role));
} }
static void static void
@ -2560,6 +2590,9 @@ bgp_show_proto_info(struct proto *P)
{ {
channel_show_info(&c->c); channel_show_info(&c->c);
if (c->c.channel != &channel_bgp)
continue;
if (p->gr_active_num) if (p->gr_active_num)
cli_msg(-1006, " Neighbor GR: %s", bgp_gr_states[c->gr_active]); cli_msg(-1006, " Neighbor GR: %s", bgp_gr_states[c->gr_active]);
@ -2615,3 +2648,8 @@ struct protocol proto_bgp = {
.get_route_info = bgp_get_route_info, .get_route_info = bgp_get_route_info,
.show_proto_info = bgp_show_proto_info .show_proto_info = bgp_show_proto_info
}; };
void bgp_build(void)
{
proto_build(&proto_bgp);
}

View file

@ -20,7 +20,6 @@
#include "lib/hash.h" #include "lib/hash.h"
#include "lib/socket.h" #include "lib/socket.h"
struct linpool;
struct eattr; struct eattr;
@ -114,13 +113,18 @@ struct bgp_config {
int gr_mode; /* Graceful restart mode (BGP_GR_*) */ int gr_mode; /* Graceful restart mode (BGP_GR_*) */
int llgr_mode; /* Long-lived graceful restart mode (BGP_LLGR_*) */ int llgr_mode; /* Long-lived graceful restart mode (BGP_LLGR_*) */
int setkey; /* Set MD5 password to system SA/SP database */ int setkey; /* Set MD5 password to system SA/SP database */
u8 local_role; /* Set peering role with neighbor [RFC 9234] */
int require_roles; /* Require configured roles on both sides */
/* Times below are in seconds */ /* Times below are in seconds */
unsigned gr_time; /* Graceful restart timeout */ unsigned gr_time; /* Graceful restart timeout */
unsigned llgr_time; /* Long-lived graceful restart stale time */ unsigned llgr_time; /* Long-lived graceful restart stale time */
unsigned connect_delay_time; /* Minimum delay between connect attempts */ unsigned connect_delay_time; /* Minimum delay between connect attempts */
unsigned connect_retry_time; /* Timeout for connect attempts */ unsigned connect_retry_time; /* Timeout for connect attempts */
unsigned hold_time, initial_hold_time; unsigned hold_time;
unsigned min_hold_time; /* Minimum accepted hold time */
unsigned initial_hold_time;
unsigned keepalive_time; unsigned keepalive_time;
unsigned min_keepalive_time; /* Minimum accepted keepalive time */
unsigned error_amnesia_time; /* Errors are forgotten after */ unsigned error_amnesia_time; /* Errors are forgotten after */
unsigned error_delay_time_min; /* Time to wait after an error is detected */ unsigned error_delay_time_min; /* Time to wait after an error is detected */
unsigned error_delay_time_max; unsigned error_delay_time_max;
@ -144,6 +148,7 @@ struct bgp_channel_config {
ip_addr next_hop_addr; /* Local address for NEXT_HOP attribute */ ip_addr next_hop_addr; /* Local address for NEXT_HOP attribute */
u8 next_hop_self; /* Always set next hop to local IP address (NH_*) */ u8 next_hop_self; /* Always set next hop to local IP address (NH_*) */
u8 next_hop_keep; /* Do not modify next hop attribute (NH_*) */ u8 next_hop_keep; /* Do not modify next hop attribute (NH_*) */
u8 next_hop_prefer; /* Prefer global or link-local next hop (NHP_*) */
u8 mandatory; /* Channel is mandatory in capability negotiation */ u8 mandatory; /* Channel is mandatory in capability negotiation */
u8 gw_mode; /* How we compute route gateway from next_hop attr, see GW_* */ u8 gw_mode; /* How we compute route gateway from next_hop attr, see GW_* */
u8 secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */ u8 secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */
@ -167,6 +172,13 @@ struct bgp_channel_config {
#define BGP_PT_INTERNAL 1 #define BGP_PT_INTERNAL 1
#define BGP_PT_EXTERNAL 2 #define BGP_PT_EXTERNAL 2
#define BGP_ROLE_UNDEFINED 255
#define BGP_ROLE_PROVIDER 0
#define BGP_ROLE_RS_SERVER 1
#define BGP_ROLE_RS_CLIENT 2
#define BGP_ROLE_CUSTOMER 3
#define BGP_ROLE_PEER 4
#define NH_NO 0 #define NH_NO 0
#define NH_ALL 1 #define NH_ALL 1
#define NH_IBGP 2 #define NH_IBGP 2
@ -179,6 +191,9 @@ struct bgp_channel_config {
#define GW_DIRECT 1 #define GW_DIRECT 1
#define GW_RECURSIVE 2 #define GW_RECURSIVE 2
#define NHP_GLOBAL 1
#define NHP_LOCAL 2
#define BGP_ADD_PATH_RX 1 #define BGP_ADD_PATH_RX 1
#define BGP_ADD_PATH_TX 2 #define BGP_ADD_PATH_TX 2
#define BGP_ADD_PATH_FULL 3 #define BGP_ADD_PATH_FULL 3
@ -203,6 +218,10 @@ struct bgp_channel_config {
#define BGP_BFD_GRACEFUL 2 /* BFD down triggers graceful restart */ #define BGP_BFD_GRACEFUL 2 /* BFD down triggers graceful restart */
/* rte->pflags */
#define BGP_REF_SUPPRESSED 0x1 /* Used for deterministic MED comparison */
#define BGP_REF_STALE 0x2 /* Route is LLGR_STATE */
#define BGP_REF_NOT_STALE 0x4 /* Route is NOT LLGR_STATE */
struct bgp_af_caps { struct bgp_af_caps {
u32 afi; u32 afi;
@ -223,6 +242,7 @@ struct bgp_caps {
u8 ext_messages; /* Extended message length, RFC draft */ u8 ext_messages; /* Extended message length, RFC draft */
u8 route_refresh; /* Route refresh capability, RFC 2918 */ u8 route_refresh; /* Route refresh capability, RFC 2918 */
u8 enhanced_refresh; /* Enhanced route refresh, RFC 7313 */ u8 enhanced_refresh; /* Enhanced route refresh, RFC 7313 */
u8 role; /* BGP role capability, RFC 9234 */
u8 gr_aware; /* Graceful restart capability, RFC 4724 */ u8 gr_aware; /* Graceful restart capability, RFC 4724 */
u8 gr_flags; /* Graceful restart flags */ u8 gr_flags; /* Graceful restart flags */
@ -454,7 +474,6 @@ struct bgp_parse_state {
jmp_buf err_jmpbuf; jmp_buf err_jmpbuf;
struct hostentry *hostentry; struct hostentry *hostentry;
struct rtable *base_table;
adata *mpls_labels; adata *mpls_labels;
/* Cached state for bgp_rte_update() */ /* Cached state for bgp_rte_update() */
@ -473,6 +492,9 @@ struct bgp_parse_state {
#define BGP_RX_BUFFER_EXT_SIZE 65535 #define BGP_RX_BUFFER_EXT_SIZE 65535
#define BGP_TX_BUFFER_EXT_SIZE 65535 #define BGP_TX_BUFFER_EXT_SIZE 65535
#define BGP_CF_WALK_CHANNELS(P,C) WALK_LIST(C, P->c.channels) if (C->c.channel == &channel_bgp)
#define BGP_WALK_CHANNELS(P,C) WALK_LIST(C, P->p.channels) if (C->c.channel == &channel_bgp)
static inline int bgp_channel_is_ipv4(struct bgp_channel *c) static inline int bgp_channel_is_ipv4(struct bgp_channel *c)
{ return BGP_AFI(c->afi) == BGP_AFI_IPV4; } { return BGP_AFI(c->afi) == BGP_AFI_IPV4; }
@ -485,6 +507,12 @@ static inline int bgp_cc_is_ipv4(struct bgp_channel_config *c)
static inline int bgp_cc_is_ipv6(struct bgp_channel_config *c) static inline int bgp_cc_is_ipv6(struct bgp_channel_config *c)
{ return BGP_AFI(c->afi) == BGP_AFI_IPV6; } { return BGP_AFI(c->afi) == BGP_AFI_IPV6; }
static inline int bgp_channel_is_role_applicable(struct bgp_channel *c)
{ return (c->afi == BGP_AF_IPV4 || c->afi == BGP_AF_IPV6); }
static inline int bgp_cc_is_role_applicable(struct bgp_channel_config *c)
{ return (c->afi == BGP_AF_IPV4 || c->afi == BGP_AF_IPV6); }
static inline uint bgp_max_packet_length(struct bgp_conn *conn) static inline uint bgp_max_packet_length(struct bgp_conn *conn)
{ return conn->ext_messages ? BGP_MAX_EXT_MSG_LENGTH : BGP_MAX_MESSAGE_LENGTH; } { return conn->ext_messages ? BGP_MAX_EXT_MSG_LENGTH : BGP_MAX_MESSAGE_LENGTH; }
@ -495,9 +523,6 @@ bgp_parse_error(struct bgp_parse_state *s, uint subcode)
longjmp(s->err_jmpbuf, 1); longjmp(s->err_jmpbuf, 1);
} }
extern struct linpool *bgp_linpool;
extern struct linpool *bgp_linpool2;
void bgp_start_timer(timer *t, uint value); void bgp_start_timer(timer *t, uint value);
void bgp_check_config(struct bgp_config *c); void bgp_check_config(struct bgp_config *c);
@ -564,9 +589,7 @@ bgp_set_attr_data(ea_list **to, struct linpool *pool, uint code, uint flags, voi
bgp_set_attr(to, pool, code, flags, (uintptr_t) a); bgp_set_attr(to, pool, code, flags, (uintptr_t) a);
} }
static inline void #define bgp_unset_attr(to, pool, code) ea_unset_attr(to, pool, 0, code)
bgp_unset_attr(ea_list **to, struct linpool *pool, uint code)
{ eattr *e = bgp_set_attr(to, pool, code, 0, 0); e->type = EAF_TYPE_UNDEF; }
int bgp_encode_mp_reach_mrt(struct bgp_write_state *s, eattr *a, byte *buf, uint size); int bgp_encode_mp_reach_mrt(struct bgp_write_state *s, eattr *a, byte *buf, uint size);
@ -588,8 +611,9 @@ int bgp_rte_better(struct rte *, struct rte *);
int bgp_rte_mergable(rte *pri, rte *sec); int bgp_rte_mergable(rte *pri, rte *sec);
int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best); int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best);
struct rte *bgp_rte_modify_stale(struct rte *r, struct linpool *pool); struct rte *bgp_rte_modify_stale(struct rte *r, struct linpool *pool);
u32 bgp_rte_igp_metric(struct rte *);
void bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old); void bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old);
int bgp_preexport(struct proto *, struct rte **, struct linpool *); int bgp_preexport(struct channel *, struct rte *);
int bgp_get_attr(const struct eattr *e, byte *buf, int buflen); int bgp_get_attr(const struct eattr *e, byte *buf, int buflen);
void bgp_get_route_info(struct rte *, byte *buf); void bgp_get_route_info(struct rte *, byte *buf);
int bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad); int bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad);
@ -660,6 +684,7 @@ void bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to);
#define BA_AS4_AGGREGATOR 0x12 /* RFC 6793 */ #define BA_AS4_AGGREGATOR 0x12 /* RFC 6793 */
#define BA_AIGP 0x1a /* RFC 7311 */ #define BA_AIGP 0x1a /* RFC 7311 */
#define BA_LARGE_COMMUNITY 0x20 /* RFC 8092 */ #define BA_LARGE_COMMUNITY 0x20 /* RFC 8092 */
#define BA_ONLY_TO_CUSTOMER 0x23 /* RFC 9234 */
/* Bird's private internal BGP attributes */ /* Bird's private internal BGP attributes */
#define BA_MPLS_LABEL_STACK 0xfe /* MPLS label stack transfer attribute */ #define BA_MPLS_LABEL_STACK 0xfe /* MPLS label stack transfer attribute */

View file

@ -31,7 +31,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6, LONG, STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6, LONG,
LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS, LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS,
DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE, DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE,
FIRST, FREE, VALIDATE, BASE) FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER,
RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, PREFER, GLOBAL)
%type <i> bgp_nh %type <i> bgp_nh
%type <i32> bgp_afi %type <i32> bgp_afi
@ -40,7 +41,7 @@ CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION, CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION,
OUT, OF, RESOURCES) OUT, OF, RESOURCES)
%type<i> bgp_cease_mask bgp_cease_list bgp_cease_flag %type<i> bgp_cease_mask bgp_cease_list bgp_cease_flag bgp_role_name
CF_GRAMMAR CF_GRAMMAR
@ -72,6 +73,7 @@ bgp_proto_start: proto_start BGP {
BGP_CFG->llgr_mode = -1; BGP_CFG->llgr_mode = -1;
BGP_CFG->llgr_time = 3600; BGP_CFG->llgr_time = 3600;
BGP_CFG->setkey = 1; BGP_CFG->setkey = 1;
BGP_CFG->local_role = BGP_ROLE_UNDEFINED;
BGP_CFG->dynamic_name = "dynbgp"; BGP_CFG->dynamic_name = "dynbgp";
BGP_CFG->check_link = -1; BGP_CFG->check_link = -1;
} }
@ -114,6 +116,14 @@ bgp_cease_flag:
| OUT OF RESOURCES { $$ = 1 << 8; } | OUT OF RESOURCES { $$ = 1 << 8; }
; ;
bgp_role_name:
PEER { $$ = BGP_ROLE_PEER; }
| PROVIDER { $$ = BGP_ROLE_PROVIDER; }
| CUSTOMER { $$ = BGP_ROLE_CUSTOMER; }
| RS_SERVER { $$ = BGP_ROLE_RS_SERVER; }
| RS_CLIENT { $$ = BGP_ROLE_RS_CLIENT; }
;
bgp_proto: bgp_proto:
bgp_proto_start proto_name '{' bgp_proto_start proto_name '{'
| bgp_proto proto_item ';' | bgp_proto proto_item ';'
@ -143,7 +153,8 @@ bgp_proto:
| bgp_proto RS CLIENT bool ';' { BGP_CFG->rs_client = $4; } | bgp_proto RS CLIENT bool ';' { BGP_CFG->rs_client = $4; }
| bgp_proto CONFEDERATION expr ';' { BGP_CFG->confederation = $3; } | bgp_proto CONFEDERATION expr ';' { BGP_CFG->confederation = $3; }
| bgp_proto CONFEDERATION MEMBER bool ';' { BGP_CFG->confederation_member = $4; } | bgp_proto CONFEDERATION MEMBER bool ';' { BGP_CFG->confederation_member = $4; }
| bgp_proto HOLD TIME expr ';' { BGP_CFG->hold_time = $4; } | bgp_proto HOLD TIME expr ';' { BGP_CFG->hold_time = $4; if (($4 && $4<3) || ($4>65535)) cf_error("Hold time must be in range 3-65535 or zero"); }
| bgp_proto MIN HOLD TIME expr ';' { BGP_CFG->min_hold_time = $5; }
| bgp_proto STARTUP HOLD TIME expr ';' { BGP_CFG->initial_hold_time = $5; } | bgp_proto STARTUP HOLD TIME expr ';' { BGP_CFG->initial_hold_time = $5; }
| bgp_proto DIRECT ';' { BGP_CFG->multihop = 0; } | bgp_proto DIRECT ';' { BGP_CFG->multihop = 0; }
| bgp_proto MULTIHOP ';' { BGP_CFG->multihop = 64; } | bgp_proto MULTIHOP ';' { BGP_CFG->multihop = 64; }
@ -167,7 +178,8 @@ bgp_proto:
| bgp_proto START DELAY TIME expr ';' { BGP_CFG->connect_delay_time = $5; log(L_WARN "%s: Start delay time option is deprecated, use connect delay time", this_proto->name); } | bgp_proto START DELAY TIME expr ';' { BGP_CFG->connect_delay_time = $5; log(L_WARN "%s: Start delay time option is deprecated, use connect delay time", this_proto->name); }
| bgp_proto CONNECT DELAY TIME expr ';' { BGP_CFG->connect_delay_time = $5; } | bgp_proto CONNECT DELAY TIME expr ';' { BGP_CFG->connect_delay_time = $5; }
| bgp_proto CONNECT RETRY TIME expr ';' { BGP_CFG->connect_retry_time = $5; } | bgp_proto CONNECT RETRY TIME expr ';' { BGP_CFG->connect_retry_time = $5; }
| bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; } | bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; if (($4<1) || ($4>65535)) cf_error("Keepalive time must be in range 1-65535"); }
| bgp_proto MIN KEEPALIVE TIME expr ';' { BGP_CFG->min_keepalive_time = $5; }
| bgp_proto ERROR FORGET TIME expr ';' { BGP_CFG->error_amnesia_time = $5; } | bgp_proto ERROR FORGET TIME expr ';' { BGP_CFG->error_amnesia_time = $5; }
| bgp_proto ERROR WAIT TIME expr ',' expr ';' { BGP_CFG->error_delay_time_min = $5; BGP_CFG->error_delay_time_max = $7; } | bgp_proto ERROR WAIT TIME expr ',' expr ';' { BGP_CFG->error_delay_time_min = $5; BGP_CFG->error_delay_time_max = $7; }
| bgp_proto DISABLE AFTER ERROR bool ';' { BGP_CFG->disable_after_error = $5; } | bgp_proto DISABLE AFTER ERROR bool ';' { BGP_CFG->disable_after_error = $5; }
@ -197,6 +209,8 @@ bgp_proto:
| bgp_proto BFD GRACEFUL ';' { init_bfd_opts(&BGP_CFG->bfd); BGP_CFG->bfd->mode = BGP_BFD_GRACEFUL; } | bgp_proto BFD GRACEFUL ';' { init_bfd_opts(&BGP_CFG->bfd); BGP_CFG->bfd->mode = BGP_BFD_GRACEFUL; }
| bgp_proto BFD { open_bfd_opts(&BGP_CFG->bfd); } bfd_opts { close_bfd_opts(); } ';' | bgp_proto BFD { open_bfd_opts(&BGP_CFG->bfd); } bfd_opts { close_bfd_opts(); } ';'
| bgp_proto ENFORCE FIRST AS bool ';' { BGP_CFG->enforce_first_as = $5; } | bgp_proto ENFORCE FIRST AS bool ';' { BGP_CFG->enforce_first_as = $5; }
| bgp_proto LOCAL ROLE bgp_role_name ';' { BGP_CFG->local_role = $4; }
| bgp_proto REQUIRE ROLES bool ';' { BGP_CFG->require_roles = $4; }
; ;
bgp_afi: bgp_afi:
@ -251,6 +265,7 @@ bgp_channel_item:
| NEXT HOP ADDRESS ipa { BGP_CC->next_hop_addr = $4; } | NEXT HOP ADDRESS ipa { BGP_CC->next_hop_addr = $4; }
| NEXT HOP SELF bgp_nh { BGP_CC->next_hop_self = $4; } | NEXT HOP SELF bgp_nh { BGP_CC->next_hop_self = $4; }
| NEXT HOP KEEP bgp_nh { BGP_CC->next_hop_keep = $4; } | NEXT HOP KEEP bgp_nh { BGP_CC->next_hop_keep = $4; }
| NEXT HOP PREFER GLOBAL { BGP_CC->next_hop_prefer = NHP_GLOBAL; }
| MANDATORY bool { BGP_CC->mandatory = $2; } | MANDATORY bool { BGP_CC->mandatory = $2; }
| MISSING LLADDR bgp_lladdr { log(L_WARN "%s.%s: Missing lladdr option is deprecated and ignored, remove it", this_proto->name, this_channel->name); } | MISSING LLADDR bgp_lladdr { log(L_WARN "%s.%s: Missing lladdr option is deprecated and ignored, remove it", this_proto->name, this_channel->name); }
| GATEWAY DIRECT { BGP_CC->gw_mode = GW_DIRECT; } | GATEWAY DIRECT { BGP_CC->gw_mode = GW_DIRECT; }
@ -343,6 +358,8 @@ dynamic_attr: BGP_AIGP
{ $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AIGP)); } ; { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AIGP)); } ;
dynamic_attr: BGP_LARGE_COMMUNITY dynamic_attr: BGP_LARGE_COMMUNITY
{ $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, T_LCLIST, EA_CODE(PROTOCOL_BGP, BA_LARGE_COMMUNITY)); } ; { $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, T_LCLIST, EA_CODE(PROTOCOL_BGP, BA_LARGE_COMMUNITY)); } ;
dynamic_attr: BGP_OTC
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_ONLY_TO_CUSTOMER)); } ;

View file

@ -238,6 +238,7 @@ bgp_prepare_capabilities(struct bgp_conn *conn)
caps->ext_messages = p->cf->enable_extended_messages; caps->ext_messages = p->cf->enable_extended_messages;
caps->route_refresh = p->cf->enable_refresh; caps->route_refresh = p->cf->enable_refresh;
caps->enhanced_refresh = p->cf->enable_refresh; caps->enhanced_refresh = p->cf->enable_refresh;
caps->role = p->cf->local_role;
if (caps->as4_support) if (caps->as4_support)
caps->as4_number = p->public_as; caps->as4_number = p->public_as;
@ -261,7 +262,7 @@ bgp_prepare_capabilities(struct bgp_conn *conn)
} }
/* Allocate and fill per-AF fields */ /* Allocate and fill per-AF fields */
WALK_LIST(c, p->p.channels) BGP_WALK_CHANNELS(p, c)
{ {
ac = &caps->af_data[caps->af_count++]; ac = &caps->af_data[caps->af_count++];
ac->afi = c->afi; ac->afi = c->afi;
@ -350,6 +351,13 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
*buf++ = 0; /* Capability data length */ *buf++ = 0; /* Capability data length */
} }
if (caps->role != BGP_ROLE_UNDEFINED)
{
*buf++ = 9; /* Capability 9: Announce chosen BGP role */
*buf++ = 1; /* Capability data length */
*buf++ = caps->role;
}
if (caps->gr_aware) if (caps->gr_aware)
{ {
*buf++ = 64; /* Capability 64: Support for graceful restart */ *buf++ = 64; /* Capability 64: Support for graceful restart */
@ -449,11 +457,15 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
struct bgp_proto *p = conn->bgp; struct bgp_proto *p = conn->bgp;
struct bgp_caps *caps; struct bgp_caps *caps;
struct bgp_af_caps *ac; struct bgp_af_caps *ac;
uint err_subcode = 0;
int i, cl; int i, cl;
u32 af; u32 af;
if (!conn->remote_caps) if (!conn->remote_caps)
{
caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + sizeof(struct bgp_af_caps)); caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + sizeof(struct bgp_af_caps));
caps->role = BGP_ROLE_UNDEFINED;
}
else else
{ {
caps = conn->remote_caps; caps = conn->remote_caps;
@ -513,6 +525,21 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
caps->ext_messages = 1; caps->ext_messages = 1;
break; break;
case 9: /* BGP role capability, RFC 9234 */
if (cl != 1)
goto err;
/* Reserved value */
if (pos[2] == BGP_ROLE_UNDEFINED)
{ err_subcode = 11; goto err; }
/* Multiple inconsistent values */
if ((caps->role != BGP_ROLE_UNDEFINED) && (caps->role != pos[2]))
{ err_subcode = 11; goto err; }
caps->role = pos[2];
break;
case 64: /* Graceful restart capability, RFC 4724 */ case 64: /* Graceful restart capability, RFC 4724 */
if (cl % 4 != 2) if (cl % 4 != 2)
goto err; goto err;
@ -638,7 +665,7 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
err: err:
mb_free(caps); mb_free(caps);
bgp_error(conn, 2, 0, NULL, 0); bgp_error(conn, 2, err_subcode, NULL, 0);
return -1; return -1;
} }
@ -654,7 +681,7 @@ bgp_check_capabilities(struct bgp_conn *conn)
/* This is partially overlapping with bgp_conn_enter_established_state(), /* This is partially overlapping with bgp_conn_enter_established_state(),
but we need to run this just after we receive OPEN message */ but we need to run this just after we receive OPEN message */
WALK_LIST(c, p->p.channels) BGP_WALK_CHANNELS(p, c)
{ {
const struct bgp_af_caps *loc = bgp_find_af_caps(local, c->afi); const struct bgp_af_caps *loc = bgp_find_af_caps(local, c->afi);
const struct bgp_af_caps *rem = bgp_find_af_caps(remote, c->afi); const struct bgp_af_caps *rem = bgp_find_af_caps(remote, c->afi);
@ -820,9 +847,25 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
if (bgp_read_options(conn, pkt+29, pkt[28], len-29) < 0) if (bgp_read_options(conn, pkt+29, pkt[28], len-29) < 0)
return; return;
/* RFC 4271 4.2 - hold time must be either 0 or at least 3 */
if (hold > 0 && hold < 3) if (hold > 0 && hold < 3)
{ bgp_error(conn, 2, 6, pkt+22, 2); return; } { bgp_error(conn, 2, 6, pkt+22, 2); return; }
/* Compute effective hold and keepalive times */
uint hold_time = MIN(hold, p->cf->hold_time);
uint keepalive_time = p->cf->keepalive_time ?
(p->cf->keepalive_time * hold_time / p->cf->hold_time) :
hold_time / 3;
/* Keepalive time might be rounded down to zero */
if (hold_time && !keepalive_time)
keepalive_time = 1;
/* Check effective values against configured minimums */
if ((hold_time < p->cf->min_hold_time) ||
(keepalive_time < p->cf->min_keepalive_time))
{ bgp_error(conn, 2, 6, pkt+22, 2); return; }
/* RFC 6286 2.2 - router ID is nonzero and AS-wide unique */ /* RFC 6286 2.2 - router ID is nonzero and AS-wide unique */
if (!id || (p->is_internal && id == p->local_id)) if (!id || (p->is_internal && id == p->local_id))
{ bgp_error(conn, 2, 3, pkt+24, -4); return; } { bgp_error(conn, 2, 3, pkt+24, -4); return; }
@ -854,6 +897,22 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
conn->received_as = asn; conn->received_as = asn;
} }
/* RFC 9234 4.2 - check role agreement */
u8 local_role = p->cf->local_role;
u8 neigh_role = caps->role;
if ((local_role != BGP_ROLE_UNDEFINED) &&
(neigh_role != BGP_ROLE_UNDEFINED) &&
!((local_role == BGP_ROLE_PEER && neigh_role == BGP_ROLE_PEER) ||
(local_role == BGP_ROLE_CUSTOMER && neigh_role == BGP_ROLE_PROVIDER) ||
(local_role == BGP_ROLE_PROVIDER && neigh_role == BGP_ROLE_CUSTOMER) ||
(local_role == BGP_ROLE_RS_CLIENT && neigh_role == BGP_ROLE_RS_SERVER) ||
(local_role == BGP_ROLE_RS_SERVER && neigh_role == BGP_ROLE_RS_CLIENT)))
{ bgp_error(conn, 2, 11, NULL, 0); return; }
if ((p->cf->require_roles) && (neigh_role == BGP_ROLE_UNDEFINED))
{ bgp_error(conn, 2, 11, NULL, 0); return; }
/* Check the other connection */ /* Check the other connection */
other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn; other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn;
switch (other->state) switch (other->state)
@ -904,8 +963,8 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
} }
/* Update our local variables */ /* Update our local variables */
conn->hold_time = MIN(hold, p->cf->hold_time); conn->hold_time = hold_time;
conn->keepalive_time = p->cf->keepalive_time ? : conn->hold_time / 3; conn->keepalive_time = keepalive_time;
conn->as4_session = conn->local_caps->as4_support && caps->as4_support; conn->as4_session = conn->local_caps->as4_support && caps->as4_support;
conn->ext_messages = conn->local_caps->ext_messages && caps->ext_messages; conn->ext_messages = conn->local_caps->ext_messages && caps->ext_messages;
p->remote_id = id; p->remote_id = id;
@ -977,7 +1036,8 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
WITHDRAW(BAD_NEXT_HOP " - zero address"); WITHDRAW(BAD_NEXT_HOP " - zero address");
rtable *tab = ipa_is_ip4(gw) ? c->igp_table_ip4 : c->igp_table_ip6; rtable *tab = ipa_is_ip4(gw) ? c->igp_table_ip4 : c->igp_table_ip6;
s->hostentry = rt_get_hostentry(tab, gw, ll, c->c.table); ip_addr lla = (c->cf->next_hop_prefer == NHP_LOCAL) ? ll : IPA_NONE;
s->hostentry = rt_get_hostentry(tab, gw, lla, c->c.table);
if (!s->mpls) if (!s->mpls)
rta_apply_hostentry(a, s->hostentry, NULL); rta_apply_hostentry(a, s->hostentry, NULL);
@ -1025,9 +1085,6 @@ bgp_apply_flow_validation(struct bgp_parse_state *s, const net_addr *n, rta *a)
int valid = rt_flowspec_check(c->base_table, c->c.table, n, a, s->proto->is_interior); int valid = rt_flowspec_check(c->base_table, c->c.table, n, a, s->proto->is_interior);
a->dest = valid ? RTD_NONE : RTD_UNREACHABLE; a->dest = valid ? RTD_NONE : RTD_UNREACHABLE;
/* Set rte.bgp.base_table later from this state variable */
s->base_table = c->base_table;
/* Invalidate cached rta if dest changes */ /* Invalidate cached rta if dest changes */
if (s->cached_rta && (s->cached_rta->dest != a->dest)) if (s->cached_rta && (s->cached_rta->dest != a->dest))
{ {
@ -1065,13 +1122,17 @@ bgp_use_next_hop(struct bgp_export_state *s, eattr *a)
return 1; return 1;
/* Keep it when explicitly set in export filter */ /* Keep it when explicitly set in export filter */
if (a->type & EAF_FRESH) if (a->fresh)
return 1; return 1;
/* Check for non-matching AF */ /* Check for non-matching AF */
if ((ipa_is_ip4(*nh) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop) if ((ipa_is_ip4(*nh) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop)
return 0; return 0;
/* Do not pass NEXT_HOP between different VRFs */
if (p->p.vrf_set && s->src && s->src->p.vrf_set && (p->p.vrf != s->src->p.vrf))
return 0;
/* Keep it when exported to internal peers */ /* Keep it when exported to internal peers */
if (p->is_interior && ipa_nonzero(*nh)) if (p->is_interior && ipa_nonzero(*nh))
return 1; return 1;
@ -1101,6 +1162,10 @@ bgp_use_gateway(struct bgp_export_state *s)
if ((ipa_is_ip4(ra->nh.gw) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop) if ((ipa_is_ip4(ra->nh.gw) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop)
return 0; return 0;
/* Do not use gateway from different VRF */
if (p->p.vrf_set && ra->nh.iface && (p->p.vrf != ra->nh.iface->master))
return 0;
/* Use it when exported to internal peers */ /* Use it when exported to internal peers */
if (p->is_interior) if (p->is_interior)
return 1; return 1;
@ -1127,6 +1192,8 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
uint lnum = ra->nh.labels ? ra->nh.labels : 1; uint lnum = ra->nh.labels ? ra->nh.labels : 1;
bgp_set_attr_data(to, s->pool, BA_MPLS_LABEL_STACK, 0, labels, lnum * 4); bgp_set_attr_data(to, s->pool, BA_MPLS_LABEL_STACK, 0, labels, lnum * 4);
} }
else
bgp_unset_attr(to, s->pool, BA_MPLS_LABEL_STACK);
} }
else else
{ {
@ -1140,6 +1207,8 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
u32 implicit_null = BGP_MPLS_NULL; u32 implicit_null = BGP_MPLS_NULL;
bgp_set_attr_data(to, s->pool, BA_MPLS_LABEL_STACK, 0, &implicit_null, 4); bgp_set_attr_data(to, s->pool, BA_MPLS_LABEL_STACK, 0, &implicit_null, 4);
} }
else
bgp_unset_attr(to, s->pool, BA_MPLS_LABEL_STACK);
} }
} }
@ -1386,8 +1455,6 @@ bgp_rte_update(struct bgp_parse_state *s, const net_addr *n, u32 path_id, rta *a
/* Prepare cached route attributes */ /* Prepare cached route attributes */
if (s->cached_rta == NULL) if (s->cached_rta == NULL)
{ {
a0->src = s->last_src;
/* Workaround for rta_lookup() breaking eattrs */ /* Workaround for rta_lookup() breaking eattrs */
ea_list *ea = a0->eattrs; ea_list *ea = a0->eattrs;
s->cached_rta = rta_lookup(a0); s->cached_rta = rta_lookup(a0);
@ -1395,12 +1462,9 @@ bgp_rte_update(struct bgp_parse_state *s, const net_addr *n, u32 path_id, rta *a
} }
rta *a = rta_clone(s->cached_rta); rta *a = rta_clone(s->cached_rta);
rte *e = rte_get_temp(a); rte *e = rte_get_temp(a, s->last_src);
e->pflags = 0; e->pflags = 0;
e->u.bgp.suppressed = 0;
e->u.bgp.stale = -1;
e->u.bgp.base_table = s->base_table;
rte_update3(&s->channel->c, n, e, s->last_src); rte_update3(&s->channel->c, n, e, s->last_src);
} }
@ -2331,11 +2395,14 @@ bgp_create_update(struct bgp_channel *c, byte *buf)
again: ; again: ;
struct lp_state tmpp;
lp_save(tmp_linpool, &tmpp);
/* Initialize write state */ /* Initialize write state */
struct bgp_write_state s = { struct bgp_write_state s = {
.proto = p, .proto = p,
.channel = c, .channel = c,
.pool = bgp_linpool, .pool = tmp_linpool,
.mp_reach = (c->afi != BGP_AF_IPV4) || c->ext_next_hop, .mp_reach = (c->afi != BGP_AF_IPV4) || c->ext_next_hop,
.as4_session = p->as4_session, .as4_session = p->as4_session,
.add_path = c->add_path_tx, .add_path = c->add_path_tx,
@ -2361,6 +2428,7 @@ again: ;
if (EMPTY_LIST(buck->prefixes)) if (EMPTY_LIST(buck->prefixes))
{ {
bgp_free_bucket(c, buck); bgp_free_bucket(c, buck);
lp_restore(tmp_linpool, &tmpp);
goto again; goto again;
} }
@ -2374,7 +2442,10 @@ again: ;
bgp_defer_bucket(c, buck); bgp_defer_bucket(c, buck);
if (!res) if (!res)
{
lp_restore(tmp_linpool, &tmpp);
goto again; goto again;
}
goto done; goto done;
} }
@ -2385,7 +2456,7 @@ again: ;
done: done:
BGP_TRACE_RL(&rl_snd_update, D_PACKETS, "Sending UPDATE"); BGP_TRACE_RL(&rl_snd_update, D_PACKETS, "Sending UPDATE");
p->stats.tx_updates++; p->stats.tx_updates++;
lp_flush(s.pool); lp_restore(tmp_linpool, &tmpp);
return res; return res;
} }
@ -2464,8 +2535,6 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis
s->last_id = 0; s->last_id = 0;
s->last_src = s->proto->p.main_source; s->last_src = s->proto->p.main_source;
s->base_table = NULL;
/* /*
* IPv4 BGP and MP-BGP may be used together in one update, therefore we do not * IPv4 BGP and MP-BGP may be used together in one update, therefore we do not
* add BA_NEXT_HOP in bgp_decode_attrs(), but we add it here independently for * add BA_NEXT_HOP in bgp_decode_attrs(), but we add it here independently for
@ -2481,6 +2550,7 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis
a->scope = SCOPE_UNIVERSE; a->scope = SCOPE_UNIVERSE;
a->from = s->proto->remote_ip; a->from = s->proto->remote_ip;
a->eattrs = ea; a->eattrs = ea;
a->pref = c->c.preference;
c->desc->decode_next_hop(s, nh, nh_len, a); c->desc->decode_next_hop(s, nh, nh_len, a);
bgp_finish_attrs(s, a); bgp_finish_attrs(s, a);
@ -2515,10 +2585,13 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
bgp_start_timer(conn->hold_timer, conn->hold_time); bgp_start_timer(conn->hold_timer, conn->hold_time);
struct lp_state tmpp;
lp_save(tmp_linpool, &tmpp);
/* Initialize parse state */ /* Initialize parse state */
struct bgp_parse_state s = { struct bgp_parse_state s = {
.proto = p, .proto = p,
.pool = bgp_linpool, .pool = tmp_linpool,
.as4_session = p->as4_session, .as4_session = p->as4_session,
}; };
@ -2596,7 +2669,7 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
done: done:
rta_free(s.cached_rta); rta_free(s.cached_rta);
lp_flush(s.pool); lp_restore(tmp_linpool, &tmpp);
return; return;
} }
@ -2984,6 +3057,7 @@ static struct {
{ 2, 6, "Unacceptable hold time" }, { 2, 6, "Unacceptable hold time" },
{ 2, 7, "Required capability missing" }, /* [RFC5492] */ { 2, 7, "Required capability missing" }, /* [RFC5492] */
{ 2, 8, "No supported AFI/SAFI" }, /* This error msg is nonstandard */ { 2, 8, "No supported AFI/SAFI" }, /* This error msg is nonstandard */
{ 2,11, "Role mismatch" }, /* From Open Policy, RFC 9234 */
{ 3, 0, "Invalid UPDATE message" }, { 3, 0, "Invalid UPDATE message" },
{ 3, 1, "Malformed attribute list" }, { 3, 1, "Malformed attribute list" },
{ 3, 2, "Unrecognized well-known attribute" }, { 3, 2, "Unrecognized well-known attribute" },
@ -3078,8 +3152,8 @@ bgp_log_error(struct bgp_proto *p, u8 class, char *msg, uint code, uint subcode,
if (len) if (len)
{ {
/* Bad peer AS - we would like to print the AS */ /* Bad peer AS / unacceptable hold time - print the value as decimal number */
if ((code == 2) && (subcode == 2) && ((len == 2) || (len == 4))) if ((code == 2) && ((subcode == 2) || (subcode == 6)) && ((len == 2) || (len == 4)))
{ {
t += bsprintf(t, ": %u", (len == 2) ? get_u16(data) : get_u32(data)); t += bsprintf(t, ": %u", (len == 2) ? get_u16(data) : get_u32(data));
goto done; goto done;

View file

@ -472,9 +472,9 @@ mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r)
#ifdef CONFIG_BGP #ifdef CONFIG_BGP
/* Find peer index */ /* Find peer index */
if (r->attrs->src->proto->proto == &proto_bgp) if (r->src->proto->proto == &proto_bgp)
{ {
struct bgp_proto *p = (void *) r->attrs->src->proto; struct bgp_proto *p = (void *) r->src->proto;
struct mrt_peer_entry *n = struct mrt_peer_entry *n =
HASH_FIND(s->peer_hash, PEER, p->remote_id, p->remote_as, p->remote_ip); HASH_FIND(s->peer_hash, PEER, p->remote_id, p->remote_as, p->remote_ip);
@ -488,7 +488,7 @@ mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r)
/* Path Identifier */ /* Path Identifier */
if (s->add_path) if (s->add_path)
mrt_put_u32(b, r->attrs->src->private_id); mrt_put_u32(b, r->src->private_id);
/* Route Attributes */ /* Route Attributes */
mrt_put_u16(b, 0); mrt_put_u16(b, 0);
@ -519,14 +519,12 @@ mrt_rib_table_dump(struct mrt_table_dump_state *s, net *n, int add_path)
continue; continue;
/* Skip routes that should be reported in the other phase */ /* Skip routes that should be reported in the other phase */
if (!s->always_add_path && (!rt->attrs->src->private_id != !s->add_path)) if (!s->always_add_path && (!rt->src->private_id != !s->add_path))
{ {
s->want_add_path = 1; s->want_add_path = 1;
continue; continue;
} }
rte_make_tmp_attrs(&rt, s->linpool, NULL);
if (f_run(s->filter, &rt, s->linpool, 0) <= F_ACCEPT) if (f_run(s->filter, &rt, s->linpool, 0) <= F_ACCEPT)
mrt_rib_table_entry(s, rt); mrt_rib_table_entry(s, rt);
@ -562,8 +560,8 @@ mrt_table_dump_init(pool *pp)
struct mrt_table_dump_state *s = mb_allocz(pool, sizeof(struct mrt_table_dump_state)); struct mrt_table_dump_state *s = mb_allocz(pool, sizeof(struct mrt_table_dump_state));
s->pool = pool; s->pool = pool;
s->linpool = lp_new(pool, 4080); s->linpool = lp_new(pool);
s->peer_lp = lp_new(pool, 4080); s->peer_lp = lp_new(pool);
mrt_buffer_init(&s->buf, pool, 2 * MRT_ATTR_BUFFER_SIZE); mrt_buffer_init(&s->buf, pool, 2 * MRT_ATTR_BUFFER_SIZE);
/* We lock the current config as we may reference it indirectly by filter */ /* We lock the current config as we may reference it indirectly by filter */
@ -918,3 +916,9 @@ struct protocol proto_mrt = {
.reconfigure = mrt_reconfigure, .reconfigure = mrt_reconfigure,
.copy_config = mrt_copy_config, .copy_config = mrt_copy_config,
}; };
void
mrt_build(void)
{
proto_build(&proto_mrt);
}

View file

@ -522,7 +522,10 @@ static inline void
add_nbma_node(struct ospf_iface *ifa, struct nbma_node *src, int found) add_nbma_node(struct ospf_iface *ifa, struct nbma_node *src, int found)
{ {
struct nbma_node *n = mb_alloc(ifa->pool, sizeof(struct nbma_node)); struct nbma_node *n = mb_alloc(ifa->pool, sizeof(struct nbma_node));
n->n = (node) {};
add_tail(&ifa->nbma_list, NODE n); add_tail(&ifa->nbma_list, NODE n);
n->ip = src->ip; n->ip = src->ip;
n->eligible = src->eligible; n->eligible = src->eligible;
n->found = found; n->found = found;

View file

@ -107,12 +107,10 @@
#include <stdlib.h> #include <stdlib.h>
#include "ospf.h" #include "ospf.h"
static int ospf_preexport(struct proto *P, rte **new, struct linpool *pool); static int ospf_preexport(struct channel *P, rte *new);
static void ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool);
static void ospf_store_tmp_attrs(struct rte *rt, struct linpool *pool);
static void ospf_reload_routes(struct channel *C); static void ospf_reload_routes(struct channel *C);
static int ospf_rte_better(struct rte *new, struct rte *old); static int ospf_rte_better(struct rte *new, struct rte *old);
static int ospf_rte_same(struct rte *new, struct rte *old); static u32 ospf_rte_igp_metric(struct rte *rt);
static void ospf_disp(timer *timer); static void ospf_disp(timer *timer);
@ -301,7 +299,7 @@ ospf_start(struct proto *P)
p->lsab_size = 256; p->lsab_size = 256;
p->lsab_used = 0; p->lsab_used = 0;
p->lsab = mb_alloc(P->pool, p->lsab_size); p->lsab = mb_alloc(P->pool, p->lsab_size);
p->nhpool = lp_new(P->pool, 12*sizeof(struct nexthop)); p->nhpool = lp_new(P->pool);
init_list(&(p->iface_list)); init_list(&(p->iface_list));
init_list(&(p->area_list)); init_list(&(p->area_list));
fib_init(&p->rtf, P->pool, ospf_get_af(p), sizeof(ort), OFFSETOF(ort, fn), 0, NULL); fib_init(&p->rtf, P->pool, ospf_get_af(p), sizeof(ort), OFFSETOF(ort, fn), 0, NULL);
@ -378,10 +376,8 @@ ospf_init(struct proto_config *CF)
P->reload_routes = ospf_reload_routes; P->reload_routes = ospf_reload_routes;
P->feed_begin = ospf_feed_begin; P->feed_begin = ospf_feed_begin;
P->feed_end = ospf_feed_end; P->feed_end = ospf_feed_end;
P->make_tmp_attrs = ospf_make_tmp_attrs;
P->store_tmp_attrs = ospf_store_tmp_attrs;
P->rte_better = ospf_rte_better; P->rte_better = ospf_rte_better;
P->rte_same = ospf_rte_same; P->rte_igp_metric = ospf_rte_igp_metric;
return P; return P;
} }
@ -390,7 +386,9 @@ ospf_init(struct proto_config *CF)
static int static int
ospf_rte_better(struct rte *new, struct rte *old) ospf_rte_better(struct rte *new, struct rte *old)
{ {
if (new->u.ospf.metric1 == LSINFINITY) u32 new_metric1 = ea_get_int(new->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
if (new_metric1 == LSINFINITY)
return 0; return 0;
if(new->attrs->source < old->attrs->source) return 1; if(new->attrs->source < old->attrs->source) return 1;
@ -398,27 +396,27 @@ ospf_rte_better(struct rte *new, struct rte *old)
if(new->attrs->source == RTS_OSPF_EXT2) if(new->attrs->source == RTS_OSPF_EXT2)
{ {
if(new->u.ospf.metric2 < old->u.ospf.metric2) return 1; u32 old_metric2 = ea_get_int(old->attrs->eattrs, EA_OSPF_METRIC2, LSINFINITY);
if(new->u.ospf.metric2 > old->u.ospf.metric2) return 0; u32 new_metric2 = ea_get_int(new->attrs->eattrs, EA_OSPF_METRIC2, LSINFINITY);
if(new_metric2 < old_metric2) return 1;
if(new_metric2 > old_metric2) return 0;
} }
if (new->u.ospf.metric1 < old->u.ospf.metric1) u32 old_metric1 = ea_get_int(old->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
if (new_metric1 < old_metric1)
return 1; return 1;
return 0; /* Old is shorter or same */ return 0; /* Old is shorter or same */
} }
static int static u32
ospf_rte_same(struct rte *new, struct rte *old) ospf_rte_igp_metric(struct rte *rt)
{ {
/* new->attrs == old->attrs always */ if (rt->attrs->source == RTS_OSPF_EXT2)
return return IGP_METRIC_UNKNOWN;
new->u.ospf.metric1 == old->u.ospf.metric1 &&
new->u.ospf.metric2 == old->u.ospf.metric2 &&
new->u.ospf.tag == old->u.ospf.tag &&
new->u.ospf.router_id == old->u.ospf.router_id;
}
return ea_get_int(rt->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
}
void void
ospf_schedule_rtcalc(struct ospf_proto *p) ospf_schedule_rtcalc(struct ospf_proto *p)
@ -484,14 +482,13 @@ ospf_disp(timer * timer)
* import to the filters. * import to the filters.
*/ */
static int static int
ospf_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED) ospf_preexport(struct channel *C, rte *e)
{ {
struct ospf_proto *p = (struct ospf_proto *) P; struct ospf_proto *p = (struct ospf_proto *) C->proto;
struct ospf_area *oa = ospf_main_area(p); struct ospf_area *oa = ospf_main_area(p);
rte *e = *new;
/* Reject our own routes */ /* Reject our own routes */
if (e->attrs->src->proto == P) if (e->src->proto == &p->p)
return -1; return -1;
/* Do not export routes to stub areas */ /* Do not export routes to stub areas */
@ -501,26 +498,6 @@ ospf_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
return 0; return 0;
} }
static void
ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool)
{
rte_init_tmp_attrs(rt, pool, 4);
rte_make_tmp_attr(rt, EA_OSPF_METRIC1, EAF_TYPE_INT, rt->u.ospf.metric1);
rte_make_tmp_attr(rt, EA_OSPF_METRIC2, EAF_TYPE_INT, rt->u.ospf.metric2);
rte_make_tmp_attr(rt, EA_OSPF_TAG, EAF_TYPE_INT, rt->u.ospf.tag);
rte_make_tmp_attr(rt, EA_OSPF_ROUTER_ID, EAF_TYPE_ROUTER_ID, rt->u.ospf.router_id);
}
static void
ospf_store_tmp_attrs(struct rte *rt, struct linpool *pool)
{
rte_init_tmp_attrs(rt, pool, 4);
rt->u.ospf.metric1 = rte_store_tmp_attr(rt, EA_OSPF_METRIC1);
rt->u.ospf.metric2 = rte_store_tmp_attr(rt, EA_OSPF_METRIC2);
rt->u.ospf.tag = rte_store_tmp_attr(rt, EA_OSPF_TAG);
rt->u.ospf.router_id = rte_store_tmp_attr(rt, EA_OSPF_ROUTER_ID);
}
/** /**
* ospf_shutdown - Finish of OSPF instance * ospf_shutdown - Finish of OSPF instance
* @P: OSPF protocol instance * @P: OSPF protocol instance
@ -558,6 +535,9 @@ ospf_shutdown(struct proto *P)
} }
FIB_WALK_END; FIB_WALK_END;
if (tm_active(p->disp_timer))
tm_stop(p->disp_timer);
return PS_DOWN; return PS_DOWN;
} }
@ -607,16 +587,20 @@ ospf_get_route_info(rte * rte, byte * buf)
} }
buf += bsprintf(buf, " %s", type); buf += bsprintf(buf, " %s", type);
buf += bsprintf(buf, " (%d/%d", rte->pref, rte->u.ospf.metric1); buf += bsprintf(buf, " (%d/%d", rte->attrs->pref, ea_get_int(rte->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY));
if (rte->attrs->source == RTS_OSPF_EXT2) if (rte->attrs->source == RTS_OSPF_EXT2)
buf += bsprintf(buf, "/%d", rte->u.ospf.metric2); buf += bsprintf(buf, "/%d", ea_get_int(rte->attrs->eattrs, EA_OSPF_METRIC2, LSINFINITY));
buf += bsprintf(buf, ")"); buf += bsprintf(buf, ")");
if ((rte->attrs->source == RTS_OSPF_EXT1 || rte->attrs->source == RTS_OSPF_EXT2) && rte->u.ospf.tag) if (rte->attrs->source == RTS_OSPF_EXT1 || rte->attrs->source == RTS_OSPF_EXT2)
{ {
buf += bsprintf(buf, " [%x]", rte->u.ospf.tag); eattr *ea = ea_find(rte->attrs->eattrs, EA_OSPF_TAG);
if (ea && (ea->u.data > 0))
buf += bsprintf(buf, " [%x]", ea->u.data);
} }
if (rte->u.ospf.router_id)
buf += bsprintf(buf, " [%R]", rte->u.ospf.router_id); eattr *ea = ea_find(rte->attrs->eattrs, EA_OSPF_ROUTER_ID);
if (ea)
buf += bsprintf(buf, " [%R]", ea->u.data);
} }
static int static int
@ -1550,3 +1534,9 @@ struct protocol proto_ospf = {
.get_attr = ospf_get_attr, .get_attr = ospf_get_attr,
.get_route_info = ospf_get_route_info .get_route_info = ospf_get_route_info
}; };
void
ospf_build(void)
{
proto_build(&proto_ospf);
}

View file

@ -144,7 +144,7 @@ orta_compare(const struct ospf_proto *p, const orta *new, const orta *old)
{ {
int r; int r;
if (old->type == RTS_DUMMY) if (!old->type)
return 1; return 1;
/* Prefer intra-area to inter-area to externals */ /* Prefer intra-area to inter-area to externals */
@ -195,7 +195,7 @@ orta_compare_asbr(const struct ospf_proto *p, const orta *new, const orta *old)
{ {
int r; int r;
if (old->type == RTS_DUMMY) if (!old->type)
return 1; return 1;
if (!p->rfc1583) if (!p->rfc1583)
@ -225,7 +225,7 @@ orta_compare_ext(const struct ospf_proto *p, const orta *new, const orta *old)
{ {
int r; int r;
if (old->type == RTS_DUMMY) if (!old->type)
return 1; return 1;
/* 16.4 (6a) - prefer routes with lower type */ /* 16.4 (6a) - prefer routes with lower type */
@ -2053,32 +2053,54 @@ again1:
if (nf->n.type) /* Add the route */ if (nf->n.type) /* Add the route */
{ {
rta a0 = { rta a0 = {
.src = p->p.main_source,
.source = nf->n.type, .source = nf->n.type,
.scope = SCOPE_UNIVERSE, .scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST, .dest = RTD_UNICAST,
.nh = *(nf->n.nhs), .nh = *(nf->n.nhs),
.pref = p->p.main_channel->preference,
}; };
if (reload || ort_changed(nf, &a0)) if (reload || ort_changed(nf, &a0))
{ {
a0.eattrs = alloca(sizeof(ea_list) + 4 * sizeof(eattr));
memset(a0.eattrs, 0, sizeof(ea_list));
nf->old_metric1 = nf->n.metric1;
nf->old_metric2 = nf->n.metric2;
nf->old_tag = nf->n.tag;
nf->old_rid = nf->n.rid;
a0.eattrs->attrs[a0.eattrs->count++] = (eattr) {
.id = EA_OSPF_METRIC1,
.type = EAF_TYPE_INT,
.u.data = nf->n.metric1,
};
if (nf->n.type == RTS_OSPF_EXT2)
a0.eattrs->attrs[a0.eattrs->count++] = (eattr) {
.id = EA_OSPF_METRIC2,
.type = EAF_TYPE_INT,
.u.data = nf->n.metric2,
};
if ((nf->n.type == RTS_OSPF_EXT1) || (nf->n.type == RTS_OSPF_EXT2))
a0.eattrs->attrs[a0.eattrs->count++] = (eattr) {
.id = EA_OSPF_TAG,
.type = EAF_TYPE_INT,
.u.data = nf->n.tag,
};
a0.eattrs->attrs[a0.eattrs->count++] = (eattr) {
.id = EA_OSPF_ROUTER_ID,
.type = EAF_TYPE_ROUTER_ID,
.u.data = nf->n.rid,
};
rta *a = rta_lookup(&a0); rta *a = rta_lookup(&a0);
rte *e = rte_get_temp(a); rte *e = rte_get_temp(a, p->p.main_source);
rta_free(nf->old_rta); rta_free(nf->old_rta);
nf->old_rta = rta_clone(a); nf->old_rta = rta_clone(a);
e->u.ospf.metric1 = nf->old_metric1 = nf->n.metric1;
e->u.ospf.metric2 = nf->old_metric2 = nf->n.metric2;
e->u.ospf.tag = nf->old_tag = nf->n.tag;
e->u.ospf.router_id = nf->old_rid = nf->n.rid;
e->pflags = EA_ID_FLAG(EA_OSPF_METRIC1) | EA_ID_FLAG(EA_OSPF_ROUTER_ID);
if (nf->n.type == RTS_OSPF_EXT2)
e->pflags |= EA_ID_FLAG(EA_OSPF_METRIC2);
/* Perhaps onfly if tag is non-zero? */
if ((nf->n.type == RTS_OSPF_EXT1) || (nf->n.type == RTS_OSPF_EXT2))
e->pflags |= EA_ID_FLAG(EA_OSPF_TAG);
DBG("Mod rte type %d - %N via %I on iface %s, met %d\n", DBG("Mod rte type %d - %N via %I on iface %s, met %d\n",
a0.source, nf->fn.addr, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1); a0.source, nf->fn.addr, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1);

View file

@ -2135,7 +2135,7 @@ ospf_hash_delete(struct top_graph *f, struct top_hash_entry *e)
if (*ee == e) if (*ee == e)
{ {
*ee = e->next; *ee = e->next;
sl_free(f->hash_slab, e); sl_free(e);
if (f->hash_entries-- < f->hash_entries_min) if (f->hash_entries-- < f->hash_entries_min)
ospf_top_rehash(f, -HASH_LO_STEP); ospf_top_rehash(f, -HASH_LO_STEP);
return; return;

View file

@ -143,10 +143,10 @@ perf_loop(void *data)
if (!p->attrs_per_rte || !(i % p->attrs_per_rte)) { if (!p->attrs_per_rte || !(i % p->attrs_per_rte)) {
struct rta a0 = { struct rta a0 = {
.src = p->p.main_source,
.source = RTS_PERF, .source = RTS_PERF,
.scope = SCOPE_UNIVERSE, .scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST, .dest = RTD_UNICAST,
.pref = p->p.main_channel->preference,
.nh.iface = p->ifa->iface, .nh.iface = p->ifa->iface,
.nh.gw = gw, .nh.gw = gw,
.nh.weight = 1, .nh.weight = 1,
@ -161,7 +161,7 @@ perf_loop(void *data)
clock_gettime(CLOCK_MONOTONIC, &ts_generated); clock_gettime(CLOCK_MONOTONIC, &ts_generated);
for (uint i=0; i<N; i++) { for (uint i=0; i<N; i++) {
rte *e = rte_get_temp(p->data[i].a); rte *e = rte_get_temp(p->data[i].a, p->p.main_source);
e->pflags = 0; e->pflags = 0;
rte_update(P, &(p->data[i].net), e); rte_update(P, &(p->data[i].net), e);
@ -315,3 +315,9 @@ struct protocol proto_perf = {
.reconfigure = perf_reconfigure, .reconfigure = perf_reconfigure,
.copy_config = perf_copy_config, .copy_config = perf_copy_config,
}; };
void
perf_build(void)
{
proto_build(&proto_perf);
}

View file

@ -43,6 +43,10 @@
#include "pipe.h" #include "pipe.h"
#ifdef CONFIG_BGP
#include "proto/bgp/bgp.h"
#endif
static void static void
pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *old) pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *old)
{ {
@ -65,34 +69,26 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *o
if (new) if (new)
{ {
src = new->src;
a = alloca(rta_size(new->attrs)); a = alloca(rta_size(new->attrs));
memcpy(a, new->attrs, rta_size(new->attrs)); memcpy(a, new->attrs, rta_size(new->attrs));
a->aflags = 0; a->cached = 0;
a->hostentry = NULL; a->hostentry = NULL;
e = rte_get_temp(a); e = rte_get_temp(a, src);
e->pflags = 0;
/* Copy protocol specific embedded attributes. */
memcpy(&(e->u), &(new->u), sizeof(e->u));
e->pref = new->pref;
e->pflags = new->pflags; e->pflags = new->pflags;
#ifdef CONFIG_BGP #ifdef CONFIG_BGP
/* Hack to cleanup cached value */ /* Hack to cleanup cached value */
if (e->attrs->src->proto->proto == &proto_bgp) if (e->src->proto->proto == &proto_bgp)
{ e->pflags &= ~(BGP_REF_STALE | BGP_REF_NOT_STALE);
e->u.bgp.stale = -1;
e->u.bgp.base_table = NULL;
}
#endif #endif
src = a->src;
} }
else else
{ {
e = NULL; e = NULL;
src = old->attrs->src; src = old->src;
} }
src_ch->table->pipe_busy = 1; src_ch->table->pipe_busy = 1;
@ -101,11 +97,11 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *o
} }
static int static int
pipe_preexport(struct proto *P, rte **ee, struct linpool *p UNUSED) pipe_preexport(struct channel *C, rte *e)
{ {
struct proto *pp = (*ee)->sender->proto; struct proto *pp = e->sender->proto;
if (pp == P) if (pp == C->proto)
return -1; /* Avoid local loops automatically */ return -1; /* Avoid local loops automatically */
return 0; return 0;
@ -307,3 +303,9 @@ struct protocol proto_pipe = {
.get_status = pipe_get_status, .get_status = pipe_get_status,
.show_proto_info = pipe_show_proto_info .show_proto_info = pipe_show_proto_info
}; };
void
pipe_build(void)
{
proto_build(&proto_pipe);
}

View file

@ -391,12 +391,12 @@ radv_net_match_trigger(struct radv_config *cf, net *n)
} }
int int
radv_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED) radv_preexport(struct channel *C, rte *new)
{ {
// struct radv_proto *p = (struct radv_proto *) P; // struct radv_proto *p = (struct radv_proto *) P;
struct radv_config *cf = (struct radv_config *) (P->cf); struct radv_config *cf = (struct radv_config *) (C->proto->cf);
if (radv_net_match_trigger(cf, (*new)->net)) if (radv_net_match_trigger(cf, new->net))
return RIC_PROCESS; return RIC_PROCESS;
if (cf->propagate_routes) if (cf->propagate_routes)
@ -555,7 +555,7 @@ radv_check_active(struct radv_proto *p)
return 1; return 1;
struct channel *c = p->p.main_channel; struct channel *c = p->p.main_channel;
return rt_examine(c->table, &cf->trigger, &p->p, c->out_filter); return rt_examine(c->table, &cf->trigger, c, c->out_filter);
} }
static void static void
@ -771,3 +771,9 @@ struct protocol proto_radv = {
.get_status = radv_get_status, .get_status = radv_get_status,
.get_attr = radv_get_attr .get_attr = radv_get_attr
}; };
void
radv_build(void)
{
proto_build(&proto_radv);
}

View file

@ -108,14 +108,14 @@ rip_add_rte(struct rip_proto *p, struct rip_rte **rp, struct rip_rte *src)
} }
static inline void static inline void
rip_remove_rte(struct rip_proto *p, struct rip_rte **rp) rip_remove_rte(struct rip_proto *p UNUSED, struct rip_rte **rp)
{ {
struct rip_rte *rt = *rp; struct rip_rte *rt = *rp;
rip_unlock_neighbor(rt->from); rip_unlock_neighbor(rt->from);
*rp = rt->next; *rp = rt->next;
sl_free(p->rte_slab, rt); sl_free(rt);
} }
static inline int rip_same_rte(struct rip_rte *a, struct rip_rte *b) static inline int rip_same_rte(struct rip_rte *a, struct rip_rte *b)
@ -124,6 +124,11 @@ static inline int rip_same_rte(struct rip_rte *a, struct rip_rte *b)
static inline int rip_valid_rte(struct rip_rte *rt) static inline int rip_valid_rte(struct rip_rte *rt)
{ return rt->from->ifa != NULL; } { return rt->from->ifa != NULL; }
struct rip_iface_adata {
struct adata ad;
struct iface *iface;
};
/** /**
* rip_announce_rte - announce route from RIP routing table to the core * rip_announce_rte - announce route from RIP routing table to the core
* @p: RIP instance * @p: RIP instance
@ -145,7 +150,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
{ {
/* Update */ /* Update */
rta a0 = { rta a0 = {
.src = p->p.main_source, .pref = p->p.main_channel->preference,
.source = RTS_RIP, .source = RTS_RIP,
.scope = SCOPE_UNIVERSE, .scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST, .dest = RTD_UNICAST,
@ -188,13 +193,39 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
a0.nh.iface = rt->from->ifa->iface; a0.nh.iface = rt->from->ifa->iface;
} }
rta *a = rta_lookup(&a0); struct {
rte *e = rte_get_temp(a); ea_list l;
eattr e[3];
struct rip_iface_adata riad;
} ea_block = {
.l = { .count = 3, },
.e = {
{
.id = EA_RIP_METRIC,
.type = EAF_TYPE_INT,
.u.data = rt_metric,
},
{
.id = EA_RIP_TAG,
.type = EAF_TYPE_INT,
.u.data = rt_tag,
},
{
.id = EA_RIP_FROM,
.type = EAF_TYPE_IFACE,
.u.ptr = &ea_block.riad.ad,
}
},
.riad = {
.ad = { .length = sizeof(struct rip_iface_adata) - sizeof(struct adata) },
.iface = a0.nh.iface,
},
};
e->u.rip.from = a0.nh.iface; a0.eattrs = &ea_block.l;
e->u.rip.metric = rt_metric;
e->u.rip.tag = rt_tag; rta *a = rta_lookup(&a0);
e->pflags = EA_ID_FLAG(EA_RIP_METRIC) | EA_ID_FLAG(EA_RIP_TAG); rte *e = rte_get_temp(a, p->p.main_source);
rte_update(&p->p, en->n.addr, e); rte_update(&p->p, en->n.addr, e);
} }
@ -307,8 +338,10 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
if (new) if (new)
{ {
/* Update */ /* Update */
u32 rt_metric = ea_get_int(new->attrs->eattrs, EA_RIP_METRIC, 1);
u32 rt_tag = ea_get_int(new->attrs->eattrs, EA_RIP_TAG, 0); u32 rt_tag = ea_get_int(new->attrs->eattrs, EA_RIP_TAG, 0);
u32 rt_metric = ea_get_int(new->attrs->eattrs, EA_RIP_METRIC, 1);
const eattr *rie = ea_find(new->attrs->eattrs, EA_RIP_FROM);
struct iface *rt_from = rie ? ((struct rip_iface_adata *) rie->u.ptr)->iface : NULL;
if (rt_metric > p->infinity) if (rt_metric > p->infinity)
{ {
@ -339,7 +372,7 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
en->valid = RIP_ENTRY_VALID; en->valid = RIP_ENTRY_VALID;
en->metric = rt_metric; en->metric = rt_metric;
en->tag = rt_tag; en->tag = rt_tag;
en->from = (new->attrs->src->proto == P) ? new->u.rip.from : NULL; en->from = (new->src->proto == P) ? rt_from : NULL;
en->iface = new->attrs->nh.iface; en->iface = new->attrs->nh.iface;
en->next_hop = new->attrs->nh.gw; en->next_hop = new->attrs->nh.gw;
} }
@ -1068,37 +1101,18 @@ rip_reload_routes(struct channel *C)
rip_kick_timer(p); rip_kick_timer(p);
} }
static void static u32
rip_make_tmp_attrs(struct rte *rt, struct linpool *pool) rip_rte_igp_metric(struct rte *rt)
{ {
rte_init_tmp_attrs(rt, pool, 2); return ea_get_int(rt->attrs->eattrs, EA_RIP_METRIC, IGP_METRIC_UNKNOWN);
rte_make_tmp_attr(rt, EA_RIP_METRIC, EAF_TYPE_INT, rt->u.rip.metric);
rte_make_tmp_attr(rt, EA_RIP_TAG, EAF_TYPE_INT, rt->u.rip.tag);
}
static void
rip_store_tmp_attrs(struct rte *rt, struct linpool *pool)
{
rte_init_tmp_attrs(rt, pool, 2);
rt->u.rip.metric = rte_store_tmp_attr(rt, EA_RIP_METRIC);
rt->u.rip.tag = rte_store_tmp_attr(rt, EA_RIP_TAG);
} }
static int static int
rip_rte_better(struct rte *new, struct rte *old) rip_rte_better(struct rte *new, struct rte *old)
{ {
return new->u.rip.metric < old->u.rip.metric; return rip_rte_igp_metric(new) < rip_rte_igp_metric(old);
} }
static int
rip_rte_same(struct rte *new, struct rte *old)
{
return ((new->u.rip.metric == old->u.rip.metric) &&
(new->u.rip.tag == old->u.rip.tag) &&
(new->u.rip.from == old->u.rip.from));
}
static void static void
rip_postconfig(struct proto_config *CF) rip_postconfig(struct proto_config *CF)
{ {
@ -1120,10 +1134,8 @@ rip_init(struct proto_config *CF)
P->rt_notify = rip_rt_notify; P->rt_notify = rip_rt_notify;
P->neigh_notify = rip_neigh_notify; P->neigh_notify = rip_neigh_notify;
P->reload_routes = rip_reload_routes; P->reload_routes = rip_reload_routes;
P->make_tmp_attrs = rip_make_tmp_attrs;
P->store_tmp_attrs = rip_store_tmp_attrs;
P->rte_better = rip_rte_better; P->rte_better = rip_rte_better;
P->rte_same = rip_rte_same; P->rte_igp_metric = rip_rte_igp_metric;
return P; return P;
} }
@ -1198,10 +1210,14 @@ rip_reconfigure(struct proto *P, struct proto_config *CF)
static void static void
rip_get_route_info(rte *rte, byte *buf) rip_get_route_info(rte *rte, byte *buf)
{ {
buf += bsprintf(buf, " (%d/%d)", rte->pref, rte->u.rip.metric); struct rip_proto *p = (struct rip_proto *) rte->src->proto;
u32 rt_metric = ea_get_int(rte->attrs->eattrs, EA_RIP_METRIC, p->infinity);
u32 rt_tag = ea_get_int(rte->attrs->eattrs, EA_RIP_TAG, 0);
if (rte->u.rip.tag) buf += bsprintf(buf, " (%d/%d)", rte->attrs->pref, rt_metric);
bsprintf(buf, " [%04x]", rte->u.rip.tag);
if (rt_tag)
bsprintf(buf, " [%04x]", rt_tag);
} }
static int static int
@ -1338,3 +1354,9 @@ struct protocol proto_rip = {
.get_route_info = rip_get_route_info, .get_route_info = rip_get_route_info,
.get_attr = rip_get_attr .get_attr = rip_get_attr
}; };
void
rip_build(void)
{
proto_build(&proto_rip);
}

View file

@ -197,6 +197,7 @@ struct rip_rte
#define EA_RIP_METRIC EA_CODE(PROTOCOL_RIP, 0) #define EA_RIP_METRIC EA_CODE(PROTOCOL_RIP, 0)
#define EA_RIP_TAG EA_CODE(PROTOCOL_RIP, 1) #define EA_RIP_TAG EA_CODE(PROTOCOL_RIP, 1)
#define EA_RIP_FROM EA_CODE(PROTOCOL_RIP, 2)
static inline int rip_is_v2(struct rip_proto *p) static inline int rip_is_v2(struct rip_proto *p)
{ return p->rip2; } { return p->rip2; }

View file

@ -121,18 +121,18 @@ rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_
struct rpki_proto *p = cache->p; struct rpki_proto *p = cache->p;
rta a0 = { rta a0 = {
.src = p->p.main_source, .pref = channel->preference,
.source = RTS_RPKI, .source = RTS_RPKI,
.scope = SCOPE_UNIVERSE, .scope = SCOPE_UNIVERSE,
.dest = RTD_NONE, .dest = RTD_NONE,
}; };
rta *a = rta_lookup(&a0); rta *a = rta_lookup(&a0);
rte *e = rte_get_temp(a); rte *e = rte_get_temp(a, p->p.main_source);
e->pflags = 0; e->pflags = 0;
rte_update2(channel, &pfxr->n, e, a0.src); rte_update2(channel, &pfxr->n, e, e->src);
} }
void void
@ -278,12 +278,13 @@ rpki_cache_change_state(struct rpki_cache *cache, const enum rpki_cache_state ne
case RPKI_CS_NO_INCR_UPDATE_AVAIL: case RPKI_CS_NO_INCR_UPDATE_AVAIL:
/* Server was unable to answer the last Serial Query and sent Cache Reset. */ /* Server was unable to answer the last Serial Query and sent Cache Reset. */
rpki_cache_change_state(cache, RPKI_CS_RESET);
break;
case RPKI_CS_ERROR_NO_DATA_AVAIL: case RPKI_CS_ERROR_NO_DATA_AVAIL:
/* No validation records are available on the cache server. */ /* No validation records are available on the cache server. */
rpki_cache_change_state(cache, RPKI_CS_RESET);
if (old_state == RPKI_CS_ESTABLISHED)
rpki_cache_change_state(cache, RPKI_CS_RESET);
else
rpki_schedule_next_retry(cache);
break; break;
case RPKI_CS_ERROR_FATAL: case RPKI_CS_ERROR_FATAL:
@ -453,6 +454,11 @@ rpki_retry_hook(timer *tm)
} }
break; break;
case RPKI_CS_NO_INCR_UPDATE_AVAIL:
case RPKI_CS_ERROR_NO_DATA_AVAIL:
rpki_cache_change_state(cache, RPKI_CS_RESET);
break;
default: default:
rpki_cache_change_state(cache, RPKI_CS_CONNECTING); rpki_cache_change_state(cache, RPKI_CS_CONNECTING);
break; break;
@ -960,3 +966,9 @@ struct protocol proto_rpki = {
.reconfigure = rpki_reconfigure, .reconfigure = rpki_reconfigure,
.get_status = rpki_get_status, .get_status = rpki_get_status,
}; };
void
rpki_build(void)
{
proto_build(&proto_rpki);
}

View file

@ -40,7 +40,7 @@ static_route_finish(void)
if (net_type_match(this_srt->net, NB_DEST) == !this_srt->dest) if (net_type_match(this_srt->net, NB_DEST) == !this_srt->dest)
cf_error("Unexpected or missing nexthop/type"); cf_error("Unexpected or missing nexthop/type");
this_srt->cmds = f_linearize(this_srt_cmds); this_srt->cmds = f_linearize(this_srt_cmds, 0);
} }
CF_DECLS CF_DECLS

View file

@ -56,10 +56,11 @@ static void
static_announce_rte(struct static_proto *p, struct static_route *r) static_announce_rte(struct static_proto *p, struct static_route *r)
{ {
rta *a = allocz(RTA_MAX_SIZE); rta *a = allocz(RTA_MAX_SIZE);
a->src = static_get_source(p, r->index); struct rte_src *src = static_get_source(p, r->index);
a->source = RTS_STATIC; a->source = RTS_STATIC;
a->scope = SCOPE_UNIVERSE; a->scope = SCOPE_UNIVERSE;
a->dest = r->dest; a->dest = r->dest;
a->pref = p->p.main_channel->preference;
if (r->dest == RTD_UNICAST) if (r->dest == RTD_UNICAST)
{ {
@ -102,7 +103,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
return; return;
/* We skip rta_lookup() here */ /* We skip rta_lookup() here */
rte *e = rte_get_temp(a); rte *e = rte_get_temp(a, src);
e->pflags = 0; e->pflags = 0;
if (r->cmds) if (r->cmds)
@ -119,7 +120,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
e->net = NULL; e->net = NULL;
} }
rte_update2(p->p.main_channel, r->net, e, a->src); rte_update2(p->p.main_channel, r->net, e, src);
r->state = SRS_CLEAN; r->state = SRS_CLEAN;
if (r->cmds) if (r->cmds)
@ -131,7 +132,7 @@ withdraw:
if (r->state == SRS_DOWN) if (r->state == SRS_DOWN)
return; return;
rte_update2(p->p.main_channel, r->net, NULL, a->src); rte_update2(p->p.main_channel, r->net, NULL, src);
r->state = SRS_DOWN; r->state = SRS_DOWN;
} }
@ -485,7 +486,7 @@ static_start(struct proto *P)
struct static_route *r; struct static_route *r;
if (!static_lp) if (!static_lp)
static_lp = lp_new(&root_pool, LP_GOOD_SIZE(1024)); static_lp = lp_new(&root_pool);
if (p->igp_table_ip4) if (p->igp_table_ip4)
rt_lock_table(p->igp_table_ip4); rt_lock_table(p->igp_table_ip4);
@ -721,9 +722,9 @@ static_get_route_info(rte *rte, byte *buf)
{ {
eattr *a = ea_find(rte->attrs->eattrs, EA_GEN_IGP_METRIC); eattr *a = ea_find(rte->attrs->eattrs, EA_GEN_IGP_METRIC);
if (a) if (a)
buf += bsprintf(buf, " (%d/%u)", rte->pref, a->u.data); buf += bsprintf(buf, " (%d/%u)", rte->attrs->pref, a->u.data);
else else
buf += bsprintf(buf, " (%d)", rte->pref); buf += bsprintf(buf, " (%d)", rte->attrs->pref);
} }
static void static void
@ -792,3 +793,9 @@ struct protocol proto_static = {
.copy_config = static_copy_config, .copy_config = static_copy_config,
.get_route_info = static_get_route_info, .get_route_info = static_get_route_info,
}; };
void
static_build(void)
{
proto_build(&proto_static);
}

View file

@ -190,6 +190,10 @@ static inline void
sockaddr_fill_dl(struct sockaddr_dl *sa, struct iface *ifa) sockaddr_fill_dl(struct sockaddr_dl *sa, struct iface *ifa)
{ {
uint len = OFFSETOF(struct sockaddr_dl, sdl_data); uint len = OFFSETOF(struct sockaddr_dl, sdl_data);
/* Workaround for FreeBSD 13.0 */
len = MAX(len, sizeof(struct sockaddr));
memset(sa, 0, len); memset(sa, 0, len);
sa->sdl_len = len; sa->sdl_len = len;
sa->sdl_family = AF_LINK; sa->sdl_family = AF_LINK;
@ -347,7 +351,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
} }
void void
krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old) krt_replace_rte(struct krt_proto *p, net *n UNUSED, rte *new, rte *old)
{ {
int err = 0; int err = 0;
@ -519,7 +523,6 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
net = net_get(p->p.main_channel->table, &ndst); net = net_get(p->p.main_channel->table, &ndst);
rta a = { rta a = {
.src = p->p.main_source,
.source = RTS_INHERIT, .source = RTS_INHERIT,
.scope = SCOPE_UNIVERSE, .scope = SCOPE_UNIVERSE,
}; };
@ -580,18 +583,23 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
} }
done: done:
e = rte_get_temp(&a); e = rte_get_temp(&a, p->p.main_source);
e->net = net; e->net = net;
e->u.krt.src = src;
e->u.krt.proto = src2; ea_list *ea = alloca(sizeof(ea_list) + 1 * sizeof(eattr));
e->u.krt.seen = 0; *ea = (ea_list) { .count = 1, .next = e->attrs->eattrs };
e->u.krt.best = 0; e->attrs->eattrs = ea;
e->u.krt.metric = 0;
ea->attrs[0] = (eattr) {
.id = EA_KRT_SOURCE,
.type = EAF_TYPE_INT,
.u.data = src2,
};
if (scan) if (scan)
krt_got_route(p, e); krt_got_route(p, e, src);
else else
krt_got_route_async(p, e, new); krt_got_route_async(p, e, new, src);
} }
static void static void
@ -1198,7 +1206,7 @@ kif_update_sysdep_addr(struct iface *i)
return 0; return 0;
ip4_addr old = i->sysdep; ip4_addr old = i->sysdep;
i->sysdep = ipa_to_ip4(ipa_from_sa4(&ifr.ifr_addr)); i->sysdep = ipa_to_ip4(ipa_from_sa4((sockaddr *) &ifr.ifr_addr));
return !ip4_equal(i->sysdep, old); return !ip4_equal(i->sysdep, old);
} }

View file

@ -4,7 +4,6 @@ Available configuration variables:
CONFIG_AUTO_ROUTES Device routes are added automagically by the kernel CONFIG_AUTO_ROUTES Device routes are added automagically by the kernel
CONFIG_SELF_CONSCIOUS We're able to recognize whether route was installed by us CONFIG_SELF_CONSCIOUS We're able to recognize whether route was installed by us
CONFIG_MULTIPLE_TABLES The kernel supports multiple routing tables CONFIG_MULTIPLE_TABLES The kernel supports multiple routing tables
CONFIG_ALL_TABLES_AT_ONCE Kernel scanner wants to process all tables at once
CONFIG_SINGLE_ROUTE There is only one route per network CONFIG_SINGLE_ROUTE There is only one route per network
CONFIG_MC_PROPER_SRC Multicast packets have source address according to socket saddr field CONFIG_MC_PROPER_SRC Multicast packets have source address according to socket saddr field

34
sysdep/cf/bsd-netlink.h Normal file
View file

@ -0,0 +1,34 @@
/*
* Configuration for FreeBSD based systems with netlink support
*
* (c) 2022 Alexander Chernikov <melifaro@FreeBSD.org>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#define CONFIG_AUTO_ROUTES
#define CONFIG_SELF_CONSCIOUS
#define CONFIG_MULTIPLE_TABLES
#define CONFIG_SINGLE_ROUTE
#define CONFIG_SKIP_MC_BIND
#define CONFIG_NO_IFACE_BIND
#define CONFIG_USE_HDRINCL
#define CONFIG_INCLUDE_SYSIO_H "sysdep/bsd/sysio.h"
#define CONFIG_INCLUDE_KRTSYS_H "sysdep/linux/krt-sys.h"
#define CONFIG_FREEBSD_NETLINK
#ifndef AF_MPLS
#define AF_MPLS 39
#endif
#ifndef SO_RCVBUFFORCE
#define SO_RCVBUFFORCE SO_RCVBUF
#endif
/*
Link: sysdep/unix
Link: sysdep/bsd-netlink
*/

View file

@ -9,7 +9,6 @@
#define CONFIG_AUTO_ROUTES #define CONFIG_AUTO_ROUTES
#define CONFIG_SELF_CONSCIOUS #define CONFIG_SELF_CONSCIOUS
#define CONFIG_MULTIPLE_TABLES #define CONFIG_MULTIPLE_TABLES
#define CONFIG_ALL_TABLES_AT_ONCE
#define CONFIG_IP6_SADR_KERNEL #define CONFIG_IP6_SADR_KERNEL
#define CONFIG_MC_PROPER_SRC #define CONFIG_MC_PROPER_SRC
@ -18,9 +17,12 @@
#define CONFIG_INCLUDE_SYSIO_H "sysdep/linux/sysio.h" #define CONFIG_INCLUDE_SYSIO_H "sysdep/linux/sysio.h"
#define CONFIG_INCLUDE_KRTSYS_H "sysdep/linux/krt-sys.h" #define CONFIG_INCLUDE_KRTSYS_H "sysdep/linux/krt-sys.h"
#define CONFIG_LINUX_NETLINK
#define CONFIG_RESTRICTED_PRIVILEGES #define CONFIG_RESTRICTED_PRIVILEGES
#define CONFIG_INCLUDE_SYSPRIV_H "sysdep/linux/syspriv.h" #define CONFIG_INCLUDE_SYSPRIV_H "sysdep/linux/syspriv.h"
#define CONFIG_MADV_DONTNEED_TO_FREE
#ifndef AF_MPLS #ifndef AF_MPLS
#define AF_MPLS 28 #define AF_MPLS 28

View file

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

View file

@ -6,7 +6,6 @@
* Can be freely distributed and used under the terms of the GNU GPL. * Can be freely distributed and used under the terms of the GNU GPL.
*/ */
#include <alloca.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
@ -28,10 +27,16 @@
#include "lib/hash.h" #include "lib/hash.h"
#include "conf/conf.h" #include "conf/conf.h"
#ifdef CONFIG_LINUX_NETLINK
#include <asm/types.h> #include <asm/types.h>
#include <linux/if.h> #include <linux/if.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#endif
#ifdef CONFIG_FREEBSD_NETLINK
#include <netlink/netlink.h>
#include <netlink/netlink_route.h>
#endif
#ifdef HAVE_MPLS_KERNEL #ifdef HAVE_MPLS_KERNEL
#include <linux/lwtunnel.h> #include <linux/lwtunnel.h>
@ -74,51 +79,16 @@
#endif #endif
#define krt_ipv4(p) ((p)->af == AF_INET) #define krt_ipv4(p) ((p)->af == AF_INET)
#define krt_ecmp6(p) ((p)->af == AF_INET6)
const int rt_default_ecmp = 16; const int rt_default_ecmp = 16;
/*
* Structure nl_parse_state keeps state of received route processing. Ideally,
* we could just independently parse received Netlink messages and immediately
* propagate received routes to the rest of BIRD, but older Linux kernel (before
* version 4.11) represents and announces IPv6 ECMP routes not as one route with
* multiple next hops (like RTA_MULTIPATH in IPv4 ECMP), but as a sequence of
* routes with the same prefix. More recent kernels work as with IPv4.
*
* Therefore, BIRD keeps currently processed route in nl_parse_state structure
* and postpones its propagation until we expect it to be final; i.e., when
* non-matching route is received or when the scan ends. When another matching
* route is received, it is merged with the already processed route to form an
* ECMP route. Note that merging is done only for IPv6 (merge == 1), but the
* postponing is done in both cases (for simplicity). All IPv4 routes or IPv6
* routes with RTA_MULTIPATH set are just considered non-matching.
*
* This is ignored for asynchronous notifications (every notification is handled
* as a separate route). It is not an issue for our routes, as we ignore such
* notifications anyways. But importing alien IPv6 ECMP routes does not work
* properly with older kernels.
*
* Whatever the kernel version is, IPv6 ECMP routes are sent as multiple routes
* for the same prefix.
*/
struct nl_parse_state struct nl_parse_state
{ {
struct krt_proto *proto;
struct linpool *pool; struct linpool *pool;
int scan; int scan;
int merge;
net *net; u32 rta_flow;
rta *attrs;
struct krt_proto *proto;
s8 new;
s8 krt_src;
u8 krt_type;
u8 krt_proto;
u32 krt_metric;
u32 rta_flow; /* Used during parsing */
}; };
/* /*
@ -161,16 +131,13 @@ nl_open_sock(struct nl_sock *nl)
} }
} }
static void static int
nl_set_strict_dump(struct nl_sock *nl UNUSED, int strict UNUSED) nl_set_strict_dump(struct nl_sock *nl UNUSED, int strict UNUSED)
{ {
/*
* Strict checking is not necessary, it improves behavior on newer kernels.
* If it is not available (missing SOL_NETLINK compile-time, or ENOPROTOOPT
* run-time), we can just ignore it.
*/
#ifdef SOL_NETLINK #ifdef SOL_NETLINK
setsockopt(nl->fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &strict, sizeof(strict)); return setsockopt(nl->fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &strict, sizeof(strict));
#else
return -1;
#endif #endif
} }
@ -198,10 +165,17 @@ nl_cfg_rx_buffer_size(struct config *cfg)
static void static void
nl_open(void) nl_open(void)
{ {
if ((nl_scan.fd >= 0) && (nl_req.fd >= 0))
return;
nl_open_sock(&nl_scan); nl_open_sock(&nl_scan);
nl_open_sock(&nl_req); nl_open_sock(&nl_req);
nl_set_strict_dump(&nl_scan, 1); if (nl_set_strict_dump(&nl_scan, 1) < 0)
{
log(L_WARN "KRT: Netlink strict checking failed, will scan all tables at once");
krt_use_shared_scan();
}
} }
static void static void
@ -256,11 +230,13 @@ nl_request_dump_addr(int af)
} }
static void static void
nl_request_dump_route(int af) nl_request_dump_route(int af, int table_id)
{ {
struct { struct {
struct nlmsghdr nh; struct nlmsghdr nh;
struct rtmsg rtm; struct rtmsg rtm;
struct rtattr rta;
u32 table_id;
} req = { } req = {
.nh.nlmsg_type = RTM_GETROUTE, .nh.nlmsg_type = RTM_GETROUTE,
.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), .nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
@ -269,7 +245,17 @@ nl_request_dump_route(int af)
.rtm.rtm_family = af, .rtm.rtm_family = af,
}; };
send(nl_scan.fd, &req, sizeof(req), 0); if (table_id < 256)
req.rtm.rtm_table = table_id;
else
{
req.rta.rta_type = RTA_TABLE;
req.rta.rta_len = RTA_LENGTH(4);
req.table_id = table_id;
req.nh.nlmsg_len = NLMSG_ALIGN(req.nh.nlmsg_len) + req.rta.rta_len;
}
send(nl_scan.fd, &req, req.nh.nlmsg_len, 0);
nl_scan.last_hdr = NULL; nl_scan.last_hdr = NULL;
} }
@ -831,11 +817,11 @@ nl_parse_multipath(struct nl_parse_state *s, struct krt_proto *p, const net_addr
rv->gw = rta_get_via(a[RTA_VIA]); rv->gw = rta_get_via(a[RTA_VIA]);
#endif #endif
if (nh->rtnh_flags & RTNH_F_ONLINK)
rv->flags |= RNF_ONLINK;
if (ipa_nonzero(rv->gw)) if (ipa_nonzero(rv->gw))
{ {
if (nh->rtnh_flags & RTNH_F_ONLINK)
rv->flags |= RNF_ONLINK;
neighbor *nbr; neighbor *nbr;
nbr = neigh_find(&p->p, rv->gw, rv->iface, nbr = neigh_find(&p->p, rv->gw, rv->iface,
(rv->flags & RNF_ONLINK) ? NEF_ONLINK : 0); (rv->flags & RNF_ONLINK) ? NEF_ONLINK : 0);
@ -1325,7 +1311,7 @@ nh_bufsize(struct nexthop *nh)
} }
static int static int
nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh) nl_send_route(struct krt_proto *p, rte *e, int op)
{ {
eattr *ea; eattr *ea;
net *net = e->net; net *net = e->net;
@ -1397,8 +1383,6 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
if (p->af == AF_MPLS) if (p->af == AF_MPLS)
priority = 0; priority = 0;
else if (a->source == RTS_DUMMY)
priority = e->u.krt.metric;
else if (KRT_CF->sys.metric) else if (KRT_CF->sys.metric)
priority = KRT_CF->sys.metric; priority = KRT_CF->sys.metric;
else if ((op != NL_OP_DELETE) && (ea = ea_find(eattrs, EA_KRT_METRIC))) else if ((op != NL_OP_DELETE) && (ea = ea_find(eattrs, EA_KRT_METRIC)))
@ -1409,15 +1393,17 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
/* For route delete, we do not specify remaining route attributes */ /* For route delete, we do not specify remaining route attributes */
if (op == NL_OP_DELETE) if (op == NL_OP_DELETE)
goto dest; goto done;
/* Default scope is LINK for device routes, UNIVERSE otherwise */ /* Default scope is LINK for device routes, UNIVERSE otherwise */
if (p->af == AF_MPLS) if (p->af == AF_MPLS)
r->r.rtm_scope = RT_SCOPE_UNIVERSE; r->r.rtm_scope = RT_SCOPE_UNIVERSE;
else if (ea = ea_find(eattrs, EA_KRT_SCOPE)) else if (ea = ea_find(eattrs, EA_KRT_SCOPE))
r->r.rtm_scope = ea->u.data; r->r.rtm_scope = ea->u.data;
else if (a->dest == RTD_UNICAST && ipa_zero(a->nh.gw))
r->r.rtm_scope = RT_SCOPE_LINK;
else else
r->r.rtm_scope = (dest == RTD_UNICAST && ipa_zero(nh->gw)) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE; r->r.rtm_scope = RT_SCOPE_UNIVERSE;
if (ea = ea_find(eattrs, EA_KRT_PREFSRC)) if (ea = ea_find(eattrs, EA_KRT_PREFSRC))
nl_add_attr_ipa(&r->h, rsize, RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data); nl_add_attr_ipa(&r->h, rsize, RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
@ -1440,13 +1426,12 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
if (metrics[0]) if (metrics[0])
nl_add_metrics(&r->h, rsize, metrics, KRT_METRICS_MAX); nl_add_metrics(&r->h, rsize, metrics, KRT_METRICS_MAX);
switch (a->dest)
dest:
switch (dest)
{ {
case RTD_UNICAST: case RTD_UNICAST:
r->r.rtm_type = RTN_UNICAST; r->r.rtm_type = RTN_UNICAST;
if (nh->next && !krt_ecmp6(p)) struct nexthop *nh = &(a->nh);
if (nh->next)
nl_add_multipath(&r->h, rsize, nh, p->af, eattrs); nl_add_multipath(&r->h, rsize, nh, p->af, eattrs);
else else
{ {
@ -1472,82 +1457,53 @@ dest:
bug("krt_capable inconsistent with nl_send_route"); bug("krt_capable inconsistent with nl_send_route");
} }
done:
/* Ignore missing for DELETE */ /* Ignore missing for DELETE */
return nl_exchange(&r->h, (op == NL_OP_DELETE)); return nl_exchange(&r->h, (op == NL_OP_DELETE));
} }
static inline int static inline int
nl_add_rte(struct krt_proto *p, rte *e) nl_allow_replace(struct krt_proto *p, rte *new)
{ {
rta *a = e->attrs; /*
int err = 0; * We use NL_OP_REPLACE for IPv4, it has an issue with not checking for
* matching rtm_protocol, but that is OK when dedicated priority is used.
*
* For IPv6, the NL_OP_REPLACE is still broken even in Linux 4.19 LTS
* (although it seems to be fixed in Linux 5.10 LTS) for sequence:
*
* ip route add 2001:db8::/32 via fe80::1 dev eth0
* ip route replace 2001:db8::/32 dev eth0
*
* (it ends with two routes instead of replacing the first by the second one)
*
* Replacing with direct and special type (e.g. unreachable) routes does not
* work, but replacing with regular routes work reliably
*/
if (krt_ecmp6(p) && a->nh.next) if (krt_ipv4(p))
{ return 1;
struct nexthop *nh = &(a->nh);
err = nl_send_route(p, e, NL_OP_ADD, RTD_UNICAST, nh); rta *a = new->attrs;
if (err < 0) return (a->dest == RTD_UNICAST) && ipa_nonzero(a->nh.gw);
return err;
for (nh = nh->next; nh; nh = nh->next)
err += nl_send_route(p, e, NL_OP_APPEND, RTD_UNICAST, nh);
return err;
}
return nl_send_route(p, e, NL_OP_ADD, a->dest, &(a->nh));
} }
static inline int
nl_delete_rte(struct krt_proto *p, rte *e)
{
int err = 0;
/* For IPv6, we just repeatedly request DELETE until we get error */
do
err = nl_send_route(p, e, NL_OP_DELETE, RTD_NONE, NULL);
while (krt_ecmp6(p) && !err);
return err;
}
static inline int
nl_replace_rte(struct krt_proto *p, rte *e)
{
rta *a = e->attrs;
return nl_send_route(p, e, NL_OP_REPLACE, a->dest, &(a->nh));
}
void void
krt_replace_rte(struct krt_proto *p, net *n UNUSED, rte *new, rte *old) krt_replace_rte(struct krt_proto *p, net *n UNUSED, rte *new, rte *old)
{ {
int err = 0; int err = 0;
/* if (old && new && nl_allow_replace(p, new))
* We use NL_OP_REPLACE for IPv4, it has an issue with not checking for
* matching rtm_protocol, but that is OK when dedicated priority is used.
*
* We do not use NL_OP_REPLACE for IPv6, as it has broken semantics for ECMP
* and with some kernel versions ECMP replace crashes kernel. Would need more
* testing and checks for kernel versions.
*
* For IPv6, we use NL_OP_DELETE and then NL_OP_ADD. We also do not trust the
* old route value, so we do not try to optimize IPv6 ECMP reconfigurations.
*/
if (krt_ipv4(p) && old && new)
{ {
err = nl_replace_rte(p, new); err = nl_send_route(p, new, NL_OP_REPLACE);
} }
else else
{ {
if (old) if (old)
nl_delete_rte(p, old); nl_send_route(p, old, NL_OP_DELETE);
if (new) if (new)
err = nl_add_rte(p, new); err = nl_send_route(p, new, NL_OP_ADD);
} }
if (new) if (new)
@ -1559,61 +1515,6 @@ krt_replace_rte(struct krt_proto *p, net *n UNUSED, rte *new, rte *old)
} }
} }
static int
nl_mergable_route(struct nl_parse_state *s, net *net, struct krt_proto *p, uint priority, uint krt_type, uint rtm_family)
{
/* Route merging is used for IPv6 scans */
if (!s->scan || (rtm_family != AF_INET6))
return 0;
/* Saved and new route must have same network, proto/table, and priority */
if ((s->net != net) || (s->proto != p) || (s->krt_metric != priority))
return 0;
/* Both must be regular unicast routes */
if ((s->krt_type != RTN_UNICAST) || (krt_type != RTN_UNICAST))
return 0;
return 1;
}
static void
nl_announce_route(struct nl_parse_state *s)
{
rte *e = rte_get_temp(s->attrs);
e->net = s->net;
e->u.krt.src = s->krt_src;
e->u.krt.proto = s->krt_proto;
e->u.krt.seen = 0;
e->u.krt.best = 0;
e->u.krt.metric = s->krt_metric;
if (s->scan)
krt_got_route(s->proto, e);
else
krt_got_route_async(s->proto, e, s->new);
s->net = NULL;
s->attrs = NULL;
s->proto = NULL;
lp_flush(s->pool);
}
static inline void
nl_parse_begin(struct nl_parse_state *s, int scan)
{
memset(s, 0, sizeof (struct nl_parse_state));
s->pool = nl_linpool;
s->scan = scan;
}
static inline void
nl_parse_end(struct nl_parse_state *s)
{
if (s->net)
nl_announce_route(s);
}
#define SKIP0(ARG, ...) do { DBG("KRT: Ignoring route - " ARG, ##__VA_ARGS__); return; } while(0) #define SKIP0(ARG, ...) do { DBG("KRT: Ignoring route - " ARG, ##__VA_ARGS__); return; } while(0)
#define SKIP(ARG, ...) do { DBG("KRT: Ignoring route %N - " ARG, &dst, ##__VA_ARGS__); return; } while(0) #define SKIP(ARG, ...) do { DBG("KRT: Ignoring route %N - " ARG, &dst, ##__VA_ARGS__); return; } while(0)
@ -1751,14 +1652,29 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
net *net = net_get(p->p.main_channel->table, n); net *net = net_get(p->p.main_channel->table, n);
if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type, i->rtm_family))
nl_announce_route(s);
rta *ra = lp_allocz(s->pool, RTA_MAX_SIZE); rta *ra = lp_allocz(s->pool, RTA_MAX_SIZE);
ra->src = p->p.main_source;
ra->source = RTS_INHERIT; ra->source = RTS_INHERIT;
ra->scope = SCOPE_UNIVERSE; ra->scope = SCOPE_UNIVERSE;
{
ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + 2 * sizeof(eattr));
*ea = (ea_list) { .flags = EALF_SORTED, .count = 2 };
ea->next = ra->eattrs;
ra->eattrs = ea;
ea->attrs[0] = (eattr) {
.id = EA_KRT_SOURCE,
.type = EAF_TYPE_INT,
.u.data = i->rtm_protocol
};
ea->attrs[1] = (eattr) {
.id = EA_KRT_METRIC,
.type = EAF_TYPE_INT,
.u.data = priority,
};
}
if (a[RTA_FLOW]) if (a[RTA_FLOW])
s->rta_flow = rta_get_u32(a[RTA_FLOW]); s->rta_flow = rta_get_u32(a[RTA_FLOW]);
else else
@ -1797,6 +1713,9 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
ra->nh.gw = rta_get_via(a[RTA_VIA]); ra->nh.gw = rta_get_via(a[RTA_VIA]);
#endif #endif
if (i->rtm_flags & RTNH_F_ONLINK)
ra->nh.flags |= RNF_ONLINK;
if (ipa_nonzero(ra->nh.gw)) if (ipa_nonzero(ra->nh.gw))
{ {
/* Silently skip strange 6to4 routes */ /* Silently skip strange 6to4 routes */
@ -1804,9 +1723,6 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
if ((i->rtm_family == AF_INET6) && ipa_in_netX(ra->nh.gw, (net_addr *) &sit)) if ((i->rtm_family == AF_INET6) && ipa_in_netX(ra->nh.gw, (net_addr *) &sit))
return; return;
if (i->rtm_flags & RTNH_F_ONLINK)
ra->nh.flags |= RNF_ONLINK;
neighbor *nbr; neighbor *nbr;
nbr = neigh_find(&p->p, ra->nh.gw, ra->nh.iface, nbr = neigh_find(&p->p, ra->nh.gw, ra->nh.iface,
(ra->nh.flags & RNF_ONLINK) ? NEF_ONLINK : 0); (ra->nh.flags & RNF_ONLINK) ? NEF_ONLINK : 0);
@ -1864,30 +1780,33 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
ra->eattrs = ea; ra->eattrs = ea;
ea->flags = EALF_SORTED; ea->flags = EALF_SORTED;
ea->count = 1; ea->count = 1;
ea->attrs[0].id = EA_KRT_SCOPE; ea->attrs[0] = (eattr) {
ea->attrs[0].flags = 0; .id = EA_KRT_SCOPE,
ea->attrs[0].type = EAF_TYPE_INT; .flags = 0,
ea->attrs[0].u.data = i->rtm_scope; .type = EAF_TYPE_INT,
.u.data = i->rtm_scope,
};
} }
if (a[RTA_PREFSRC]) if (a[RTA_PREFSRC])
{ {
ip_addr ps = rta_get_ipa(a[RTA_PREFSRC]); ip_addr ps = rta_get_ipa(a[RTA_PREFSRC]);
struct adata *ad = lp_alloc(s->pool, sizeof(struct adata) + sizeof(ps));
ad->length = sizeof(ps);
memcpy(ad->data, &ps, sizeof(ps));
ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + sizeof(eattr)); ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + sizeof(eattr));
ea->next = ra->eattrs; ea->next = ra->eattrs;
ra->eattrs = ea; ra->eattrs = ea;
ea->flags = EALF_SORTED; ea->flags = EALF_SORTED;
ea->count = 1; ea->count = 1;
ea->attrs[0].id = EA_KRT_PREFSRC; ea->attrs[0] = (eattr) {
ea->attrs[0].flags = 0; .id = EA_KRT_PREFSRC,
ea->attrs[0].type = EAF_TYPE_IP_ADDRESS; .flags = 0,
.type = EAF_TYPE_IP_ADDRESS,
struct adata *ad = lp_alloc(s->pool, sizeof(struct adata) + sizeof(ps)); .u.ptr = ad,
ad->length = sizeof(ps); };
memcpy(ad->data, &ps, sizeof(ps));
ea->attrs[0].u.ptr = ad;
} }
/* Can be set per-route or per-nexthop */ /* Can be set per-route or per-nexthop */
@ -1898,10 +1817,12 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
ra->eattrs = ea; ra->eattrs = ea;
ea->flags = EALF_SORTED; ea->flags = EALF_SORTED;
ea->count = 1; ea->count = 1;
ea->attrs[0].id = EA_KRT_REALM; ea->attrs[0] = (eattr) {
ea->attrs[0].flags = 0; .id = EA_KRT_REALM,
ea->attrs[0].type = EAF_TYPE_INT; .flags = 0,
ea->attrs[0].u.data = s->rta_flow; .type = EAF_TYPE_INT,
.u.data = s->rta_flow,
};
} }
if (a[RTA_METRICS]) if (a[RTA_METRICS])
@ -1918,13 +1839,12 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
for (t = 1; t < KRT_METRICS_MAX; t++) for (t = 1; t < KRT_METRICS_MAX; t++)
if (metrics[0] & (1 << t)) if (metrics[0] & (1 << t))
{ ea->attrs[n++] = (eattr) {
ea->attrs[n].id = EA_CODE(PROTOCOL_KERNEL, KRT_METRICS_OFFSET + t); .id = EA_CODE(PROTOCOL_KERNEL, KRT_METRICS_OFFSET + t),
ea->attrs[n].flags = 0; .flags = 0,
ea->attrs[n].type = EAF_TYPE_INT; /* FIXME: Some are EAF_TYPE_BITFIELD */ .type = EAF_TYPE_INT, /* FIXME: Some are EAF_TYPE_BITFIELD */
ea->attrs[n].u.data = metrics[t]; .u.data = metrics[t],
n++; };
}
if (n > 0) if (n > 0)
{ {
@ -1935,60 +1855,40 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
} }
} }
/* rte *e = rte_get_temp(ra, p->p.main_source);
* Ideally, now we would send the received route to the rest of kernel code. e->net = net;
* But IPv6 ECMP routes before 4.11 are sent as a sequence of routes, so we
* postpone it and merge next hops until the end of the sequence. Note that
* when doing merging of next hops, we expect the new route to be unipath.
* Otherwise, we ignore additional next hops in nexthop_insert().
*/
if (!s->net) if (s->scan)
{ krt_got_route(p, e, krt_src);
/* Store the new route */
s->net = net;
s->attrs = ra;
s->proto = p;
s->new = new;
s->krt_src = krt_src;
s->krt_type = i->rtm_type;
s->krt_proto = i->rtm_protocol;
s->krt_metric = priority;
}
else else
{ krt_got_route_async(p, e, new, krt_src);
/* Merge next hops with the stored route */
rta *oa = s->attrs;
struct nexthop *nhs = &oa->nh; lp_flush(s->pool);
nexthop_insert(&nhs, &ra->nh);
/* Perhaps new nexthop is inserted at the first position */
if (nhs == &ra->nh)
{
/* Swap rtas */
s->attrs = ra;
/* Keep old eattrs */
ra->eattrs = oa->eattrs;
}
}
} }
void void
krt_do_scan(struct krt_proto *p UNUSED) /* CONFIG_ALL_TABLES_AT_ONCE => p is NULL */ krt_do_scan(struct krt_proto *p)
{ {
struct nlmsghdr *h; struct nl_parse_state s = {
struct nl_parse_state s; .proto = p,
.pool = nl_linpool,
.scan = 1,
};
nl_parse_begin(&s, 1); /* Table-specific scan or shared scan */
nl_request_dump_route(AF_UNSPEC); if (p)
nl_request_dump_route(p->af, krt_table_id(p));
else
nl_request_dump_route(AF_UNSPEC, 0);
struct nlmsghdr *h;
while (h = nl_get_scan()) while (h = nl_get_scan())
{
if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE) if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)
nl_parse_route(&s, h); nl_parse_route(&s, h);
else else
log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type); log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type);
nl_parse_end(&s); }
} }
/* /*
@ -2003,16 +1903,18 @@ static struct config *nl_last_config; /* For tracking changes to nl_async_bufsiz
static void static void
nl_async_msg(struct nlmsghdr *h) nl_async_msg(struct nlmsghdr *h)
{ {
struct nl_parse_state s; struct nl_parse_state s = {
.proto = NULL,
.pool = nl_linpool,
.scan = 0,
};
switch (h->nlmsg_type) switch (h->nlmsg_type)
{ {
case RTM_NEWROUTE: case RTM_NEWROUTE:
case RTM_DELROUTE: case RTM_DELROUTE:
DBG("KRT: Received async route notification (%d)\n", h->nlmsg_type); DBG("KRT: Received async route notification (%d)\n", h->nlmsg_type);
nl_parse_begin(&s, 0);
nl_parse_route(&s, h); nl_parse_route(&s, h);
nl_parse_end(&s);
break; break;
case RTM_NEWLINK: case RTM_NEWLINK:
case RTM_DELLINK: case RTM_DELLINK:

View file

@ -19,111 +19,229 @@
#include <sys/mman.h> #include <sys/mman.h>
#endif #endif
#ifdef HAVE_MMAP long page_size = 0;
#define KEEP_PAGES 512
#ifdef HAVE_MMAP
#define KEEP_PAGES_MAIN_MAX 256
#define KEEP_PAGES_MAIN_MIN 8
#define CLEANUP_PAGES_BULK 256
STATIC_ASSERT(KEEP_PAGES_MAIN_MIN * 4 < KEEP_PAGES_MAIN_MAX);
static u64 page_size = 0;
static _Bool use_fake = 0; static _Bool use_fake = 0;
uint pages_kept = 0; #if DEBUGGING
static list pages_list; struct free_page {
node unused[42];
static void cleanup_pages(void *data); node n;
static event page_cleanup_event = { .hook = cleanup_pages }; };
#else #else
static const u64 page_size = 4096; /* Fake page size */ struct free_page {
node n;
};
#endif #endif
u64 get_page_size(void) #define EP_POS_MAX ((page_size - OFFSETOF(struct empty_pages, pages)) / sizeof (void *))
struct empty_pages {
node n;
uint pos;
void *pages[0];
};
struct free_pages {
list pages; /* List of (struct free_page) keeping free pages without releasing them (hot) */
list empty; /* List of (struct empty_pages) keeping invalidated pages mapped for us (cold) */
u16 min, max; /* Minimal and maximal number of free pages kept */
uint cnt; /* Number of free pages in list */
event cleanup;
};
static void global_free_pages_cleanup_event(void *);
static struct free_pages global_free_pages = {
.min = KEEP_PAGES_MAIN_MIN,
.max = KEEP_PAGES_MAIN_MAX,
.cleanup = { .hook = global_free_pages_cleanup_event },
};
uint *pages_kept = &global_free_pages.cnt;
static void *
alloc_sys_page(void)
{ {
if (page_size) void *ptr = mmap(NULL, page_size, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
return page_size;
#ifdef HAVE_MMAP if (ptr == MAP_FAILED)
if (page_size = sysconf(_SC_PAGESIZE)) bug("mmap(%lu) failed: %m", page_size);
{
if ((u64_popcount(page_size) > 1) || (page_size > 16384))
{
/* Too big or strange page, use the aligned allocator instead */
page_size = 4096;
use_fake = 1;
}
return page_size;
}
bug("Page size must be non-zero"); return ptr;
#endif
} }
extern int shutting_down; /* Shutdown requested. */
#else // ! HAVE_MMAP
#define use_fake 1
#endif
void * void *
alloc_page(void) alloc_page(void)
{ {
#ifdef HAVE_MMAP /* If the system page allocator is goofy, we use posix_memalign to get aligned blocks of memory. */
if (pages_kept) if (use_fake)
{
node *page = TAIL(pages_list);
rem_node(page);
pages_kept--;
memset(page, 0, get_page_size());
return page;
}
if (!use_fake)
{
void *ret = mmap(NULL, get_page_size(), PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ret == MAP_FAILED)
bug("mmap(%lu) failed: %m", (long unsigned int) page_size);
return ret;
}
else
#endif
{ {
void *ptr = NULL; void *ptr = NULL;
int err = posix_memalign(&ptr, page_size, page_size); int err = posix_memalign(&ptr, page_size, page_size);
if (err || !ptr) if (err || !ptr)
bug("posix_memalign(%lu) failed", (long unsigned int) page_size); bug("posix_memalign(%lu) failed", (long unsigned int) page_size);
return ptr; return ptr;
} }
#ifdef HAVE_MMAP
struct free_pages *fps = &global_free_pages;
/* If there is any free page kept hot, we use it. */
if (fps->cnt)
{
struct free_page *fp = SKIP_BACK(struct free_page, n, HEAD(fps->pages));
rem_node(&fp->n);
/* If the hot-free-page cache is getting short, request the cleanup routine to replenish the cache */
if ((--fps->cnt < fps->min) && !shutting_down)
ev_schedule(&fps->cleanup);
return fp;
}
/* If there is any free page kept cold, we use that. */
if (!EMPTY_LIST(fps->empty))
{
struct empty_pages *ep = HEAD(fps->empty);
/* Either the keeper page contains at least one cold page pointer, return that */
if (ep->pos)
return ep->pages[--ep->pos];
/* Or the keeper page has no more cold page pointer, return the keeper page */
rem_node(&ep->n);
return ep;
}
/* And in the worst case, allocate a new page by mmap() */
return alloc_sys_page();
#endif
} }
void void
free_page(void *ptr) free_page(void *ptr)
{ {
#ifdef HAVE_MMAP /* If the system page allocator is goofy, we just free the block and care no more. */
if (!use_fake) if (use_fake)
{ {
if (!pages_kept)
init_list(&pages_list);
memset(ptr, 0, sizeof(node));
add_tail(&pages_list, ptr);
if (++pages_kept > KEEP_PAGES)
ev_schedule(&page_cleanup_event);
}
else
#endif
free(ptr); free(ptr);
return;
}
#ifdef HAVE_MMAP
struct free_pages *fps = &global_free_pages;
struct free_page *fp = ptr;
/* Otherwise, we add the free page to the hot-free-page list */
fp->n = (node) {};
add_tail(&fps->pages, &fp->n);
/* And if there are too many hot free pages, we ask for page cleanup */
if ((++fps->cnt > fps->max) && !shutting_down)
ev_schedule(&fps->cleanup);
#endif
} }
#ifdef HAVE_MMAP #ifdef HAVE_MMAP
static void static void
cleanup_pages(void *data UNUSED) global_free_pages_cleanup_event(void *data UNUSED)
{ {
for (uint seen = 0; (pages_kept > KEEP_PAGES) && (seen < KEEP_PAGES); seen++) /* Cleanup on shutdown is ignored. All pages may be kept hot, OS will take care. */
if (shutting_down)
return;
struct free_pages *fps = &global_free_pages;
/* Cleanup may get called when hot free page cache is short of pages. Replenishing. */
while (fps->cnt / 2 < fps->min)
{ {
void *ptr = HEAD(pages_list); struct free_page *fp = alloc_sys_page();
rem_node(ptr); fp->n = (node) {};
if (munmap(ptr, get_page_size()) == 0) add_tail(&fps->pages, &fp->n);
pages_kept--; fps->cnt++;
else if (errno == ENOMEM)
add_tail(&pages_list, ptr);
else
bug("munmap(%p) failed: %m", ptr);
} }
if (pages_kept > KEEP_PAGES) /* Or the hot free page cache is too big. Moving some pages to the cold free page cache. */
ev_schedule(&page_cleanup_event); for (int limit = CLEANUP_PAGES_BULK; limit && (fps->cnt > fps->max / 2); fps->cnt--, limit--)
{
struct free_page *fp = SKIP_BACK(struct free_page, n, TAIL(fps->pages));
rem_node(&fp->n);
/* Empty pages are stored as pointers. To store them, we need a pointer block. */
struct empty_pages *ep;
if (EMPTY_LIST(fps->empty) || ((ep = HEAD(fps->empty))->pos == EP_POS_MAX))
{
/* There is either no pointer block or the last block is full. We use this block as a pointer block. */
ep = (struct empty_pages *) fp;
*ep = (struct empty_pages) {};
add_head(&fps->empty, &ep->n);
}
else
{
/* We store this block as a pointer into the first free place
* and tell the OS that the underlying memory is trash. */
ep->pages[ep->pos++] = fp;
if (madvise(fp, page_size,
#ifdef CONFIG_MADV_DONTNEED_TO_FREE
MADV_DONTNEED
#else
MADV_FREE
#endif
) < 0)
bug("madvise(%p) failed: %m", fp);
}
}
/* If the hot free page cleanup hit the limit, re-schedule this routine
* to allow for other routines to run. */
if (fps->cnt > fps->max)
ev_schedule(&fps->cleanup);
} }
#endif #endif
void
resource_sys_init(void)
{
#ifdef HAVE_MMAP
ASSERT_DIE(global_free_pages.cnt == 0);
/* Check what page size the system supports */
if (!(page_size = sysconf(_SC_PAGESIZE)))
die("System page size must be non-zero");
if ((u64_popcount(page_size) == 1) && (page_size >= (1 << 10)) && (page_size <= (1 << 18)))
{
/* We assume that page size has only one bit and is between 1K and 256K (incl.).
* Otherwise, the assumptions in lib/slab.c (sl_head's num_full range) aren't met. */
struct free_pages *fps = &global_free_pages;
init_list(&fps->pages);
init_list(&fps->empty);
global_free_pages_cleanup_event(NULL);
return;
}
/* Too big or strange page, use the aligned allocator instead */
log(L_WARN "Got strange memory page size (%ld), using the aligned allocator instead", (s64) page_size);
use_fake = 1;
#endif
page_size = 4096;
}

View file

@ -1854,8 +1854,8 @@ sk_read_ssh(sock *s)
/* sk_read() and sk_write() are called from BFD's event loop */ /* sk_read() and sk_write() are called from BFD's event loop */
int static inline int
sk_read(sock *s, int revents) sk_read_noflush(sock *s, int revents)
{ {
switch (s->type) switch (s->type)
{ {
@ -1918,7 +1918,15 @@ sk_read(sock *s, int revents)
} }
int int
sk_write(sock *s) sk_read(sock *s, int revents)
{
int e = sk_read_noflush(s, revents);
tmp_flush();
return e;
}
static inline int
sk_write_noflush(sock *s)
{ {
switch (s->type) switch (s->type)
{ {
@ -1966,6 +1974,14 @@ sk_write(sock *s)
} }
} }
int
sk_write(sock *s)
{
int e = sk_write_noflush(s);
tmp_flush();
return e;
}
int sk_is_ipv4(sock *s) int sk_is_ipv4(sock *s)
{ return s->af == AF_INET; } { return s->af == AF_INET; }
@ -1984,6 +2000,7 @@ sk_err(sock *s, int revents)
} }
s->err_hook(s, se); s->err_hook(s, se);
tmp_flush();
} }
void void

View file

@ -243,6 +243,13 @@ struct protocol proto_unix_iface = {
.copy_config = kif_copy_config .copy_config = kif_copy_config
}; };
void
kif_build(void)
{
proto_build(&proto_unix_iface);
}
/* /*
* Tracing of routes * Tracing of routes
*/ */
@ -277,22 +284,23 @@ static struct tbf rl_alien = TBF_DEFAULT_LOG_LIMITS;
* the same key. * the same key.
*/ */
static inline u32
krt_metric(rte *a)
{
eattr *ea = ea_find(a->attrs->eattrs, EA_KRT_METRIC);
return ea ? ea->u.data : 0;
}
static inline int static inline int
krt_same_key(rte *a, rte *b) krt_same_key(rte *a, rte *b)
{ {
return a->u.krt.metric == b->u.krt.metric; return (krt_metric(a) == krt_metric(b));
} }
static inline int static inline int
krt_uptodate(rte *a, rte *b) krt_uptodate(rte *a, rte *b)
{ {
if (a->attrs != b->attrs) return (a->attrs == b->attrs);
return 0;
if (a->u.krt.proto != b->u.krt.proto)
return 0;
return 1;
} }
static void static void
@ -300,9 +308,7 @@ krt_learn_announce_update(struct krt_proto *p, rte *e)
{ {
net *n = e->net; net *n = e->net;
rta *aa = rta_clone(e->attrs); rta *aa = rta_clone(e->attrs);
rte *ee = rte_get_temp(aa); rte *ee = rte_get_temp(aa, p->p.main_source);
ee->pflags = EA_ID_FLAG(EA_KRT_SOURCE) | EA_ID_FLAG(EA_KRT_METRIC);
ee->u.krt = e->u.krt;
rte_update(&p->p, n->n.addr, ee); rte_update(&p->p, n->n.addr, ee);
} }
@ -312,6 +318,15 @@ krt_learn_announce_delete(struct krt_proto *p, net *n)
rte_update(&p->p, n->n.addr, NULL); rte_update(&p->p, n->n.addr, NULL);
} }
static void
krt_learn_alien_attr(struct channel *c, rte *e)
{
ASSERT(!e->attrs->cached);
e->attrs->pref = c->preference;
e->attrs = rta_lookup(e->attrs);
}
/* Called when alien route is discovered during scan */ /* Called when alien route is discovered during scan */
static void static void
krt_learn_scan(struct krt_proto *p, rte *e) krt_learn_scan(struct krt_proto *p, rte *e)
@ -320,7 +335,7 @@ krt_learn_scan(struct krt_proto *p, rte *e)
net *n = net_get(p->krt_table, n0->n.addr); net *n = net_get(p->krt_table, n0->n.addr);
rte *m, **mm; rte *m, **mm;
e->attrs = rta_lookup(e->attrs); krt_learn_alien_attr(p->p.main_channel, e);
for(mm=&n->routes; m = *mm; mm=&m->next) for(mm=&n->routes; m = *mm; mm=&m->next)
if (krt_same_key(m, e)) if (krt_same_key(m, e))
@ -331,7 +346,7 @@ krt_learn_scan(struct krt_proto *p, rte *e)
{ {
krt_trace_in_rl(&rl_alien, p, e, "[alien] seen"); krt_trace_in_rl(&rl_alien, p, e, "[alien] seen");
rte_free(e); rte_free(e);
m->u.krt.seen = 1; m->pflags |= KRT_REF_SEEN;
} }
else else
{ {
@ -347,7 +362,7 @@ krt_learn_scan(struct krt_proto *p, rte *e)
{ {
e->next = n->routes; e->next = n->routes;
n->routes = e; n->routes = e;
e->u.krt.seen = 1; e->pflags |= KRT_REF_SEEN;
} }
} }
@ -377,24 +392,23 @@ again:
ee = &n->routes; ee = &n->routes;
while (e = *ee) while (e = *ee)
{ {
if (e->u.krt.best) if (e->pflags & KRT_REF_BEST)
old_best = e; old_best = e;
if (!e->u.krt.seen) if (!(e->pflags & KRT_REF_SEEN))
{ {
*ee = e->next; *ee = e->next;
rte_free(e); rte_free(e);
continue; continue;
} }
if (!best || best->u.krt.metric > e->u.krt.metric) if (!best || krt_metric(best) > krt_metric(e))
{ {
best = e; best = e;
pbest = ee; pbest = ee;
} }
e->u.krt.seen = 0; e->pflags &= ~(KRT_REF_SEEN | KRT_REF_BEST);
e->u.krt.best = 0;
ee = &e->next; ee = &e->next;
} }
if (!n->routes) if (!n->routes)
@ -408,18 +422,18 @@ again:
goto again; goto again;
} }
best->u.krt.best = 1; best->pflags |= KRT_REF_BEST;
*pbest = best->next; *pbest = best->next;
best->next = n->routes; best->next = n->routes;
n->routes = best; n->routes = best;
if ((best != old_best) || p->reload) if ((best != old_best) || p->reload)
{ {
DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric); DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, krt_metric(best));
krt_learn_announce_update(p, best); krt_learn_announce_update(p, best);
} }
else else
DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric); DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, krt_metric(best));
} }
FIB_ITERATE_END; FIB_ITERATE_END;
@ -433,7 +447,7 @@ krt_learn_async(struct krt_proto *p, rte *e, int new)
net *n = net_get(p->krt_table, n0->n.addr); net *n = net_get(p->krt_table, n0->n.addr);
rte *g, **gg, *best, **bestp, *old_best; rte *g, **gg, *best, **bestp, *old_best;
e->attrs = rta_lookup(e->attrs); krt_learn_alien_attr(p->p.main_channel, e);
old_best = n->routes; old_best = n->routes;
for(gg=&n->routes; g = *gg; gg = &g->next) for(gg=&n->routes; g = *gg; gg = &g->next)
@ -476,18 +490,18 @@ krt_learn_async(struct krt_proto *p, rte *e, int new)
bestp = &n->routes; bestp = &n->routes;
for(gg=&n->routes; g=*gg; gg=&g->next) for(gg=&n->routes; g=*gg; gg=&g->next)
{ {
if (best->u.krt.metric > g->u.krt.metric) if (krt_metric(best) > krt_metric(g))
{ {
best = g; best = g;
bestp = gg; bestp = gg;
} }
g->u.krt.best = 0; g->pflags &= ~KRT_REF_BEST;
} }
if (best) if (best)
{ {
best->u.krt.best = 1; best->pflags |= KRT_REF_BEST;
*bestp = best->next; *bestp = best->next;
best->next = n->routes; best->next = n->routes;
n->routes = best; n->routes = best;
@ -528,12 +542,6 @@ krt_dump(struct proto *P)
rt_dump(p->krt_table); rt_dump(p->krt_table);
} }
static void
krt_dump_attrs(rte *e)
{
debug(" [m=%d,p=%d]", e->u.krt.metric, e->u.krt.proto);
}
#endif #endif
/* /*
@ -582,8 +590,6 @@ krt_export_net(struct krt_proto *p, net *net, rte **rt_free)
if (filter == FILTER_REJECT) if (filter == FILTER_REJECT)
return NULL; return NULL;
rte_make_tmp_attrs(&rt, krt_filter_lp, NULL);
/* We could run krt_preexport() here, but it is already handled by krt_is_installed() */ /* We could run krt_preexport() here, but it is already handled by krt_is_installed() */
if (filter == FILTER_ACCEPT) if (filter == FILTER_ACCEPT)
@ -624,13 +630,14 @@ krt_same_dest(rte *k, rte *e)
*/ */
void void
krt_got_route(struct krt_proto *p, rte *e) krt_got_route(struct krt_proto *p, rte *e, s8 src)
{ {
rte *new = NULL, *rt_free = NULL; rte *new = NULL, *rt_free = NULL;
net *n = e->net; net *n = e->net;
e->pflags = 0;
#ifdef KRT_ALLOW_LEARN #ifdef KRT_ALLOW_LEARN
switch (e->u.krt.src) switch (src)
{ {
case KRT_SRC_KERNEL: case KRT_SRC_KERNEL:
goto ignore; goto ignore;
@ -752,11 +759,12 @@ krt_prune(struct krt_proto *p)
} }
void void
krt_got_route_async(struct krt_proto *p, rte *e, int new) krt_got_route_async(struct krt_proto *p, rte *e, int new, s8 src)
{ {
net *net = e->net; net *net = e->net;
e->pflags = 0;
switch (e->u.krt.src) switch (src)
{ {
case KRT_SRC_BIRD: case KRT_SRC_BIRD:
/* Should be filtered by the back end */ /* Should be filtered by the back end */
@ -783,18 +791,17 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new)
rte_free(e); rte_free(e);
} }
/* /*
* Periodic scanning * Periodic scanning
*/ */
static timer *krt_scan_all_timer;
#ifdef CONFIG_ALL_TABLES_AT_ONCE static int krt_scan_all_count;
static _Bool krt_scan_all_tables;
static timer *krt_scan_timer;
static int krt_scan_count;
static void static void
krt_scan(timer *t UNUSED) krt_scan_all(timer *t UNUSED)
{ {
struct krt_proto *p; struct krt_proto *p;
node *n; node *n;
@ -815,35 +822,42 @@ krt_scan(timer *t UNUSED)
} }
static void static void
krt_scan_timer_start(struct krt_proto *p) krt_scan_all_timer_start(struct krt_proto *p)
{ {
if (!krt_scan_count) if (!krt_scan_all_count)
krt_scan_timer = tm_new_init(krt_pool, krt_scan, NULL, KRT_CF->scan_time, 0); krt_scan_all_timer = tm_new_init(krt_pool, krt_scan_all, NULL, KRT_CF->scan_time, 0);
krt_scan_count++; krt_scan_all_count++;
tm_start(krt_scan_timer, 1 S); tm_start(krt_scan_all_timer, 1 S);
} }
static void static void
krt_scan_timer_stop(struct krt_proto *p UNUSED) krt_scan_all_timer_stop(void)
{ {
krt_scan_count--; ASSERT(krt_scan_all_count > 0);
if (!krt_scan_count) krt_scan_all_count--;
if (!krt_scan_all_count)
{ {
rfree(krt_scan_timer); rfree(krt_scan_all_timer);
krt_scan_timer = NULL; krt_scan_all_timer = NULL;
} }
} }
static void static void
krt_scan_timer_kick(struct krt_proto *p UNUSED) krt_scan_all_timer_kick(void)
{ {
tm_start(krt_scan_timer, 0); tm_start(krt_scan_all_timer, 0);
}
void
krt_use_shared_scan(void)
{
krt_scan_all_tables = 1;
} }
#else
static void static void
krt_scan(timer *t) krt_scan(timer *t)
@ -861,54 +875,43 @@ krt_scan(timer *t)
static void static void
krt_scan_timer_start(struct krt_proto *p) krt_scan_timer_start(struct krt_proto *p)
{ {
p->scan_timer = tm_new_init(p->p.pool, krt_scan, p, KRT_CF->scan_time, 0); if (krt_scan_all_tables)
tm_start(p->scan_timer, 1 S); krt_scan_all_timer_start(p);
else
{
p->scan_timer = tm_new_init(p->p.pool, krt_scan, p, KRT_CF->scan_time, 0);
tm_start(p->scan_timer, 1 S);
}
} }
static void static void
krt_scan_timer_stop(struct krt_proto *p) krt_scan_timer_stop(struct krt_proto *p)
{ {
tm_stop(p->scan_timer); if (krt_scan_all_tables)
krt_scan_all_timer_stop();
else
tm_stop(p->scan_timer);
} }
static void static void
krt_scan_timer_kick(struct krt_proto *p) krt_scan_timer_kick(struct krt_proto *p)
{ {
tm_start(p->scan_timer, 0); if (krt_scan_all_tables)
krt_scan_all_timer_kick();
else
tm_start(p->scan_timer, 0);
} }
#endif
/* /*
* Updates * Updates
*/ */
static void
krt_make_tmp_attrs(struct rte *rt, struct linpool *pool)
{
rte_init_tmp_attrs(rt, pool, 2);
rte_make_tmp_attr(rt, EA_KRT_SOURCE, EAF_TYPE_INT, rt->u.krt.proto);
rte_make_tmp_attr(rt, EA_KRT_METRIC, EAF_TYPE_INT, rt->u.krt.metric);
}
static void
krt_store_tmp_attrs(struct rte *rt, struct linpool *pool)
{
rte_init_tmp_attrs(rt, pool, 2);
rt->u.krt.proto = rte_store_tmp_attr(rt, EA_KRT_SOURCE);
rt->u.krt.metric = rte_store_tmp_attr(rt, EA_KRT_METRIC);
}
static int static int
krt_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED) krt_preexport(struct channel *C, rte *e)
{ {
// struct krt_proto *p = (struct krt_proto *) P; // struct krt_proto *p = (struct krt_proto *) P;
rte *e = *new; if (e->src->proto == C->proto)
if (e->attrs->src->proto == P)
return -1; return -1;
if (!krt_capable(e)) if (!krt_capable(e))
@ -934,7 +937,7 @@ krt_rt_notify(struct proto *P, struct channel *ch UNUSED, net *net,
* kernel, which would remove the new imported route instead. * kernel, which would remove the new imported route instead.
*/ */
rte *best = net->routes; rte *best = net->routes;
if (!new && best && (best->attrs->src->proto == P)) if (!new && best && (best->src->proto == P))
return; return;
#endif #endif
@ -983,14 +986,6 @@ krt_feed_end(struct channel *C)
} }
static int
krt_rte_same(rte *a, rte *b)
{
/* src is always KRT_SRC_ALIEN and type is irrelevant */
return (a->u.krt.proto == b->u.krt.proto) && (a->u.krt.metric == b->u.krt.metric);
}
/* /*
* Protocol glue * Protocol glue
*/ */
@ -1016,11 +1011,6 @@ krt_postconfig(struct proto_config *CF)
if (! proto_cf_main_channel(CF)) if (! proto_cf_main_channel(CF))
cf_error("Channel not specified"); cf_error("Channel not specified");
#ifdef CONFIG_ALL_TABLES_AT_ONCE
if (krt_cf->scan_time != cf->scan_time)
cf_error("All kernel syncers must use the same table scan interval");
#endif
struct channel_config *cc = proto_cf_main_channel(CF); struct channel_config *cc = proto_cf_main_channel(CF);
struct rtable_config *tab = cc->table; struct rtable_config *tab = cc->table;
if (tab->krt_attached) if (tab->krt_attached)
@ -1049,9 +1039,6 @@ krt_init(struct proto_config *CF)
p->p.if_notify = krt_if_notify; p->p.if_notify = krt_if_notify;
p->p.reload_routes = krt_reload_routes; p->p.reload_routes = krt_reload_routes;
p->p.feed_end = krt_feed_end; p->p.feed_end = krt_feed_end;
p->p.make_tmp_attrs = krt_make_tmp_attrs;
p->p.store_tmp_attrs = krt_store_tmp_attrs;
p->p.rte_same = krt_rte_same;
krt_sys_init(p); krt_sys_init(p);
return &p->p; return &p->p;
@ -1209,6 +1196,11 @@ struct protocol proto_unix_kernel = {
.get_attr = krt_get_attr, .get_attr = krt_get_attr,
#ifdef KRT_ALLOW_LEARN #ifdef KRT_ALLOW_LEARN
.dump = krt_dump, .dump = krt_dump,
.dump_attrs = krt_dump_attrs,
#endif #endif
}; };
void
krt_build(void)
{
proto_build(&proto_unix_kernel);
}

View file

@ -24,6 +24,9 @@ struct kif_proto;
#define EA_KRT_SOURCE EA_CODE(PROTOCOL_KERNEL, 0) #define EA_KRT_SOURCE EA_CODE(PROTOCOL_KERNEL, 0)
#define EA_KRT_METRIC EA_CODE(PROTOCOL_KERNEL, 1) #define EA_KRT_METRIC EA_CODE(PROTOCOL_KERNEL, 1)
#define KRT_REF_SEEN 0x1 /* Seen in table */
#define KRT_REF_BEST 0x2 /* Best in table */
/* Whenever we recognize our own routes, we allow learing of foreign routes */ /* Whenever we recognize our own routes, we allow learing of foreign routes */
#ifdef CONFIG_SELF_CONSCIOUS #ifdef CONFIG_SELF_CONSCIOUS
@ -52,10 +55,7 @@ struct krt_proto {
struct rtable *krt_table; /* Internal table of inherited routes */ struct rtable *krt_table; /* Internal table of inherited routes */
#endif #endif
#ifndef CONFIG_ALL_TABLES_AT_ONCE
timer *scan_timer; timer *scan_timer;
#endif
struct bmap sync_map; /* Keeps track which exported routes were successfully written to kernel */ struct bmap sync_map; /* Keeps track which exported routes were successfully written to kernel */
struct bmap seen_map; /* Routes seen during last periodic scan */ struct bmap seen_map; /* Routes seen during last periodic scan */
node krt_node; /* Node in krt_proto_list */ node krt_node; /* Node in krt_proto_list */
@ -76,8 +76,9 @@ extern pool *krt_pool;
struct proto_config * kif_init_config(int class); struct proto_config * kif_init_config(int class);
void kif_request_scan(void); void kif_request_scan(void);
void krt_got_route(struct krt_proto *p, struct rte *e); void krt_use_shared_scan(void);
void krt_got_route_async(struct krt_proto *p, struct rte *e, int new); void krt_got_route(struct krt_proto *p, struct rte *e, s8 src);
void krt_got_route_async(struct krt_proto *p, struct rte *e, int new, s8 src);
static inline int static inline int
krt_get_sync_error(struct krt_proto *p, struct rte *e) krt_get_sync_error(struct krt_proto *p, struct rte *e)

View file

@ -309,22 +309,15 @@ die(const char *msg, ...)
void void
debug(const char *msg, ...) debug(const char *msg, ...)
{ {
#define MAX_DEBUG_BUFSIZE 65536 #define MAX_DEBUG_BUFSIZE 16384
va_list args; va_list args;
static uint bufsize = 4096; char buf[MAX_DEBUG_BUFSIZE];
static char *buf = NULL;
if (!buf)
buf = mb_alloc(&root_pool, bufsize);
va_start(args, msg); va_start(args, msg);
if (dbgf) if (dbgf)
{ {
while (bvsnprintf(buf, bufsize, msg, args) < 0) if (bvsnprintf(buf, MAX_DEBUG_BUFSIZE, msg, args) < 0)
if (bufsize >= MAX_DEBUG_BUFSIZE) bug("Extremely long debug output, split it.");
bug("Extremely long debug output, split it.");
else
buf = mb_realloc(buf, (bufsize *= 2));
fputs(buf, dbgf); fputs(buf, dbgf);
} }

View file

@ -116,7 +116,7 @@ add_num_const(char *name, int val, const char *file, const uint line)
struct f_val *v = cfg_alloc(sizeof(struct f_val)); struct f_val *v = cfg_alloc(sizeof(struct f_val));
*v = (struct f_val) { .type = T_INT, .val.i = val }; *v = (struct f_val) { .type = T_INT, .val.i = val };
struct symbol *sym = cf_get_symbol(name); struct symbol *sym = cf_get_symbol(name);
if (sym->class && (sym->scope == conf_this_scope)) if (sym->class && cf_symbol_is_local(sym))
cf_error("Error reading value for %s from %s:%d: already defined", name, file, line); cf_error("Error reading value for %s from %s:%d: already defined", name, file, line);
cf_define_symbol(sym, SYM_CONSTANT | T_INT, val, v); cf_define_symbol(sym, SYM_CONSTANT | T_INT, val, v);
@ -242,6 +242,8 @@ async_config(void)
{ {
struct config *conf; struct config *conf;
config_free_old();
log(L_INFO "Reconfiguration requested by SIGHUP"); log(L_INFO "Reconfiguration requested by SIGHUP");
if (!unix_read_config(&conf, config_name)) if (!unix_read_config(&conf, config_name))
{ {
@ -280,6 +282,9 @@ cmd_read_config(const char *name)
void void
cmd_check_config(const char *name) cmd_check_config(const char *name)
{ {
if (cli_access_restricted())
return;
struct config *conf = cmd_read_config(name); struct config *conf = cmd_read_config(name);
if (!conf) if (!conf)
return; return;
@ -324,6 +329,8 @@ cmd_reconfig(const char *name, int type, uint timeout)
if (cli_access_restricted()) if (cli_access_restricted())
return; return;
config_free_old();
struct config *conf = cmd_read_config(name); struct config *conf = cmd_read_config(name);
if (!conf) if (!conf)
return; return;
@ -434,7 +441,7 @@ int
cli_get_command(cli *c) cli_get_command(cli *c)
{ {
sock *s = c->priv; sock *s = c->priv;
byte *t = c->rx_aux ? : s->rbuf; byte *t = s->rbuf;
byte *tend = s->rpos; byte *tend = s->rpos;
byte *d = c->rx_pos; byte *d = c->rx_pos;
byte *dend = c->rx_buf + CLI_RX_BUF_SIZE - 2; byte *dend = c->rx_buf + CLI_RX_BUF_SIZE - 2;
@ -445,16 +452,22 @@ cli_get_command(cli *c)
t++; t++;
else if (*t == '\n') else if (*t == '\n')
{ {
t++;
c->rx_pos = c->rx_buf;
c->rx_aux = t;
*d = 0; *d = 0;
t++;
/* Move remaining data and reset pointers */
uint rest = (t < tend) ? (tend - t) : 0;
memmove(s->rbuf, t, rest);
s->rpos = s->rbuf + rest;
c->rx_pos = c->rx_buf;
return (d < dend) ? 1 : -1; return (d < dend) ? 1 : -1;
} }
else if (d < dend) else if (d < dend)
*d++ = *t++; *d++ = *t++;
} }
c->rx_aux = s->rpos = s->rbuf;
s->rpos = s->rbuf;
c->rx_pos = d; c->rx_pos = d;
return 0; return 0;
} }
@ -479,6 +492,14 @@ cli_err(sock *s, int err)
cli_free(s->data); cli_free(s->data);
} }
static void
cli_connect_err(sock *s UNUSED, int err)
{
ASSERT_DIE(err);
if (config->cli_debug)
log(L_INFO "Failed to accept CLI connection: %s", strerror(err));
}
static int static int
cli_connect(sock *s, uint size UNUSED) cli_connect(sock *s, uint size UNUSED)
{ {
@ -493,7 +514,6 @@ cli_connect(sock *s, uint size UNUSED)
s->pool = c->pool; /* We need to have all the socket buffers allocated in the cli pool */ s->pool = c->pool; /* We need to have all the socket buffers allocated in the cli pool */
s->fast_rx = 1; s->fast_rx = 1;
c->rx_pos = c->rx_buf; c->rx_pos = c->rx_buf;
c->rx_aux = NULL;
rmove(s, c->pool); rmove(s, c->pool);
return 1; return 1;
} }
@ -507,6 +527,7 @@ cli_init_unix(uid_t use_uid, gid_t use_gid)
s = cli_sk = sk_new(cli_pool); s = cli_sk = sk_new(cli_pool);
s->type = SK_UNIX_PASSIVE; s->type = SK_UNIX_PASSIVE;
s->rx_hook = cli_connect; s->rx_hook = cli_connect;
s->err_hook = cli_connect_err;
s->rbsize = 1024; s->rbsize = 1024;
s->fast_rx = 1; s->fast_rx = 1;
@ -897,8 +918,6 @@ main(int argc, char **argv)
open_pid_file(); open_pid_file();
protos_build(); protos_build();
proto_build(&proto_unix_kernel);
proto_build(&proto_unix_iface);
struct config *conf = read_config(); struct config *conf = read_config();

View file

@ -20,6 +20,7 @@
#include "test/birdtest.h" #include "test/birdtest.h"
#include "lib/string.h" #include "lib/string.h"
#include "lib/event.h"
#ifdef HAVE_EXECINFO_H #ifdef HAVE_EXECINFO_H
#include <execinfo.h> #include <execinfo.h>
@ -119,6 +120,9 @@ bt_init(int argc, char *argv[])
clock_gettime(CLOCK_MONOTONIC, &bt_begin); clock_gettime(CLOCK_MONOTONIC, &bt_begin);
bt_suite_case_begin = bt_suite_begin = bt_begin; bt_suite_case_begin = bt_suite_begin = bt_begin;
resource_init();
ev_init_list(&global_event_list);
return; return;
usage: usage:
@ -172,6 +176,8 @@ int bt_run_test_fn(int (*fn)(const void *), const void *fn_arg, int timeout)
if (!bt_suite_result) if (!bt_suite_result)
result = 0; result = 0;
tmp_flush();
return result; return result;
} }

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