Compare commits

...

137 commits

Author SHA1 Message Date
JerryXiao 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/"
IMG_BASE: registry.nic.cz/labs/bird
TOOLS_DIR: /var/lib/gitlab-runner/bird-tools
STAYRTR_BINARY: /var/lib/gitlab-runner/stayrtr-0.1-108-g8d18a41-linux-x86_64
stages:
- image
# - image
- build
- pkg
- test
@ -32,185 +33,12 @@ stages:
# That's Docker in Docker
- dind
docker_debian-8-amd64:
variables:
IMG_NAME: "debian-8-amd64"
<<: *docker_build
docker_debian-8-i386:
variables:
IMG_NAME: "debian-8-i386"
<<: *docker_build
docker_debian-9-amd64:
variables:
IMG_NAME: "debian-9-amd64"
<<: *docker_build
docker_debian-9-i386:
variables:
IMG_NAME: "debian-9-i386"
<<: *docker_build
docker_debian-10-amd64:
variables:
IMG_NAME: "debian-10-amd64"
<<: *docker_build
docker_debian-10-i386:
variables:
IMG_NAME: "debian-10-i386"
<<: *docker_build
docker_debian-11-amd64:
variables:
IMG_NAME: "debian-11-amd64"
<<: *docker_build
# GPG error
#docker_debian-11-i386:
# Docker build example
#docker_debian-11-amd64:
# variables:
# IMG_NAME: "debian-11-i386"
# IMG_NAME: "debian-11-amd64"
# <<: *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
stage: build
@ -416,13 +244,11 @@ build-opensuse-15.3-amd64:
# needs: [build-debian-8-i386]
# image: registry.nic.cz/labs/bird:debian-8-i386
# Dpkg error: PATH is not set
pkg-debian-9-amd64:
<<: *pkg-deb
needs: [build-debian-9-amd64]
image: registry.nic.cz/labs/bird:debian-9-amd64
# Dpkg error: PATH is not set
pkg-debian-9-i386:
<<: *pkg-deb
needs: [build-debian-9-i386]
@ -531,6 +357,7 @@ build-birdlab:
- sudo git clean -fx
- git pull --ff-only
- mv $DIR/bird $DIR/birdc netlab/common
- ln -s $STAYRTR_BINARY netlab/common/stayrtr
.test: &test-base
stage: test
@ -541,7 +368,7 @@ build-birdlab:
script:
- cd $TOOLS_DIR/netlab
- sudo ./stop
- sudo ./runtest -m check $TEST_NAME
- sudo ./runtest -s v2 -m check $TEST_NAME
test-ospf-base:
<<: *test-base
@ -613,6 +440,16 @@ test-bgp-merged:
variables:
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-base
variables:
@ -623,12 +460,32 @@ test-ebgp-star:
variables:
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-base
variables:
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
variables:
TEST_NAME: cf-ibgp-flat
@ -647,3 +504,49 @@ test-rip-base:
<<: *test-base
variables:
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@)
daemon=$(exedir)/bird
protocols=@protocols@
PROTO_BUILD := $(protocols) dev kif krt
prefix=@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)
o BGP performance improvements
o BFD: New 'strict bind' option

View file

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

View file

@ -574,6 +574,8 @@ check_eof(void)
return 0;
}
static inline void cf_swap_soft_scope(void);
static struct symbol *
cf_new_symbol(const byte *c)
{
@ -583,6 +585,8 @@ cf_new_symbol(const byte *c)
if (l > SYM_MAX_LEN)
cf_error("Symbol too long");
cf_swap_soft_scope();
s = cfg_allocz(sizeof(struct symbol) + l + 1);
*s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, };
strcpy(s->name, c);
@ -654,10 +658,10 @@ cf_localize_symbol(struct symbol *sym)
/* If the symbol type is void, it has been recently allocated just in this scope. */
if (!sym->class)
return sym;
/* If the scope is the current, it is already defined in this scope. */
if (sym->scope == conf_this_scope)
cf_error("Symbol already defined");
if (cf_symbol_is_local(sym))
cf_error("Symbol '%s' already defined", sym->name);
/* Not allocated here yet, doing it now. */
return cf_new_symbol(sym->name);
@ -713,7 +717,7 @@ cf_lex_symbol(const char *data)
static 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;
for (k=keyword_list; k->name; k++)
@ -787,12 +791,60 @@ cf_push_scope(struct symbol *sym)
void
cf_pop_scope(void)
{
ASSERT(!conf_this_scope->soft_scopes);
conf_this_scope->active = 0;
conf_this_scope = conf_this_scope->next;
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
* @sym: symbol

View file

@ -61,6 +61,7 @@
static jmp_buf conf_jmpbuf;
struct config *config, *new_config;
pool *config_pool;
static struct config *old_config; /* Old configuration */
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 *
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);
struct config *c = lp_allocz(l, sizeof(struct config));
@ -200,6 +201,23 @@ config_free(struct config *c)
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
config_add_obstacle(struct config *c)
{
@ -491,10 +509,12 @@ config_timeout(timer *t UNUSED)
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_timer = tm_new(&root_pool);
config_timer = tm_new(config_pool);
config_timer->hook = config_timeout;
}

View file

@ -70,6 +70,7 @@ struct config *config_alloc(const char *name);
int config_parse(struct config *);
int cli_parse(struct config *);
void config_free(struct config *);
void config_free_old(void);
int config_commit(struct config *, int type, uint timeout);
int config_confirm(void);
int config_undo(void);
@ -96,7 +97,7 @@ void order_shutdown(int gr);
/* Pools */
extern pool *config_pool;
extern linpool *cfg_mem;
#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 symbol *name; /* Name of this scope */
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 {
@ -190,6 +193,9 @@ struct symbol *cf_get_symbol(const byte *c);
struct symbol *cf_default_name(char *template, int *counter);
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
* @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_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);
/* Parser */

View file

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

View file

@ -180,7 +180,7 @@ flow6_opts:
flow_builder_init:
{
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
flow_builder_clear(this_flow);
};

View file

@ -238,6 +238,8 @@ else
;;
openbsd*)
sysdesc=bsd
CPPFLAGS="$CPPFLAGS -I/usr/local/include"
LDFLAGS="$LDFLAGS -L/usr/local/lib"
;;
dragonfly*)
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
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
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
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
a given filter (<cf>filter <m/name/</cf> or <cf>filter { <m/filter/ }
</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
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
restart affected protocols, therefore already accepted routes (according
to old filters) would be still propagated, but new routes would be
@ -1260,8 +1270,8 @@ this:
<code>
filter not_too_far
int var;
{
int var;
if defined( rip_metric ) then
var = rip_metric;
else {
@ -1290,9 +1300,9 @@ local variables. Recursion is not allowed. Function definitions look like this:
<code>
function name ()
int local_variable;
{
local_variable = 5;
int local_variable;
int another_variable = 5;
}
function with_parameters (int parameter)
@ -1301,16 +1311,19 @@ function with_parameters (int parameter)
}
</code>
<p>Unlike in C, variables are declared after the <cf/function/ line, but before
the first <cf/{/. You can't declare variables in nested blocks. 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>Like in C programming language, variables are declared inside function body,
either at the beginning, or mixed with other statements. Declarations may
contain initialization. You can also declare variables in nested blocks, such
variables have scope restricted to such block. There is a deprecated syntax to
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
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.
<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
<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/;
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
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
<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
@ -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:
<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 {
2: print "two"; print "I can do more commands without {}";
3 .. 5: print "three to five";
else: print "something else";
}
if 1234 = i then printn "."; else {
print "not 1234";
print "You need {} around multiple commands";
}
</code>
@ -1762,7 +1789,7 @@ Common route attributes are:
<tag><label id="rta-source"><m/enum/ source</tag>
what protocol has told me about this route. Possible values:
<cf/RTS_DUMMY/, <cf/RTS_STATIC/, <cf/RTS_INHERIT/, <cf/RTS_DEVICE/,
<cf/RTS_STATIC/, <cf/RTS_INHERIT/, <cf/RTS_DEVICE/,
<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/.
@ -1880,8 +1907,9 @@ protocol babel [<name>] {
ipv4 { <channel config> };
ipv6 [sadr] { <channel config> };
randomize router id <switch>;
metric decay <time>;
interface <interface pattern> {
type <wired|wireless>;
type <wired|wireless|tunnel>;
rxcost <number>;
limit <number>;
hello interval <time>;
@ -1894,6 +1922,11 @@ protocol babel [<name>] {
check link <switch>;
next hop ipv4 <address>;
next hop ipv6 <address>;
rtt cost <number>;
rtt min <time>;
rtt max <time>;
rtt winlen <number>;
send timestamps <switch>;
authentication none|mac [permissive];
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
of not having stable router IDs in the network. Default: no.
<tag><label id="babel-type">type wired|wireless </tag>
This option specifies the interface type: Wired or wireless. 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
<tag><label id="babel-metric-decay">metric decay <m/time/ s</tag>
The metric smoothing decay time. When route metrics vary (because of
varying quality of a wireless link, or varying RTT when timestamps are
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
the metrics of routes discovered over this interface. This technique will
gradually degrade the metric of routes when packets are lost rather than
the more binary up/down mechanism of wired type links. Default:
<cf/wired/.
the more binary up/down mechanism of wired type links. A tunnel is like a
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>
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
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>
Selects authentication method to be used. <cf/none/ means that packets
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="8212"> - Default EBGP Route Propagation Behavior without Policies
<item> <rfc id="9117"> - Revised Validation Procedure for BGP Flow Specifications
<item> <rfc id="9234"> - Route Leak Prevention and Detection Using Roles
</itemize>
<sect1>Route selection rules
@ -2722,9 +2799,16 @@ using the following configuration parameters:
<tag><label id="bgp-hold-time">hold time <m/number/</tag>
Time in seconds to wait for a Keepalive message from the other side
before considering the connection stale. Default: depends on agreement
with the neighboring router, we prefer 240 seconds if the other side is
willing to accept it.
before considering the connection stale. The effective value is
negotiated during session establishment and it is a minimum of this
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>
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>
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.
<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>
Delay in seconds between protocol startup and the first attempt to
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
therefore does not have such attribute). Default: 100 (0 in pre-1.2.0
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>
<sect1>Channel configuration
@ -2870,6 +2984,20 @@ be used in explicit configuration.
BGP session (if acceptable), or the preferred address of an associated
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>
For received routes, their <cf/gw/ (immediate next hop) attribute is
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
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>
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
@ -3107,6 +3242,11 @@ some of them (marked with `<tt/O/') are optional.
This attribute contains accumulated IGP metric, which is a total
distance to the destination through multiple autonomous systems.
Currently, the attribute is not accessible from filters.
<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>
<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) \
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
* 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);
getter = f_new_inst(FI_VAR_GET, lval->sym);
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:
setter = f_new_inst(FI_RTA_SET, expr, lval->sa);
getter = f_new_inst(FI_RTA_GET, lval->sa);
@ -266,7 +292,7 @@ assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const
checker = f_new_inst(FI_EQ, expr, getter);
setter->next = checker;
return assert_done(setter, start, end);
}
@ -277,6 +303,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
IF, THEN, ELSE, CASE,
FOR, IN, DO,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
PREFERENCE,
@ -296,21 +323,23 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%nonassoc ELSE
%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 <fsa> static_attr
%type <f> filter where_filter
%type <fl> filter_body function_body
%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 <fret> break_command
%type <fret> break_command
%type <i32> cnum
%type <e> pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body
%type <trie> fprefix_set
%type <v> set_atom switch_atom fipa
%type <px> fprefix
%type <t> get_cf_position
%type <s> for_var
CF_GRAMMAR
@ -328,7 +357,7 @@ filter_def:
conf: filter_eval ;
filter_eval:
EVAL term { f_eval_int(f_linearize($2)); }
EVAL term { f_eval_int(f_linearize($2, 1)); }
;
conf: custom_attr ;
@ -403,25 +432,28 @@ type:
;
function_argsn:
/* EMPTY */
/* EMPTY */ { $$ = NULL; }
| function_argsn type symbol ';' {
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:
'(' ')' { $$ = 0; }
'(' ')' { $$ = NULL; }
| '(' function_argsn type symbol ')' {
cf_define_symbol($4, SYM_VARIABLE | $3, offset, $4->scope->slots++);
$$ = $4->scope->slots;
$$ = cfg_alloc(sizeof(struct f_arg));
$$->arg = cf_define_symbol($4, SYM_VARIABLE | $3, offset, sym_->scope->slots++);
$$->next = $2;
}
;
function_vars:
/* EMPTY */ { $$ = 0; }
| 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;
}
;
@ -433,10 +465,12 @@ filter:
cf_assert_symbol($1, SYM_FILTER);
$$ = $1->filter;
}
| filter_body {
| { cf_push_scope(NULL); } filter_body {
struct filter *f = cfg_alloc(sizeof(struct filter));
*f = (struct filter) { .root = $1 };
*f = (struct filter) { .root = $2 };
$$ = f;
cf_pop_scope();
}
;
@ -449,20 +483,35 @@ where_filter:
function_body:
function_vars '{' cmds '}' {
$$ = f_linearize($3);
$$ = f_linearize($3, 0);
$$->vars = $1;
}
;
conf: 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);
cf_push_scope($2);
} function_args function_body {
DBG("Definition of function %s with %u args and %u local vars.\n", $2->name, $4, $5->vars);
$5->args = $4;
$2->function = $5;
} function_args {
/* Make dummy f_line for storing function prototype */
struct f_line *dummy = cfg_allocz(sizeof(struct f_line));
$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();
}
;
@ -473,7 +522,11 @@ cmds: /* EMPTY */ { $$ = NULL; }
| 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;
if ($1)
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
*/
@ -527,7 +571,7 @@ set_atom:
| VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
| '(' 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");
}
| CF_SYM_KNOWN {
@ -539,13 +583,13 @@ set_atom:
switch_atom:
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; }
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
;
cnum:
term { $$ = f_eval_int(f_linearize($1)); }
term { $$ = f_eval_int(f_linearize($1, 1)); }
pair_item:
'(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); }
@ -629,19 +673,19 @@ fprefix_set:
;
switch_body: /* EMPTY */ { $$ = NULL; }
| switch_body switch_items ':' cmds {
| switch_body switch_items ':' cmds_scoped {
/* Fill data fields */
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)
t->data = line;
$$ = f_merge_items($1, $2);
}
| switch_body ELSECOL cmds {
| switch_body ELSECOL cmds_scoped {
struct f_tree *t = f_new_tree();
t->from.type = t->to.type = T_VOID;
t->right = t;
t->data = f_linearize($3);
t->data = f_linearize($3, 0);
$$ = f_merge_items($1, t);
}
;
@ -658,6 +702,7 @@ bgp_path:
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 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 {
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;
@ -677,6 +722,7 @@ constant:
| fipa { $$ = f_new_inst(FI_CONSTANT, $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, }); }
| '[' ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = NULL, }); }
| '[' set_items ']' {
DBG( "We've got a set here..." );
$$ = 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; }
function_call:
CF_SYM_KNOWN '(' var_list ')' {
CF_SYM_KNOWN '(' var_list ')'
{
if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really.");
struct f_inst *fc = f_new_inst(FI_CALL, $1);
uint args = 0;
/* Revert the var_list */
struct f_inst *args = NULL;
while ($3) {
args++;
struct f_inst *tmp = $3->next;
$3->next = fc;
struct f_inst *tmp = $3;
$3 = $3->next;
fc = $3;
$3 = tmp;
tmp->next = args;
args = tmp;
}
if (args != $1->function->args)
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;
$$ = f_new_inst(FI_CALL, args, $1);
}
;
@ -753,6 +794,7 @@ static_attr:
| IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
| IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); }
| WEIGHT { $$ = f_new_static_attr(T_INT, SA_WEIGHT, 0); }
| PREFERENCE { $$ = f_new_static_attr(T_INT, SA_PREF, 0); }
| GW_MPLS { $$ = f_new_static_attr(T_INT, SA_GW_MPLS, 0); }
;
@ -779,8 +821,6 @@ term:
| constant { $$ = $1; }
| constructor { $$ = $1; }
| PREFERENCE { $$ = f_new_inst(FI_PREF_GET); }
| static_attr { $$ = f_new_inst(FI_RTA_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:
IF term THEN block {
'{' cmds_scoped '}' {
$$ = $2;
}
| IF term THEN cmd {
$$ = 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);
}
| 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 ';' {
switch ($1->class) {
case SYM_VARIABLE_RANGE:
@ -878,9 +949,6 @@ cmd:
cf_error( "This static attribute is read-only.");
$$ = f_new_inst(FI_RTA_SET, $3, $1);
}
| PREFERENCE '=' term ';' {
$$ = f_new_inst(FI_PREF_SET, $3);
}
| UNSET '(' dynamic_attr ')' ';' {
$$ = f_new_inst(FI_EA_UNSET, $3);
}
@ -902,7 +970,7 @@ cmd:
| PRINTN print_list ';' {
$$ = f_new_inst(FI_PRINT, $2);
}
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
| CASE term '{' switch_body '}' {
$$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
}
@ -923,7 +991,6 @@ get_cf_position:
lvalue:
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 }; }
| 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)
{
switch(t) {
case T_PATH: return T_INT;
case T_CLIST: return T_PAIR;
case T_ECLIST: return T_EC;
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 = {
.type = T_PATH,
.val.ad = &null_adata,
@ -91,6 +94,9 @@ const struct f_val f_const_empty_path = {
}, f_const_empty_lclist = {
.type = T_LCLIST,
.val.ad = &null_adata,
}, f_const_empty_prefix_set = {
.type = T_PREFIX_SET,
.val.ti = &f_const_empty_trie,
};
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))
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;
}
@ -301,6 +307,12 @@ val_same(const struct f_val *v1, const struct f_val *v2)
int
clist_set_type(const struct f_tree *set, struct f_val *v)
{
if (!set)
{
v->type = T_VOID;
return 1;
}
switch (set->from.type)
{
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)
{
if (!clist)
@ -345,7 +357,7 @@ clist_match_set(const struct adata *clist, const struct f_tree *set)
return 0;
}
static int
int
eclist_match_set(const struct adata *list, const struct f_tree *set)
{
if (!list)
@ -369,7 +381,7 @@ eclist_match_set(const struct adata *list, const struct f_tree *set)
return 0;
}
static int
int
lclist_match_set(const struct adata *list, const struct f_tree *set)
{
if (!list)
@ -537,6 +549,9 @@ val_in_range(const struct f_val *v1, const struct f_val *v2)
if (v2->type != T_SET)
return F_CMP_ERROR;
if (!v2->val.t)
return 0;
/* With integrated Quad<->IP implicit conversion */
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))))

View file

@ -100,6 +100,7 @@ enum f_sa_code {
SA_IFNAME,
SA_IFINDEX,
SA_WEIGHT,
SA_PREF,
SA_GW_MPLS,
} PACKED;
@ -191,6 +192,7 @@ struct f_tree *f_new_tree(void);
struct f_tree *build_tree(struct f_tree *);
const struct f_tree *find_tree(const struct f_tree *t, const struct f_val *val);
int same_tree(const struct f_tree *t0, const struct f_tree *t2);
int tree_node_count(const struct f_tree *t);
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);
@ -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);
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)
{ 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 *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);
}
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);

View file

@ -32,6 +32,7 @@ m4_divert(-1)m4_dnl
#
# 101 content of per-inst struct
# 102 constructor arguments
# 110 constructor attributes
# 103 constructor body
# 104 dump line item content
# (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.
m4_define(FID_STRUCT_IN, `m4_divert(101)')
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_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])')
m4_define(FID_LINEARIZE_BODY, `m4_divert(105)')
@ -106,15 +108,18 @@ FID_STRUCT_IN()m4_dnl
struct f_inst * f$1;
FID_NEW_ARGS()m4_dnl
, struct f_inst * f$1
FID_NEW_ATTRIBUTES()m4_dnl
NONNULL(m4_eval($1+1))
FID_NEW_BODY()m4_dnl
whati->f$1 = f$1;
for (const struct f_inst *child = f$1; child; child = child->next) {
what->size += child->size;
const struct f_inst *child$1 = f$1;
do {
what->size += child$1->size;
FID_IFCONST([[
if (child->fi_code != FI_CONSTANT)
if (child$1->fi_code != FI_CONSTANT)
constargs = 0;
]])
}
} while (child$1 = child$1->next);
FID_LINEARIZE_BODY
pos = linearize(dest, whati->f$1, pos);
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));
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
# that was needed in the former implementation.
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
f_dump_line(item->fl$1, indent + 1);
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
if (!f_same(f1->fl$1, f2->fl$1)) return 0;
FID_ITERATE_BODY()m4_dnl
@ -238,9 +249,13 @@ m4_define(ERROR,
# This macro specifies result type and makes there are no conflicting definitions
m4_define(RESULT_TYPE,
`m4_ifdef([[INST_RESULT_TYPE]],
[[m4_ifelse(INST_RESULT_TYPE,$1,,[[ERROR([[Multiple type definitons]])]])]],
[[m4_ifelse(INST_RESULT_TYPE,$1,,[[ERROR([[Multiple type definitions in]] INST_NAME)]])]],
[[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_, `
FID_NEW_BODY()m4_dnl
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.
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 { ... }
INST_NAME(),
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_HIC(
[[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_dnl The one case in The Big Switch inside interpreter
@ -321,7 +339,9 @@ m4_undivert(102)m4_dnl
break;
]],
[[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
)
{
@ -365,6 +385,7 @@ case INST_NAME(): {
#undef whati
#undef item
dest->items[pos].fi_code = what->fi_code;
dest->items[pos].flags = what->flags;
dest->items[pos].lineno = what->lineno;
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
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_OUTVAL]], [[$3]])m4_dnl instruction output value count,
m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl reset NEVER_CONSTANT trigger,
m4_undefine([[INST_RESULT_TYPE]])m4_dnl and reset RESULT_TYPE value.
FID_INTERPRET_BODY()m4_dnl By default, every code is interpreter code.
@ -495,6 +517,11 @@ f_const_promotion(struct f_inst *arg, enum f_type want)
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;
}
@ -550,7 +577,7 @@ FID_WR_PUT(8)
}
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;
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++)
out->len = linearize(out, inst[i], out->len);
out->results = results;
#ifdef LOCAL_DEBUG
f_dump_line(out, 0);
#endif
@ -630,6 +659,7 @@ FID_WR_PUT(4)m4_dnl
struct f_inst {
struct f_inst *next; /* Next instruction */
enum f_instruction_code fi_code; /* Instruction code */
enum f_instruction_flags flags; /* Flags, instruction-specific */
enum f_type type; /* Type of returned value, if known */
int size; /* How many instructions are underneath */
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 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_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 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 STATIC_ATTR; static attribute definition
* m4_dnl DYNAMIC_ATTR; dynamic attribute definition
@ -80,10 +81,17 @@
* m4_dnl )
*
* 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_TYPE(type); just declare the type of result value
* m4_dnl RESULT_VOID; return undef
* 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.
* 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
@ -91,6 +99,24 @@
*
* 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
* 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 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 */
@ -246,7 +303,7 @@
RESULT_TYPE(T_BOOL);
if (v1.val.i)
LINE(2,0);
LINE(2,1);
else
RESULT_VAL(v1);
}
@ -256,7 +313,7 @@
RESULT_TYPE(T_BOOL);
if (!v1.val.i)
LINE(2,0);
LINE(2,1);
else
RESULT_VAL(v1);
}
@ -349,7 +406,7 @@
break;
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");
pm->item[i] = (struct f_path_mask_item) {
@ -371,12 +428,14 @@
INST(FI_NEQ, 2, 1) {
ARG_ANY(1);
ARG_ANY(2);
ARG_PREFER_SAME_TYPE(1, 2);
RESULT(T_BOOL, i, !val_same(&v1, &v2));
}
INST(FI_EQ, 2, 1) {
ARG_ANY(1);
ARG_ANY(2);
ARG_PREFER_SAME_TYPE(1, 2);
RESULT(T_BOOL, i, val_same(&v1, &v2));
}
@ -447,6 +506,18 @@
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 */
INST(FI_VAR_SET, 1, 0) {
NEVER_CONSTANT;
@ -477,12 +548,100 @@
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) {
ARG(1, T_BOOL);
if (v1.val.i)
LINE(2,0);
else
LINE(3,1);
LINE(3,0);
}
INST(FI_PRINT, 0, 0) {
@ -526,13 +685,14 @@
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_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_SCOPE: RESULT(sa.f_type, i, rta->scope); 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_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_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;
default:
@ -561,7 +721,7 @@
{
ip_addr ip = v1.val.ip;
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))
runtime( "Invalid gw address" );
@ -637,6 +797,10 @@
}
break;
case SA_PREF:
rta->pref = v1.val.i;
break;
default:
bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
}
@ -709,9 +873,6 @@
case EAF_TYPE_LC_SET:
RESULT_(T_LCLIST, ad, e->u.ptr);
break;
case EAF_TYPE_UNDEF:
RESULT_VOID;
break;
default:
bug("Unknown dynamic attribute type");
}
@ -732,7 +893,10 @@
l->count = 1;
l->attrs[0].id = da.ea_code;
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) {
case EAF_TYPE_INT:
@ -787,35 +951,8 @@
ACCESS_RTE;
ACCESS_EATTRS;
{
struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr));
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;
f_rta_cow(fs);
ea_unset_attr(fs->eattrs, fs->pool, 1, da.ea_code);
}
INST(FI_LENGTH, 1, 1) { /* Get length of */
@ -983,7 +1120,7 @@
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);
RESULT_TYPE(f_type_element_type(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);
RESULT_TYPE(f_type_element_type(v1.type));
switch(v1.type)
@ -1051,7 +1188,7 @@
}
}
INST(FI_RETURN, 1, 1) {
INST(FI_RETURN, 1, 0) {
NEVER_CONSTANT;
/* Acquire the return value */
ARG_ANY(1);
@ -1079,28 +1216,59 @@
INST(FI_CALL, 0, 1) {
NEVER_CONSTANT;
VARARG;
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()
if (!(f1->sym->flags & SYM_FLAG_SAME))
return 0;
if (!(f1->sym->flags & SYM_FLAG_SAME) && !(f1_->flags & FIF_RECURSIVE))
return 0;
FID_ITERATE_BODY()
if (!(what->flags & FIF_RECURSIVE))
BUFFER_PUSH(fit->lines) = whati->sym->function;
FID_INTERPRET_BODY()
/* Push the body on stack */
LINEX(sym->function);
curline.vbase = curline.ventry;
curline.emask |= FE_RETURN;
/* Before this instruction was called, there was the T_VOID
* automatic return value pushed on value stack and also
* 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;
/* Arguments on stack */
fstk->vcnt += sym->function->args;
/* Storage for local variables */
memset(&(fstk->vstk[fstk->vcnt]), 0, sizeof(struct f_val) * sym->function->vars);
@ -1213,17 +1381,10 @@
if (v1.type == T_PATH)
{
const struct f_tree *set = NULL;
u32 key = 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;
if ((v2.type == T_SET) && path_set_type(v2.val.t) || (v2.type == T_INT))
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
else
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)
@ -1275,10 +1436,8 @@
if (v1.type == T_PATH)
{
u32 key = 0;
if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT))
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, v2.val.t, key, 1) ]]);
if ((v2.type == T_SET) && path_set_type(v2.val.t))
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter integer");
}
@ -1368,7 +1527,7 @@
}
INST(FI_FORMAT, 1, 0) { /* Format */
INST(FI_FORMAT, 1, 1) { /* Format */
ARG_ANY(1);
RESULT(T_STRING, s, val_format_str(fpool, &v1));
}

View file

@ -22,7 +22,7 @@
/* Flags for instructions */
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;
/* 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)
{ return f_instruction_name_(fi) + 3; }
struct f_arg {
struct symbol *arg;
struct f_arg *next;
};
/* Filter structures for execution */
/* Line of instructions to be unconditionally executed one after another */
struct f_line {
uint len; /* Line length */
u8 args; /* Function: Args required */
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 */
};
/* 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);
static inline struct f_line *f_linearize(const struct f_inst *root)
{ return f_linearize_concat(&root, 1); }
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, uint results)
{ return f_linearize_concat(&root, 1, results); }
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));
struct filter *f = cfg_allocz(sizeof(struct filter));
f->root = f_linearize(cond);
f->root = f_linearize(cond, 0);
return f;
}
@ -128,11 +128,11 @@ ca_lookup(pool *p, const char *name, int f_type)
static int inited = 0;
if (!inited) {
idm_init(&ca_idm, &root_pool, 8);
HASH_INIT(ca_hash, &root_pool, CA_ORDER);
idm_init(&ca_idm, config_pool, 8);
HASH_INIT(ca_hash, config_pool, CA_ORDER);
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++;
}
@ -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);
}
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->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. */
fstk->vcnt -= curline.line->vars;
fstk->vcnt -= curline.line->args;
fstk->vcnt = curline.ventry + curline.line->results;
fstk->ecnt--;
}

View file

@ -46,9 +46,7 @@ run_function(const void *arg)
if (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, NULL);
rfree(tmp);
enum filter_return fret = f_eval(t->fn, tmp_linpool, NULL);
return (fret < F_REJECT);
}

View file

@ -44,9 +44,8 @@ bt_test_same(onef, twof, 0);
*/
function t_bool()
bool b;
{
b = true;
bool b = true;
bt_assert(b);
bt_assert(!!b);
@ -82,12 +81,11 @@ define xyzzy = (120+10);
define '1a-a1' = (xyzzy-100);
function t_int()
int i;
{
bt_assert(xyzzy = 130);
bt_assert('1a-a1' = 30);
i = four;
int i = four;
i = 12*100 + 60/2 + i;
i = (i + 0);
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];
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(5 ~ [1..20]);
bt_assert(2 ~ [ 1, 2, 3 ]);
bt_assert(5 ~ [ 4 .. 7 ]);
bt_assert(1 !~ [ 2, 3, 4 ]);
bt_assert(999 !~ [ 666, 333 ]);
bt_assert(1 !~ []);
bt_assert(1 !~ is);
is = [ 2, 3, 4, 7..11 ];
bt_assert(10 ~ is);
@ -170,6 +173,7 @@ int set is;
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([]) = "[]");
}
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()
string st;
{
st = "Hello";
string st = "Hello";
bt_assert(format(st) = "Hello");
bt_assert(st ~ "Hell*");
bt_assert(st ~ "?ello");
@ -210,9 +213,8 @@ function 'mkpair-a'(int a)
}
function t_pair()
pair pp;
{
pp = (1, 2);
pair pp = (1, 2);
bt_assert(format(pp) = "(1,2)");
bt_assert((1,2) = pp);
bt_assert((1,1+1) = pp);
@ -233,10 +235,11 @@ bt_test_suite(t_pair, "Testing pairs");
*/
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)];
bt_assert(format(ps) = "[(1,2), (3,4)..(4,8), (5,0)..(5,65535), (6,3)..(6,6)]");
bt_assert(pp ~ ps);
@ -253,6 +256,7 @@ pair set ps;
bt_assert((6,6+one) !~ ps);
bt_assert(((one+6),2) !~ ps);
bt_assert((1,1) !~ ps);
bt_assert(pp !~ []);
ps = [(20..150, 200..300), (50100..50200, 1000..50000), (*, 5+5)];
bt_assert((100,200) ~ ps);
@ -304,6 +308,7 @@ quad qq;
qq = 1.2.3.4;
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 !~ []);
}
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 !~ []);
}
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()
{
bt_assert(format(RTS_DUMMY) = "(enum 30)0");
bt_assert(format(RTS_STATIC) = "(enum 30)1");
bt_assert(format(NET_IP4) = "(enum 36)1");
bt_assert(format(NET_VPN6) = "(enum 36)4");
@ -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/9 !~ [ 1.0.0.0/8- ]);
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 } ]);
}
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()
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} ];
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()
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(12::34 = 12::34);
bt_assert(12::34 ~ [ 12::33..12::35 ]);
@ -681,6 +713,7 @@ int set set12;
bt_assert(3 ~ p2);
bt_assert(p2 ~ [2, 10..20]);
bt_assert(p2 ~ [4, 10..20]);
bt_assert(p2 !~ []);
p2 = prepend(p2, 5);
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 set35 3 set12 set12 =]);
bt_assert(p2 ~ mkpath(5, 4));
bt_assert(p2 ~ [= * [3] * =]);
bt_assert(p2 !~ [= * [] * =]);
bt_assert(p2.len = 5);
bt_assert(p2.first = 5);
@ -699,6 +734,10 @@ int set set12;
bt_assert(p2.len = 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(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( 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(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");
@ -759,6 +807,7 @@ clist r;
bt_assert(l ~ [(2,2..3)]);
bt_assert(l ~ [(1,1..2)]);
bt_assert(l ~ [(1,1)..(1,2)]);
bt_assert(l !~ []);
l = add(l, (2,5));
l = add(l, (5,one));
@ -796,6 +845,9 @@ clist r;
bt_assert(l !~ [(*,(one+6))]);
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, [(*,(4+one))]);
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(r.min = (1,3));
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");
@ -913,11 +971,15 @@ eclist r;
bt_assert((ro, 10.20.30.40, 100) !~ el);
bt_assert(el !~ [(rt, 10, 35..40)]);
bt_assert(el !~ [(ro, 10, *)]);
bt_assert(el !~ []);
el = add(el, (rt, 10, 40));
el2 = filter(el, [(rt, 10, 20..40)] );
el2 = add(el2, (rt, 10, 50));
bt_assert(delete(el, []) = el);
bt_assert(filter(el, []) = --empty--);
# eclist A (1,30,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))");
@ -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(r.min = (rt, 1, 3));
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");
@ -1035,6 +1104,9 @@ lclist r;
ll2 = add(ll2, (30, 30, 30));
ll2 = add(ll2, (40, 40, 40));
bt_assert(delete(ll, []) = ll);
bt_assert(filter(ll, []) = ---empty---);
# lclist A (10, 20, 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(r.min = (1, 2, 3));
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");
@ -1094,6 +1179,7 @@ lc set lls;
bt_assert(ll !~ [(5,10,15), (10,21,30)]);
bt_assert(ll !~ [(10,21..25,*)]);
bt_assert(ll !~ [(11, *, *)]);
bt_assert(ll !~ []);
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)]");
@ -1150,6 +1236,10 @@ bt_test_suite(t_rd, "Testing route distinguishers");
function t_rd_set()
rd set rds;
{
rds = [];
bt_assert(rds = []);
bt_assert(10:20 !~ rds);
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:200 ~ rds);
bt_assert(100010:150 !~ rds);
bt_assert(100010:150 !~ []);
}
bt_test_suite(t_rd_set, "Testing sets of route distinguishers");
@ -1226,7 +1317,85 @@ function fifteen()
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()
bgppath h_src;
{
bt_assert(fifteen() = 15);
@ -1238,6 +1407,17 @@ function t_call_function()
bt_assert(callme(4, 4) = 16);
bt_assert(callme(7, 2) = 14);
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");
@ -1434,13 +1614,16 @@ filter vpn_filter
bt_assert(net.type != NET_IP6);
bt_assert(net.rd = 0:1:2);
bool b = false;
case (net.type) {
NET_IP4: print "IPV4";
NET_IP6: print "IPV6";
else: b = true;
}
bt_assert(b);
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_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;
}
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
tree_node_format(const struct f_tree *t, buffer *buf)

View file

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

View file

@ -249,14 +249,14 @@ get_outer_net(net_addr *net, const struct f_prefix *src)
}
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);
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);
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 *
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;
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.
*/
static list *
read_prefix_list(linpool *lp, FILE *f, int v6, int plus)
read_prefix_list(FILE *f, int v6, int plus)
{
ASSERT(!v6);
@ -294,7 +294,7 @@ read_prefix_list(linpool *lp, FILE *f, int v6, int plus)
char s[32];
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);
errno = 0;
@ -308,7 +308,7 @@ read_prefix_list(linpool *lp, FILE *f, int v6, int plus)
if (n != 5)
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);
px->prefix.lo = 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
read_prefix_file(const char *filename, int plus,
linpool *lp0, linpool *lp1,
list *data[], struct f_trie *trie[])
{
FILE *f = fopen(filename, "r");
@ -339,10 +338,10 @@ read_prefix_file(const char *filename, int plus,
int n = 0;
list *pxlist;
while (pxlist = read_prefix_list(lp0, f, 0, plus))
while (pxlist = read_prefix_list(f, 0, plus))
{
data[n] = pxlist;
trie[n] = make_trie_from_prefix_list(lp1, pxlist);
trie[n] = make_trie_from_prefix_list(pxlist);
bt_debug("NEXT\n");
n++;
}
@ -437,11 +436,10 @@ t_match_random_net(void)
bt_config_parse(BT_CONFIG_SIMPLE);
int v6 = 0;
linpool *lp = lp_new_default(&root_pool);
for (int round = 0; round < TESTS_NUM; round++)
{
list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6, 0);
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
list *prefixes = make_random_prefix_list(PREFIXES_NUM, v6, 0);
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
{
@ -451,7 +449,7 @@ t_match_random_net(void)
}
v6 = !v6;
lp_flush(lp);
tmp_flush();
}
bt_bird_cleanup();
@ -465,11 +463,10 @@ t_match_inner_net(void)
bt_config_parse(BT_CONFIG_SIMPLE);
int v6 = 0;
linpool *lp = lp_new_default(&root_pool);
for (int round = 0; round < TESTS_NUM; round++)
{
list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6, 0);
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
list *prefixes = make_random_prefix_list(PREFIXES_NUM, v6, 0);
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
struct f_prefix_node *n = HEAD(*prefixes);
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
@ -482,7 +479,7 @@ t_match_inner_net(void)
}
v6 = !v6;
lp_flush(lp);
tmp_flush();
}
bt_bird_cleanup();
@ -496,11 +493,10 @@ t_match_outer_net(void)
bt_config_parse(BT_CONFIG_SIMPLE);
int v6 = 0;
linpool *lp = lp_new_default(&root_pool);
for (int round = 0; round < TESTS_NUM; round++)
{
list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6, 0);
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
list *prefixes = make_random_prefix_list(PREFIXES_NUM, v6, 0);
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
struct f_prefix_node *n = HEAD(*prefixes);
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
@ -513,7 +509,7 @@ t_match_outer_net(void)
}
v6 = !v6;
lp_flush(lp);
tmp_flush();
}
v6 = !v6;
@ -531,24 +527,22 @@ static int
benchmark_trie_dataset(const char *filename, int plus)
{
int n = 0;
linpool *lp0 = lp_new_default(&root_pool);
linpool *lp1 = lp_new_default(&root_pool);
list *data[TRIE_BUFFER_SIZE];
struct f_trie *trie[TRIE_BUFFER_SIZE];
net_addr *nets;
bt_reset_suite_case_timer();
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);
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",
(uint) (trie_size / 1000), (uint) (trie_size % 1000));
int t = PREFIX_BENCH_NUM / n;
int tb = MIN(t, TEST_BUFFER_SIZE);
nets = lp_alloc(lp0, tb * sizeof(net_addr));
nets = tmp_alloc(tb * sizeof(net_addr));
if (!plus)
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);
rfree(lp0);
rfree(lp1);
tmp_flush();
return 1;
}
@ -621,12 +613,11 @@ t_trie_same(void)
bt_config_parse(BT_CONFIG_SIMPLE);
int v6 = 0;
linpool *lp = lp_new_default(&root_pool);
for (int round = 0; round < TESTS_NUM*4; round++)
{
list *prefixes = make_random_prefix_list(lp, 100 * PREFIXES_NUM, v6, 0);
struct f_trie *trie1 = f_new_trie(lp, 0);
struct f_trie *trie2 = f_new_trie(lp, 0);
list *prefixes = make_random_prefix_list(100 * PREFIXES_NUM, v6, 0);
struct f_trie *trie1 = f_new_trie(tmp_linpool, 0);
struct f_trie *trie2 = f_new_trie(tmp_linpool, 0);
struct f_prefix_node *n;
WALK_LIST(n, *prefixes)
@ -638,7 +629,7 @@ t_trie_same(void)
bt_assert(trie_same(trie1, trie2));
v6 = !v6;
lp_flush(lp);
tmp_flush();
}
bt_bird_cleanup();
@ -664,15 +655,14 @@ t_trie_walk(void)
bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE);
linpool *lp = lp_new_default(&root_pool);
for (int round = 0; round < TESTS_NUM*8; round++)
{
int level = round / TESTS_NUM;
int v6 = level % 2;
int num = PREFIXES_NUM * (int[]){1, 10, 100, 1000}[level / 2];
int pos = 0, end = 0;
list *prefixes = make_random_prefix_list(lp, num, v6, 1);
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
list *prefixes = make_random_prefix_list(num, v6, 1);
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
struct f_prefix *pxset = malloc((num + 1) * sizeof(struct f_prefix));
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_debug("Subnet walk done for %s (found %d nets)\n", buf0, pos - p0);
lp_flush(lp);
tmp_flush();
}
bt_bird_cleanup();
@ -815,7 +805,6 @@ t_trie_walk_to_root(void)
bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE);
linpool *lp = lp_new_default(&root_pool);
for (int round = 0; round < TESTS_NUM * 4; round++)
{
int level = round / TESTS_NUM;
@ -824,8 +813,8 @@ t_trie_walk_to_root(void)
int pos = 0;
int st = 0, sn = 0, sm = 0;
list *prefixes = make_random_prefix_list(lp, num, v6, 1);
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
list *prefixes = make_random_prefix_list(num, v6, 1);
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
struct f_prefix *pxset = malloc((num + 1) * sizeof(struct f_prefix));
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);
lp_flush(lp);
tmp_flush();
}
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)
$(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_objs := $(tests_objs) $(src-o-files)

View file

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

View file

@ -41,7 +41,6 @@ fill_expected_array(void)
static void
init_buffer(void)
{
resource_init();
buffer_pool = &root_pool;
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);
ev_run(e);
tmp_flush();
}
return !EMPTY_LIST(*l);
@ -184,6 +185,7 @@ ev_run_list_limited(event_list *l, uint limit)
io_log_event(e->hook, e->data);
ev_run(e);
tmp_flush();
limit--;
}

View file

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

View file

@ -446,10 +446,7 @@ t_validation6(void)
static int
t_builder4(void)
{
resource_init();
struct flow_builder *fb = flow_builder_init(&root_pool);
linpool *lp = lp_new_default(&root_pool);
/* Expectation */
@ -492,7 +489,7 @@ t_builder4(void)
flow_builder_set_type(fb, FLOW_TYPE_TCP_FLAGS);
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);
@ -529,8 +526,6 @@ t_builder6(void)
{
net_addr_ip6 ip;
resource_init();
linpool *lp = lp_new_default(&root_pool);
struct flow_builder *fb = flow_builder_init(&root_pool);
fb->ipv6 = 1;
@ -574,7 +569,7 @@ t_builder6(void)
flow_builder_set_type(fb, FLOW_TYPE_LABEL);
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);
/* Reverse order */
@ -601,7 +596,7 @@ t_builder6(void)
flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
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);
return 1;

View file

@ -215,6 +215,12 @@ mem_hash_mix(u64 *h, const void *p, uint s)
*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
mem_hash_value(u64 *h)
{

View file

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

View file

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

View file

@ -27,21 +27,22 @@
struct lp_chunk {
struct lp_chunk *next;
uint size;
uintptr_t data_align[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 {
resource r;
byte *ptr, *end;
struct lp_chunk *first, *current; /* Normal (reusable) 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_dump(resource *);
static resource *lp_lookup(resource *, unsigned long);
@ -59,19 +60,14 @@ static struct resclass lp_class = {
/**
* lp_new - create a new linear memory pool
* @p: pool
* @blk: block size
*
* 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
* @blk.
* The linear pool consists of a list of memory chunks of page size.
*/
linpool
*lp_new(pool *p, uint blk)
*lp_new(pool *p)
{
linpool *m = ralloc(p, &lp_class);
m->chunk_size = blk;
m->threshold = 3*blk/4;
return m;
return ralloc(p, &lp_class);
}
/**
@ -102,14 +98,13 @@ lp_alloc(linpool *m, uint size)
else
{
struct lp_chunk *c;
if (size >= m->threshold)
if (size > LP_DATA_SIZE)
{
/* Too large => allocate large chunk */
c = xmalloc(sizeof(struct lp_chunk) + size);
m->total_large += size;
c->next = m->first_large;
m->first_large = c;
c->size = size;
}
else
{
@ -121,10 +116,10 @@ lp_alloc(linpool *m, uint size)
else
{
/* Need to allocate a new chunk */
c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size);
m->total += m->chunk_size;
c = alloc_page();
m->total += LP_DATA_SIZE;
c->next = NULL;
c->size = m->chunk_size;
if (m->current)
m->current->next = c;
@ -133,7 +128,7 @@ lp_alloc(linpool *m, uint size)
}
m->current = c;
m->ptr = c->data + size;
m->end = c->data + m->chunk_size;
m->end = c->data + LP_DATA_SIZE;
}
return c->data;
}
@ -195,7 +190,7 @@ lp_flush(linpool *m)
/* Move ptr to the first chunk and free all large chunks */
m->current = c = m->first;
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)
{
@ -218,6 +213,7 @@ lp_save(linpool *m, lp_state *p)
{
p->current = m->current;
p->large = m->first_large;
p->total_large = m->total_large;
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 */
m->current = c = p->current;
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))
{
m->first_large = c->next;
m->total_large -= c->size;
xfree(c);
}
}
@ -258,7 +254,7 @@ lp_free(resource *r)
for(d=m->first; d; d = c)
{
c = d->next;
xfree(d);
free_page(d);
}
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++)
;
debug("(chunk=%d threshold=%d count=%d+%d total=%d+%d)\n",
m->chunk_size,
m->threshold,
debug("(count=%d+%d total=%d+%d)\n",
cnt,
cntl,
m->total,
@ -291,19 +285,22 @@ static struct resmem
lp_memsize(resource *r)
{
linpool *m = (linpool *) r;
struct lp_chunk *c;
int cnt = 0;
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)),
struct resmem sz = {
.overhead = sizeof(struct linpool) + ALLOC_OVERHEAD,
.effective = m->total_large,
};
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;
for(c=m->first; c; c=c->next)
if ((unsigned long) c->data <= a && (unsigned long) c->data + c->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)
if ((unsigned long) c->data <= a && (unsigned long) c->data + LP_DATA_SIZE > a)
return r;
return NULL;
}

View file

@ -568,3 +568,51 @@ buffer_puts(buffer *buf, const char *str)
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;
}
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
pool_free(resource *P)
{
@ -270,9 +284,12 @@ rlookup(unsigned long a)
void
resource_init(void)
{
resource_sys_init();
root_pool.r.class = &pool_class;
root_pool.name = "Root";
init_list(&root_pool.inside);
tmp_init(&root_pool);
}
/**
@ -407,21 +424,6 @@ mb_realloc(void *m, unsigned size)
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

View file

@ -44,6 +44,7 @@ typedef struct pool pool;
void resource_init(void);
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 rdump(void *); /* Dump to debug output */
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_allocz(pool *, unsigned size);
void *mb_realloc(void *m, unsigned size);
void mb_move(void *, pool *);
void mb_free(void *);
/* Memory pools with linear allocation */
@ -69,9 +69,10 @@ typedef struct linpool linpool;
typedef struct lp_state {
void *current, *large;
byte *ptr;
uint total_large;
} lp_state;
linpool *lp_new(pool *, unsigned blk);
linpool *lp_new(pool *);
void *lp_alloc(linpool *, unsigned size); /* Aligned */
void *lp_allocu(linpool *, unsigned size); /* Unaligned */
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_restore(linpool *m, lp_state *p); /* Restore state */
extern const int lp_chunk_size;
#define LP_GAS 1024
#define LP_GOOD_SIZE(x) (((x + LP_GAS - 1) & (~(LP_GAS - 1))) - lp_chunk_size)
#define lp_new_default(p) lp_new(p, LP_GOOD_SIZE(LP_GAS*4))
extern _Thread_local linpool *tmp_linpool; /* Temporary linpool autoflushed regularily */
#define tmp_alloc(sz) lp_alloc(tmp_linpool, sz)
#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 */
@ -91,7 +98,7 @@ typedef struct slab slab;
slab *sl_new(pool *, unsigned size);
void *sl_alloc(slab *);
void *sl_allocz(slab *);
void sl_free(slab *, void *);
void sl_free(void *);
/*
* 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);
/* 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 free_page(void *);
extern uint pages_kept;
void resource_sys_init(void);
#ifdef HAVE_LIBDMALLOC
/*

View file

@ -32,6 +32,7 @@
#include "nest/bird.h"
#include "lib/resource.h"
#include "lib/string.h"
#include "lib/tlists.h"
#undef FAKE_SLAB /* Turn on if you want to debug memory allocations */
@ -98,7 +99,7 @@ sl_allocz(slab *s)
}
void
sl_free(slab *s, void *oo)
sl_free(void *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
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 {
resource r;
uint obj_size, head_size, head_bitfield_len;
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 = {
@ -169,18 +197,15 @@ static struct resclass sl_class = {
slab_memsize
};
struct sl_head {
node n;
u32 num_full;
u32 used_bits[0];
};
#define SL_GET_HEAD(x) ((struct sl_head *) (((uintptr_t) (x)) & ~(page_size-1)))
struct sl_alignment { /* Magic structure for testing of alignment */
byte data;
int x[0];
};
#define SL_HEAD_CHANGE_STATE(_s, _h, _from, _to) ({ \
ASSERT_DIE(_h->state == slh_##_from); \
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
@ -202,7 +227,6 @@ sl_new(pool *p, uint size)
s->obj_size = size;
s->head_size = sizeof(struct sl_head);
u64 page_size = get_page_size();
do {
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");
s->num_empty_heads = 0;
init_list(&s->empty_heads);
init_list(&s->partial_heads);
init_list(&s->full_heads);
return s;
}
@ -237,8 +258,7 @@ sl_alloc(slab *s)
struct sl_head *h;
redo:
h = HEAD(s->partial_heads);
if (!h->n.next)
if (!(h = s->partial_heads.first))
goto no_partial;
okay:
for (uint i=0; i<s->head_bitfield_len; i++)
@ -258,23 +278,27 @@ okay:
return out;
}
rem_node(&h->n);
add_tail(&s->full_heads, &h->n);
SL_HEAD_CHANGE_STATE(s, h, partial, full);
goto redo;
no_partial:
h = HEAD(s->empty_heads);
if (h->n.next)
if (h = s->empty_heads.first)
{
rem_node(&h->n);
add_head(&s->partial_heads, &h->n);
SL_HEAD_CHANGE_STATE(s, h, empty, partial);
s->num_empty_heads--;
goto okay;
}
h = alloc_page();
ASSERT_DIE(SL_GET_HEAD(h) == h);
#ifdef POISON
memset(h, 0xba, page_size);
#endif
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;
}
@ -303,9 +327,10 @@ sl_allocz(slab *s)
* and returns it back to the Slab @s.
*/
void
sl_free(slab *s, void *oo)
sl_free(void *oo)
{
struct sl_head *h = SL_GET_HEAD(oo);
struct slab *s = h->slab;
#ifdef POISON
memset(oo, 0xdb, s->data_size);
@ -318,19 +343,22 @@ sl_free(slab *s, void *oo)
h->used_bits[pos / 32] &= ~(1 << (pos % 32));
if (h->num_full-- == s->objs_per_slab)
{
rem_node(&h->n);
add_head(&s->partial_heads, &h->n);
}
if ((h->num_full-- == s->objs_per_slab) && (h->state == slh_full))
SL_HEAD_CHANGE_STATE(s, h, full, partial);
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)
{
#ifdef POISON
memset(h, 0xde, page_size);
#endif
free_page(h);
}
else
{
add_head(&s->empty_heads, &h->n);
sl_head_add_head(&s->empty_heads, h);
h->state = slh_empty;
s->num_empty_heads++;
}
}
@ -340,13 +368,12 @@ static void
slab_free(resource *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);
WALK_LIST_DELSAFE(h, g, s->partial_heads)
WALK_TLIST_DELSAFE(sl_head, h, &s->partial_heads)
free_page(h);
WALK_LIST_DELSAFE(h, g, s->full_heads)
WALK_TLIST_DELSAFE(sl_head, h, &s->full_heads)
free_page(h);
}
@ -355,13 +382,12 @@ slab_dump(resource *r)
{
slab *s = (slab *) r;
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++;
WALK_LIST(h, s->partial_heads)
WALK_TLIST(sl_head, h, &s->partial_heads)
pc++;
WALK_LIST(h, s->full_heads)
WALK_TLIST(sl_head, h, &s->full_heads)
fc++;
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;
size_t heads = 0;
struct sl_head *h;
WALK_LIST(h, s->full_heads)
WALK_TLIST(sl_head, h, &s->full_heads)
heads++;
size_t items = heads * s->objs_per_slab;
WALK_LIST(h, s->partial_heads)
WALK_TLIST(sl_head, h, &s->partial_heads)
{
heads++;
items += h->num_full;
}
WALK_LIST(h, s->empty_heads)
WALK_TLIST(sl_head, h, &s->empty_heads)
heads++;
size_t eff = items * s->obj_size;
size_t eff = items * s->data_size;
return (struct resmem) {
.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 *s = (slab *) r;
struct sl_head *h;
WALK_LIST(h, s->partial_heads)
if ((unsigned long) h < a && (unsigned long) h + get_page_size() < a)
WALK_TLIST(sl_head, h, &s->partial_heads)
if ((unsigned long) h < a && (unsigned long) h + page_size < a)
return r;
WALK_LIST(h, s->full_heads)
if ((unsigned long) h < a && (unsigned long) h + get_page_size() < a)
WALK_TLIST(sl_head, h, &s->full_heads)
if ((unsigned long) h < a && (unsigned long) h + page_size < a)
return r;
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 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_print(buffer *buf, const char *fmt, ...);
void buffer_puts(buffer *buf, const char *str);

View file

@ -76,6 +76,19 @@ current_time(void)
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
current_real_time(void)
{
@ -233,6 +246,7 @@ timers_fire(struct timeloop *loop)
io_log_event(t->hook, t->data);
t->hook(t);
tmp_flush();
}
}

View file

@ -44,6 +44,7 @@ static inline timer *timers_first(struct timeloop *loop)
extern struct timeloop main_timeloop;
btime current_time(void);
btime current_time_now(void);
btime current_real_time(void);
//#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
Name: bird
Version: 2.0.10
Version: 2.0.11
Release: 1
Copyright: GPL
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)
$(all-daemon)
$(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_targets := $(tests_targets) $(tests-target-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;
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))
return 1;
p += BS;
@ -602,8 +602,10 @@ 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)
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)
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);
int match;
if (set)
if (set->type == T_SET)
{
struct f_val v = {T_INT, .val.i = as};
match = !!find_tree(set, &v);
struct f_val v = { .type = T_INT, .val.i = as};
match = !!find_tree(set->val.t, &v);
}
else
match = (as == key);
else /* T_INT */
match = (as == set->val.i);
if (match == pos)
{
@ -667,6 +669,35 @@ as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tr
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
{

View file

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

View file

@ -693,3 +693,51 @@ lc_set_max(const struct adata *list, lcomm *val)
*val = (lcomm) { res[0], res[1], res[2] };
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
struct linpool *lp;
enum set_type
{
SET_TYPE_INT,
@ -38,24 +36,23 @@ generate_set_sequence(enum set_type type, int len)
{
struct adata empty_as_path = {};
set_sequence = set_sequence_same = set_sequence_higher = set_random = &empty_as_path;
lp = lp_new_default(&root_pool);
int i;
for (i = 0; i < len; i++)
{
if (type == SET_TYPE_INT)
{
set_sequence = int_set_add(lp, set_sequence, i);
set_sequence_same = int_set_add(lp, set_sequence_same, i);
set_sequence_higher = int_set_add(lp, set_sequence_higher, i + SET_SIZE);
set_random = int_set_add(lp, set_random, bt_random());
set_sequence = int_set_add(tmp_linpool, set_sequence, i);
set_sequence_same = int_set_add(tmp_linpool, set_sequence_same, i);
set_sequence_higher = int_set_add(tmp_linpool, set_sequence_higher, i + SET_SIZE);
set_random = int_set_add(tmp_linpool, set_random, bt_random());
}
else if (type == SET_TYPE_EC)
{
set_sequence = ec_set_add(lp, set_sequence, i);
set_sequence_same = ec_set_add(lp, set_sequence_same, i);
set_sequence_higher = ec_set_add(lp, set_sequence_higher, i + SET_SIZE);
set_random = ec_set_add(lp, set_random, (bt_random() << 32 | bt_random()));
set_sequence = ec_set_add(tmp_linpool, set_sequence, i);
set_sequence_same = ec_set_add(tmp_linpool, set_sequence_same, i);
set_sequence_higher = ec_set_add(tmp_linpool, set_sequence_higher, i + SET_SIZE);
set_random = ec_set_add(tmp_linpool, set_random, (bt_random() << 32 | bt_random()));
}
else
bt_abort_msg("This should be unreachable");
@ -71,7 +68,6 @@ t_set_int_contains(void)
{
int i;
resource_init();
generate_set_sequence(SET_TYPE_INT, 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++)
bt_assert_msg(data[i] == i, "(data[i] = %d) == i = %d)", data[i], i);
rfree(lp);
return 1;
}
static int
t_set_int_union(void)
{
resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE);
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_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(int_set_format(set_union, 0, 2, buf, BUFFER_SIZE) == 0);
rfree(lp);
return 1;
}
static int
t_set_int_format(void)
{
resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE_FOR_FORMAT_OUTPUT);
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(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;
}
static int
t_set_int_delete(void)
{
resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE);
const struct adata *deleting_sequence = set_sequence;
u32 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),
"int_set_get_size(deleting_sequence) %d == SET_SIZE-1-i %d",
int_set_get_size(deleting_sequence),
@ -160,7 +150,6 @@ t_set_ec_contains(void)
{
u32 i;
resource_init();
generate_set_sequence(SET_TYPE_EC, 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++)
// 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;
}
static int
t_set_ec_union(void)
{
resource_init();
generate_set_sequence(SET_TYPE_EC, SET_SIZE);
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_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(ec_set_format(set_union, 0, buf, BUFFER_SIZE) == 0);
rfree(lp);
return 1;
}
static int
t_set_ec_format(void)
{
resource_init();
const struct adata 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;
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++)
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_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);
rfree(lp);
return 1;
}
static int
t_set_ec_delete(void)
{
resource_init();
generate_set_sequence(SET_TYPE_EC, SET_SIZE);
const struct adata *deleting_sequence = set_sequence;
u32 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),
"ec_set_get_size(deleting_sequence) %d == SET_SIZE-1-i %d",
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
*/
struct f_val;
struct f_tree;
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);
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);
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)
{ 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) */
#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_lo(u64 ec) { return ec; }
static inline u64 ec_get(const u32 *l, int i)
{ 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 */
static inline u64 ec_as2(enum ec_subtype kind, u64 key, u64 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 ec_set_max(const struct adata *list, u64 *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 */

View file

@ -29,7 +29,7 @@ typedef struct cli {
node n; /* Node in list of all log hooks */
pool *pool;
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;
event *event;
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 *rta_pool;
extern uint *pages_kept;
void
cmd_show_memory(void)
@ -117,10 +118,11 @@ cmd_show_memory(void)
print_size("Routing tables:", rmemsize(rt_table_pool));
print_size("Route attributes:", rmemsize(rta_pool));
print_size("Protocols:", rmemsize(proto_pool));
print_size("Current config:", rmemsize(config_pool));
struct resmem total = rmemsize(&root_pool);
#ifdef HAVE_MMAP
print_size("Standby memory:", (struct resmem) { .overhead = get_page_size() * pages_kept });
total.overhead += get_page_size() * pages_kept;
print_size("Standby memory:", (struct resmem) { .overhead = page_size * *pages_kept });
total.overhead += page_size * *pages_kept;
#endif
print_size("Total:", total);
cli_msg(0, "");

View file

@ -130,7 +130,7 @@ CF_KEYWORDS(SORTED, TRIE, MIN, MAX, SETTLE, TIME, GC, THRESHOLD, PERIOD)
/* 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_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)
CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
@ -165,7 +165,7 @@ rtrid:
idval:
NUM { $$ = $1; }
| '(' term ')' { $$ = f_eval_int(f_linearize($2)); }
| '(' term ')' { $$ = f_eval_int(f_linearize($2, 1)); }
| IP4 { $$ = ip4_to_u32($1); }
| CF_SYM_KNOWN {
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(); } ;
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); } ;
r_args:
@ -860,7 +860,7 @@ CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]])
{ filters_dump_all(); cli_msg(0, ""); } ;
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(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->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)
{ 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
* @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)
{ 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
* @p: protocol instance the route is going to be exported to

View file

@ -23,9 +23,9 @@
#include "filter/f-inst.h"
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];
#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->parent = proto;
cf->copy = 1;
return cf;
}
@ -829,6 +830,9 @@ static int reconfigure_type; /* Hack to propagate type info to channel_reconfig
int
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 */
if ((c->table != cf->table->table) || (cf->ra_mode && (c->ra_mode != cf->ra_mode)))
return 0;
@ -1651,6 +1655,8 @@ proto_build(struct protocol *p)
/* FIXME: convert this call to some protocol hook */
extern void bfd_init_all(void);
void protos_build_gen(void);
/**
* protos_build - build a protocol list
*
@ -1663,44 +1669,7 @@ extern void bfd_init_all(void);
void
protos_build(void)
{
init_list(&proto_list);
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
protos_build_gen();
proto_pool = rp_new(&root_pool, "Protocols");
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;
}
cmd(s->proto->proto, arg, 0);
cli_msg(0, "");
if (s->proto->proto)
{
cmd(s->proto->proto, arg, 0);
cli_msg(0, "");
}
else
cli_msg(9002, "%s does not exist", s->name);
}
static void

View file

@ -74,7 +74,6 @@ struct protocol {
struct proto * (*init)(struct proto_config *); /* Create new instance */
int (*reconfigure)(struct proto *, struct proto_config *); /* Try to reconfigure instance, returns success */
void (*dump)(struct proto *); /* Debugging dump */
void (*dump_attrs)(struct rte *); /* Dump protocol-dependent attributes */
int (*start)(struct proto *); /* Start the instance */
int (*shutdown)(struct proto *); /* Stop the instance */
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 protos_build(void);
void proto_build(struct protocol *);
void protos_build(void); /* Called from sysdep to initialize protocols */
void proto_build(struct protocol *); /* Called from protocol to register itself */
void protos_preconfig(struct config *);
void protos_commit(struct config *new, struct config *old, int force_restart, int type);
struct proto * proto_spawn(struct proto_config *cf, uint disabled);
@ -198,12 +197,11 @@ struct proto {
* ifa_notify Notify protocol about interface address changes.
* rt_notify Notify protocol about routing table updates.
* 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.
* store_tmp_attrs Store private attrs back to rte and undef added attributes. The route and rta MUST NOT be cached.
* preexport Called as the first step of the route exporting process.
* It can construct a new rte, add private attributes and
* decide whether the route shall be exported: 1=yes, -1=no,
* 0=process it through the export filter set by the user.
* preexport Called as the first step of the route exporting process.
* It can decide whether the route shall be exported:
* -1 = reject,
* 0 = continue to export filter
* 1 = accept immediately
* reload_routes Request channel to reload all its routes to the core
* (using rte_update()). Returns: 0=reload cannot be done,
* 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 (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old);
void (*neigh_notify)(struct neighbor *neigh);
void (*make_tmp_attrs)(struct rte *rt, struct linpool *pool);
void (*store_tmp_attrs)(struct rte *rt, struct linpool *pool);
int (*preexport)(struct proto *, struct rte **rt, struct linpool *pool);
int (*preexport)(struct channel *, struct rte *rt);
void (*reload_routes)(struct channel *);
void (*feed_begin)(struct channel *, int initial);
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_better)(struct rte *, struct rte *);
int (*rte_same)(struct rte *, struct rte *);
int (*rte_mergable)(struct rte *, struct rte *);
struct rte * (*rte_modify)(struct rte *, struct linpool *);
void (*rte_insert)(struct network *, struct rte *);
void (*rte_remove)(struct network *, struct rte *);
u32 (*rte_igp_metric)(struct rte *);
/* Hic sunt protocol-specific data */
};
@ -469,7 +465,6 @@ struct channel_class {
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_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_*) */
u16 preference; /* Default route preference */
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 in_keep_filtered; /* Routes rejected in import filter are kept */
u8 rpki_reload; /* RPKI changes trigger channel reload */

View file

@ -256,50 +256,13 @@ struct hostentry {
typedef struct rte {
struct rte *next;
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 rta *attrs; /* Attributes of this route */
u32 id; /* Table specific route id */
byte flags; /* Flags (REF_...) */
byte pflags; /* Protocol-specific flags */
word pref; /* Route preference */
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;
#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);
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
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);
/* 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);
void rt_refresh_begin(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 *);
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);
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_all(void);
int rt_feed_channel(struct channel *c);
@ -489,18 +448,17 @@ typedef struct rta {
u32 uc; /* Use count */
u32 hash_key; /* Hash over important fields */
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 */
ip_addr from; /* Advertising router */
u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */
u8 source; /* Route source (RTS_...) */
u8 scope; /* Route scope (SCOPE_... -- see ip.h) */
u8 dest; /* Route destination type (RTD_...) */
u8 aflags;
u16 cached:1; /* Are attributes cached? */
u16 source:7; /* Route source (RTS_...) */
u16 scope:4; /* Route scope (SCOPE_... -- see ip.h) */
u16 dest:4; /* Route destination type (RTD_...) */
word pref;
struct nexthop nh; /* Next hop */
} rta;
#define RTS_DUMMY 0 /* Dummy route to be removed soon */
#define RTS_STATIC 1 /* Normal static route */
#define RTS_INHERIT 2 /* Route inherited from kernel */
#define RTS_DEVICE 3 /* Device route */
@ -518,11 +476,6 @@ typedef struct rta {
#define RTS_PERF 15 /* Perf checker */
#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_UNICAST 1 /* Next hop is neighbor router */
#define RTD_BLACKHOLE 2 /* Silently drop packets */
@ -530,8 +483,6 @@ typedef struct rta {
#define RTD_PROHIBIT 4 /* Administratively prohibited */
#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
protocol-specific metric is availabe */
@ -553,10 +504,13 @@ static inline int rte_is_reachable(rte *r)
typedef struct eattr {
word id; /* EA_CODE(PROTOCOL_..., protocol-dependent ID) */
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 {
u32 data;
const struct adata *ptr; /* Attribute data elsewhere */
uintptr_t data;
const struct adata *ptr; /* Attribute data elsewhere */
} u;
} eattr;
@ -564,7 +518,6 @@ typedef struct eattr {
#define EA_CODE(proto,id) (((proto) << 8) | (id))
#define EA_ID(ea) ((ea) & 0xff)
#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_IS_CUSTOM(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_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_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_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 {
uint length; /* Length of data */
@ -625,7 +576,6 @@ typedef struct ea_list {
#define EALF_SORTED 1 /* Attributes are sorted by code */
#define EALF_BISECT 2 /* Use interval bisection for searching */
#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_get_source(struct proto *p, u32 id);
@ -641,7 +591,7 @@ struct ea_walk_state {
eattr *ea_find(ea_list *, unsigned ea);
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_sort(ea_list *); /* Sort entries in all sub-lists */
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; \
} while(0) \
struct ea_one_attr_list {
ea_list l;
eattr a;
};
static inline eattr *
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));
eattr *e = &a->attrs[0];
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->flags = EALF_SORTED;
a->count = 1;
a->next = *to;
*to = a;
e->id = id;
e->type = type;
e->flags = flags;
.a.id = id,
.a.type = type,
.a.flags = flags,
};
if (type & EAF_EMBEDDED)
e->u.data = (u32) val;
ea->a.u.data = val;
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
@ -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; }
#define RTA_MAX_SIZE (sizeof(rta) + sizeof(u32)*MPLS_MAX_LABEL_STACK)
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; }
void rta__free(rta *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 char * const rta_src_names[RTS_MAX] = {
[RTS_DUMMY] = "",
[RTS_STATIC] = "static",
[RTS_INHERIT] = "inherit",
[RTS_DEVICE] = "device",
@ -155,7 +154,7 @@ rt_prune_sources(void)
{
HASH_DO_REMOVE(src_hash, RSH, sp);
idm_free(&src_ids, src->global_id);
sl_free(rte_src_slab, src);
sl_free(src);
}
}
HASH_WALK_FILTER_END;
@ -392,7 +391,7 @@ nexthop_free(struct nexthop *o)
while (o)
{
n = o->next;
sl_free(nexthop_slab(o), o);
sl_free(o);
o = n;
}
}
@ -449,8 +448,7 @@ ea_find(ea_list *e, unsigned id)
{
eattr *a = ea__find(e, id & EA_CODE_MASK);
if (a && (a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF &&
!(id & EA_ALLOW_UNDEF))
if (a && a->undef && !(id & EA_ALLOW_UNDEF))
return NULL;
return a;
}
@ -517,7 +515,7 @@ ea_walk(struct ea_walk_state *s, uint id, uint max)
BIT32_SET(s->visited, n);
if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
if (a->undef)
continue;
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
* a provided default if no such attribute is present.
*/
int
ea_get_int(ea_list *e, unsigned id, int def)
uintptr_t
ea_get_int(ea_list *e, unsigned id, uintptr_t def)
{
eattr *a = ea_find(e, id);
if (!a)
@ -617,14 +615,17 @@ ea_do_prune(ea_list *e)
/* Now s0 is the most recent version, s[-1] the oldest one */
/* Drop undefs */
if ((s0->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
if (s0->undef)
continue;
/* Copy the newest version to destination */
*d = *s0;
/* 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 */
d++;
@ -738,6 +739,9 @@ ea_same(ea_list *x, ea_list *y)
if (a->id != b->id ||
a->flags != b->flags ||
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)))
return 0;
}
@ -940,6 +944,10 @@ ea_show(struct cli *c, const eattr *e)
{
*pos++ = ':';
*pos++ = ' ';
if (e->undef)
bsprintf(pos, "undefined");
else
switch (e->type & EAF_TYPE_MASK)
{
case EAF_TYPE_INT:
@ -969,7 +977,6 @@ ea_show(struct cli *c, const eattr *e)
case EAF_TYPE_LC_SET:
ea_show_lc_set(c, ad, pos, buf, end);
return;
case EAF_TYPE_UNDEF:
default:
bsprintf(pos, "<type %02x>", e->type);
}
@ -1005,7 +1012,7 @@ ea_dump(ea_list *e)
eattr *a = &e->attrs[i];
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]);
if (a->type & EAF_ORIGINATED)
if (a->originated)
debug("o");
if (a->type & EAF_EMBEDDED)
debug(":%08x", a->u.data);
@ -1104,13 +1111,14 @@ rta_hash(rta *a)
u64 h;
mem_hash_init(&h);
#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(from);
MIX(igp_metric);
MIX(source);
MIX(scope);
MIX(dest);
BMIX(source);
BMIX(scope);
BMIX(dest);
MIX(pref);
#undef MIX
return mem_hash_value(&h) ^ nexthop_hash(&(a->nh)) ^ ea_hash(a->eattrs);
@ -1119,8 +1127,7 @@ rta_hash(rta *a)
static inline int
rta_same(rta *x, rta *y)
{
return (x->src == y->src &&
x->source == y->source &&
return (x->source == y->source &&
x->scope == y->scope &&
x->dest == y->dest &&
x->igp_metric == y->igp_metric &&
@ -1198,7 +1205,7 @@ rta_lookup(rta *o)
rta *r;
uint h;
ASSERT(!(o->aflags & RTAF_CACHED));
ASSERT(!o->cached);
if (o->eattrs)
ea_normalize(o->eattrs);
@ -1209,8 +1216,7 @@ rta_lookup(rta *o)
r = rta_copy(o);
r->hash_key = h;
r->aflags = RTAF_CACHED;
rt_lock_source(r->src);
r->cached = 1;
rt_lock_hostentry(r->hostentry);
rta_insert(r);
@ -1223,18 +1229,17 @@ rta_lookup(rta *o)
void
rta__free(rta *a)
{
ASSERT(rta_cache_count && (a->aflags & RTAF_CACHED));
ASSERT(rta_cache_count && a->cached);
rta_cache_count--;
*a->pprev = a->next;
if (a->next)
a->next->pprev = a->pprev;
rt_unlock_hostentry(a->hostentry);
rt_unlock_source(a->src);
if (a->nh.next)
nexthop_free(a->nh.next);
ea_free(a->eattrs);
a->aflags = 0; /* Poison the entry */
sl_free(rta_slab(a), a);
a->cached = 0;
sl_free(a);
}
rta *
@ -1248,7 +1253,7 @@ rta_do_cow(rta *o, linpool *lp)
memcpy(*nhn, nho, nexthop_size(nho));
nhn = &((*nhn)->next);
}
r->aflags = 0;
r->cached = 0;
r->uc = 0;
return r;
}
@ -1262,16 +1267,16 @@ rta_do_cow(rta *o, linpool *lp)
void
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_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1",
"RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL" };
static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };
debug("p=%s uc=%d %s %s%s h=%04x",
a->src->proto->name, a->uc, rts[a->source], ip_scope_text(a->scope),
debug("pref=%d uc=%d %s %s%s h=%04x",
a->pref, a->uc, rts[a->source], ip_scope_text(a->scope),
rtd[a->dest], a->hash_key);
if (!(a->aflags & RTAF_CACHED))
if (!a->cached)
debug(" !CACHED");
debug(" <-%I", a->from);
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);
rta a0 = {
.src = src,
.pref = c->preference,
.source = RTS_DEVICE,
.scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST,
@ -91,7 +91,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
};
a = rta_lookup(&a0);
e = rte_get_temp(a);
e = rte_get_temp(a, src);
e->pflags = 0;
rte_update2(c, net, e, src);
}
@ -195,3 +195,9 @@ struct protocol proto_device = {
.reconfigure = dev_reconfigure,
.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)
sl_free(f->fib_slab, E);
sl_free(E);
else
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)
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)
get_route_info(e, info);
else
bsprintf(info, " (%d)", e->pref);
bsprintf(info, " (%d)", a->pref);
if (d->last_table != d->tab)
rt_show_table(c, d);
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)
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;
ee = e;
rte_make_tmp_attrs(&e, c->show_pool, NULL);
/* Export channel is down, do not try to export routes to it */
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)
{
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)
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;
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++)
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);
}

View file

@ -549,7 +549,7 @@ rte_find(net *net, struct rte_src *src)
{
rte *e = net->routes;
while (e && e->attrs->src != src)
while (e && e->src != src)
e = e->next;
return e;
}
@ -564,14 +564,14 @@ rte_find(net *net, struct rte_src *src)
* the protocol.
*/
rte *
rte_get_temp(rta *a)
rte_get_temp(rta *a, struct rte_src *src)
{
rte *e = sl_alloc(rte_slab);
e->attrs = a;
e->id = 0;
e->flags = 0;
e->pref = 0;
rt_lock_source(e->src = src);
return e;
}
@ -581,6 +581,8 @@ rte_do_cow(rte *r)
rte *e = sl_alloc(rte_slab);
memcpy(e, r, sizeof(rte));
rt_lock_source(e->src);
e->attrs = rta_clone(r->attrs);
e->flags = 0;
return e;
@ -618,176 +620,6 @@ rte_cow_rta(rte *r, linpool *lp)
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 */
rte_better(rte *new, rte *old)
{
@ -798,20 +630,20 @@ rte_better(rte *new, rte *old)
if (!rte_is_valid(new))
return 0;
if (new->pref > old->pref)
if (new->attrs->pref > old->attrs->pref)
return 1;
if (new->pref < old->pref)
if (new->attrs->pref < old->attrs->pref)
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
* have the same preference, try to break the tie by comparing addresses. Not too
* 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 0;
}
@ -824,13 +656,13 @@ rte_mergable(rte *pri, rte *sec)
if (!rte_is_valid(pri) || !rte_is_valid(sec))
return 0;
if (pri->pref != sec->pref)
if (pri->attrs->pref != sec->attrs->pref)
return 0;
if (pri->attrs->src->proto->proto != sec->attrs->src->proto->proto)
if (pri->src->proto->proto != sec->src->proto->proto)
return 0;
if (mergable = pri->attrs->src->proto->rte_mergable)
if (mergable = pri->src->proto->rte_mergable)
return mergable(pri, sec);
return 0;
@ -839,8 +671,8 @@ rte_mergable(rte *pri, rte *sec)
static void
rte_trace(struct channel *c, rte *e, int dir, char *msg)
{
log(L_TRACE "%s.%s %c %s %N %s",
c->proto->name, c->name ?: "?", dir, msg, e->net->n.addr,
log(L_TRACE "%s.%s %c %s %N %uL %uG %s",
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));
}
@ -870,7 +702,7 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int si
rt = rt0;
*rt_free = NULL;
v = p->preexport ? p->preexport(p, &rt, pool) : 0;
v = p->preexport ? p->preexport(c, rt) : 0;
if (v < 0)
{
if (silent)
@ -888,8 +720,6 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int si
goto accept;
}
rte_make_tmp_attrs(&rt, pool, NULL);
v = filter && ((filter == FILTER_REJECT) ||
(f_run(filter, &rt, pool,
(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;
}
#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:
if (rt != rt0)
*rt_free = rt;
@ -1288,7 +1112,20 @@ rte_announce(rtable *tab, uint type, net *net, rte *new, rte *old,
break;
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;
case RA_MERGED:
@ -1351,16 +1188,18 @@ rte_validate(rte *e)
void
rte_free(rte *e)
{
rt_unlock_source(e->src);
if (rta_is_cached(e->attrs))
rta_free(e->attrs);
sl_free(rte_slab, e);
sl_free(e);
}
static inline void
rte_free_quick(rte *e)
{
rt_unlock_source(e->src);
rta_free(e->attrs);
sl_free(rte_slab, e);
sl_free(e);
}
static int
@ -1370,8 +1209,7 @@ rte_same(rte *x, rte *y)
return
x->attrs == y->attrs &&
x->pflags == y->pflags &&
x->pref == y->pref &&
(!x->attrs->src->proto->rte_same || x->attrs->src->proto->rte_same(x, y)) &&
x->src == y->src &&
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 */
while (old = *k)
{
if (old->attrs->src == src)
if (old->src == src)
{
/* If there is the same route in the routing table but from
* a different sender, then there are two paths from the
@ -1674,26 +1512,6 @@ rte_update_unlock(void)
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
* @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_stats *stats = &c->stats;
const struct filter *filter = c->in_filter;
rte *dummy = NULL;
net *nn;
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->sender = c;
if (!new->pref)
new->pref = c->preference;
stats->imp_updates_received++;
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)
{
rta *old_attrs = NULL;
rte_make_tmp_attrs(&new, rte_update_pool, &old_attrs);
int fr = f_run(filter, &new, rte_update_pool, 0);
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");
if (! c->in_keep_filtered)
{
rta_free(old_attrs);
goto drop;
}
new->flags |= REF_FILTERED;
}
rte_store_tmp_attrs(new, rte_update_pool, old_attrs);
}
if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
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:
/* And recalculate the best route */
rte_hide_dummy_routes(nn, &dummy);
rte_recalculate(c, nn, new, src);
rte_unhide_dummy_routes(nn, &dummy);
rte_update_unlock();
return;
@ -1855,7 +1659,7 @@ static inline void
rte_discard(rte *old) /* Non-filtered route deletion, used during garbage collection */
{
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();
}
@ -1875,7 +1679,7 @@ rte_modify(rte *old)
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();
@ -1883,8 +1687,9 @@ rte_modify(rte *old)
/* Check rtable for best route to given net whether it would be exported do p */
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);
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();
/* 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)
{
rte_make_tmp_attrs(&rt, rte_update_pool, NULL);
v = (f_run(filter, &rt, rte_update_pool, FF_SILENT) <= F_ACCEPT);
}
/* Discard temporary rte */
if (rt != n->routes)
@ -1999,10 +1801,8 @@ rte_dump(rte *e)
{
net *n = e->net;
debug("%-1N ", n->n.addr);
debug("PF=%02x pref=%d ", e->pflags, e->pref);
debug("PF=%02x ", e->pflags);
rta_dump(e->attrs);
if (e->attrs->src->proto->proto->dump_attrs)
e->attrs->src->proto->proto->dump_attrs(e);
debug("\n");
}
@ -2322,12 +2122,7 @@ static struct resclass rt_class = {
rtable *
rt_setup(pool *pp, struct rtable_config *cf)
{
int ns = strlen("Routing table ") + strlen(cf->name) + 1;
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);
pool *p = rp_newf(pp, "Routing table %s", cf->name);
rtable *t = ralloc(p, &rt_class);
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));
rta_apply_hostentry(a, old->attrs->hostentry, &mls);
a->aflags = 0;
a->cached = 0;
rte *e = sl_alloc(rte_slab);
memcpy(e, old, sizeof(rte));
e->attrs = rta_lookup(a);
rt_lock_source(e->src);
return e;
}
@ -2882,12 +2678,16 @@ static rte *
rt_flowspec_update_rte(rtable *tab, rte *r)
{
#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;
const net_addr *n = r->net->n.addr;
struct bgp_proto *p = (void *) r->attrs->src->proto;
int valid = rt_flowspec_check(r->u.bgp.base_table, tab, n, r->attrs, p->is_interior);
struct bgp_proto *p = (void *) r->src->proto;
int valid = rt_flowspec_check(bc->base_table, tab, n, r->attrs, p->is_interior);
int dest = valid ? RTD_NONE : RTD_UNREACHABLE;
if (dest == r->attrs->dest)
@ -2896,11 +2696,12 @@ rt_flowspec_update_rte(rtable *tab, rte *r)
rta *a = alloca(RTA_MAX_SIZE);
memcpy(a, r->attrs, rta_size(r->attrs));
a->dest = dest;
a->aflags = 0;
a->cached = 0;
rte *new = sl_alloc(rte_slab);
memcpy(new, r, sizeof(rte));
new->attrs = rta_lookup(a);
rt_lock_source(new->src);
return new;
#else
@ -2936,8 +2737,8 @@ rt_next_hop_update_net(rtable *tab, net *n)
/* Call a pre-comparison hook */
/* Not really an efficient way to compute this */
if (e->attrs->src->proto->rte_recalculate)
e->attrs->src->proto->rte_recalculate(tab, n, new, e, NULL);
if (e->src->proto->rte_recalculate)
e->src->proto->rte_recalculate(tab, n, new, e, NULL);
if (e != old_best)
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);
if (!new->pref)
new->pref = c->preference;
if (!rta_is_cached(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 */
for (pos = &net->routes; old = *pos; pos = &old->next)
if (old->attrs->src == src)
if (old->src == src)
{
if (new && rte_same(old, new))
{
@ -3391,7 +3189,7 @@ rt_reload_channel(struct channel *c)
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;
@ -3474,9 +3272,7 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int re
if (new)
{
net = net_get(tab, n);
src = new->attrs->src;
rte_store_tmp_attrs(new, rte_update_pool, NULL);
src = new->src;
if (!rta_is_cached(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
{
net = net_find(tab, n);
src = old0->attrs->src;
src = old0->src;
if (!net)
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 */
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))
{
@ -3641,7 +3437,7 @@ hc_delete_hostentry(struct hostcache *hc, pool *p, struct hostentry *he)
rem_node(&he->ln);
hc_remove(hc, he);
sl_free(hc->slab, he);
sl_free(he);
hc->hash_items--;
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->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);
tab->hostcache = hc;
@ -3717,36 +3513,12 @@ rt_get_igp_metric(rte *rt)
if (ea)
return ea->u.data;
rta *a = rt->attrs;
#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)
if (rt->attrs->source == RTS_DEVICE)
return 0;
if (rt->src->proto->rte_igp_metric)
return rt->src->proto->rte_igp_metric(rt);
return IGP_METRIC_UNKNOWN;
}
@ -3770,9 +3542,10 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
{
rte *e = n->routes;
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 */
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;
}
pxlen = n->n.addr->pxlen;
if (a->dest == RTD_UNICAST)
{
for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
@ -3841,6 +3616,7 @@ rt_update_hostcache(rtable *tab)
struct hostentry *
rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
{
ip_addr link = ipa_zero(ll) ? a : ll;
struct hostentry *he;
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);
struct hostcache *hc = tab->hostcache;
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;
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);
return he;
}

View file

@ -119,7 +119,7 @@ babel_get_source(struct babel_proto *p, struct babel_entry *e, u64 router_id)
}
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;
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_)
{
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 *
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);
if (r)
if (r) {
babel_update_smoothed_metric(p, r);
return r;
}
r = sl_allocz(p->route_slab);
@ -174,7 +273,7 @@ babel_retract_route(struct babel_proto *p, struct babel_route *r)
}
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",
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)
r->e->selected = NULL;
sl_free(p->route_slab, r);
sl_free(r);
}
static void
@ -304,7 +403,7 @@ babel_add_seqno_request(struct babel_proto *p, struct babel_entry *e,
struct babel_seqno_request *sr;
WALK_LIST(sr, e->requests)
if (sr->router_id == router_id)
if (sr->router_id == router_id && sr->nbr == nbr)
{
/* Found matching or newer */
if (ge_mod64k(sr->seqno, seqno) && seqno_request_valid(sr))
@ -338,30 +437,33 @@ found:
}
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)
rem_node(&sr->nbr_node);
rem_node(NODE sr);
sl_free(p->seqno_slab, sr);
sl_free(sr);
}
static int
babel_satisfy_seqno_request(struct babel_proto *p, struct babel_entry *e,
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))
{
/* 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);
return 1;
ret = 1;
}
return 0;
return ret;
}
static void
@ -434,6 +536,10 @@ babel_get_neighbor(struct babel_iface *ifa, ip_addr addr)
init_list(&nbr->requests);
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;
}
@ -458,6 +564,8 @@ babel_flush_neighbor(struct babel_proto *p, struct babel_neighbor *nbr)
nbr->ifa = NULL;
rem_node(NODE nbr);
mb_free(nbr->srtt_pool);
mb_free(nbr->srtt_pool_sorted);
mb_free(nbr);
}
@ -569,6 +677,7 @@ babel_update_cost(struct babel_neighbor *nbr)
switch (cf->type)
{
case BABEL_IFACE_TYPE_WIRED:
case BABEL_IFACE_TYPE_TUNNEL:
/* 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 */
@ -597,6 +706,25 @@ babel_update_cost(struct babel_neighbor *nbr)
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:
/* If RX cost changed, send IHU with next Hello */
if (rxcost != nbr->rxcost)
@ -616,7 +744,7 @@ done:
struct babel_route *r; node *n;
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);
}
}
@ -640,13 +768,36 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
if (r)
{
rta a0 = {
.src = p->p.main_source,
.source = RTS_BABEL,
.scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST,
.pref = c->preference,
.from = r->neigh->addr,
.nh.gw = r->next_hop,
.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;
rta *a = rta_lookup(&a0);
rte *rte = rte_get_temp(a);
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);
rte *rte = rte_get_temp(a, p->p.main_source);
e->unreachable = 0;
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 */
rta a0 = {
.src = p->p.main_source,
.source = RTS_BABEL,
.scope = SCOPE_UNIVERSE,
.dest = RTD_UNREACHABLE,
.pref = 1,
};
rta *a = rta_lookup(&a0);
rte *rte = rte_get_temp(a);
memset(&rte->u.babel, 0, sizeof(rte->u.babel));
rte *rte = rte_get_temp(a, p->p.main_source);
rte->pflags = 0;
rte->pref = 1;
e->unreachable = 1;
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 */
if (mod && (mod != best))
{
/* Either select modified route, or keep old best route */
if ((mod->metric < (best ? best->metric : BABEL_INFINITY)) && mod->feasible)
if (babel_route_better(p, mod, best))
best = mod;
else
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)
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 */
WALK_LIST(r, e->routes)
if ((r->metric < (best ? best->metric : BABEL_INFINITY)) && r->feasible)
if (babel_route_better(p, r, best))
best = r;
}
if (best)
{
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)
{
@ -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.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",
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.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",
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);
struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender);
struct babel_iface_config *cf = n->ifa->cf;
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_cost(n);
/* Speed up session establishment by sending IHU immediately */
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
@ -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);
n->txcost = msg->rxcost;
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);
}
@ -1287,9 +1519,9 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
return;
/* Last paragraph above - update the entry */
babel_update_metric(p, r, metric);
r->feasible = feasible;
r->seqno = msg->seqno;
r->metric = metric;
r->advert_metric = msg->metric;
r->router_id = msg->router_id;
r->next_hop = msg->next_hop;
@ -2009,7 +2241,13 @@ babel_dump(struct proto *P)
static void
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
@ -2017,6 +2255,9 @@ babel_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
{
switch (a->id)
{
case EA_BABEL_SEQNO:
return GA_FULL;
case EA_BABEL_METRIC:
bsprintf(buf, "metric: %d", a->u.data);
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, "%-25s %-10s %6s %6s %6s %7s %4s",
"IP address", "Interface", "Metric", "Routes", "Hellos", "Expires", "Auth");
cli_msg(-1024, "%-25s %-10s %6s %6s %6s %7s %4s %11s",
"IP address", "Interface", "Metric", "Routes", "Hellos", "Expires", "Auth", "RTT");
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);
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->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
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 */
if ((a->dest == RTD_UNREACHABLE) && (a->src->proto == P))
if ((a->dest == RTD_UNREACHABLE) && (new->src->proto == C->proto))
return -1;
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),
* 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)
{
/* Update */
uint internal = (new->attrs->src->proto == P);
uint rt_seqno = internal ? new->u.babel.seqno : p->update_seqno;
uint rt_seqno;
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)
{
@ -2322,15 +2554,16 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
static int
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
babel_rte_same(struct rte *new, struct rte *old)
static u32
babel_rte_igp_metric(struct rte *rt)
{
return ((new->u.babel.seqno == old->u.babel.seqno) &&
(new->u.babel.metric == old->u.babel.metric) &&
(new->u.babel.router_id == old->u.babel.router_id));
return ea_get_int(rt->attrs->eattrs, EA_BABEL_METRIC, BABEL_INFINITY);
}
@ -2364,10 +2597,8 @@ babel_init(struct proto_config *CF)
P->if_notify = babel_if_notify;
P->rt_notify = babel_rt_notify;
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_same = babel_rte_same;
P->rte_igp_metric = babel_rte_igp_metric;
return P;
}
@ -2483,3 +2714,9 @@ struct protocol proto_babel = {
.get_route_info = babel_get_route_info,
.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_ROUTER_ID EA_CODE(PROTOCOL_BABEL, 1)
#define EA_BABEL_SEQNO EA_CODE(PROTOCOL_BABEL, 2)
#define BABEL_MAGIC 42
#define BABEL_VERSION 2
@ -50,10 +51,28 @@
#define BABEL_GARBAGE_INTERVAL (300 S_)
#define BABEL_RXCOST_WIRED 96
#define BABEL_RXCOST_WIRELESS 256
#define BABEL_RXCOST_RTT 96
#define BABEL_INITIAL_HOP_COUNT 255
#define BABEL_MAX_SEND_INTERVAL 5 /* Unused ? */
#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 */
#define BABEL_TIME_UNITS 10000 /* On-wire times are counted in centiseconds */
#define BABEL_MIN_INTERVAL (0x0001 * BABEL_TIME_UNITS)
@ -93,6 +112,8 @@ enum babel_tlv_type {
enum babel_subtlv_type {
BABEL_SUBTLV_PAD1 = 0,
BABEL_SUBTLV_PADN = 1,
BABEL_SUBTLV_DIVERSITY = 2, /* we don't support this */
BABEL_SUBTLV_TIMESTAMP = 3,
/* Mandatory subtlvs */
BABEL_SUBTLV_SOURCE_PREFIX = 128,
@ -103,6 +124,7 @@ enum babel_iface_type {
BABEL_IFACE_TYPE_UNDEF = 0,
BABEL_IFACE_TYPE_WIRED = 1,
BABEL_IFACE_TYPE_WIRELESS = 2,
BABEL_IFACE_TYPE_TUNNEL = 3,
BABEL_IFACE_TYPE_MAX
};
@ -118,6 +140,8 @@ enum babel_ae_type {
struct babel_config {
struct proto_config c;
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 */
u8 randomize_router_id;
@ -137,6 +161,12 @@ struct babel_iface_config {
uint ihu_interval; /* IHU 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 tx_length; /* TX packet length limit (including headers), 0 for MTU */
int tx_tos;
@ -224,6 +254,14 @@ struct babel_neighbor {
u16 next_hello_seqno;
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;
u8 auth_passed;
u8 auth_index_len;
@ -261,9 +299,11 @@ struct babel_route {
u16 seqno;
u16 metric;
u16 advert_metric;
u16 smoothed_metric;
u64 router_id;
ip_addr next_hop;
btime refresh_time;
btime smoothed_time;
btime expires;
};
@ -321,6 +361,8 @@ struct babel_msg_hello {
u16 seqno;
uint interval;
ip_addr sender;
u32 tstamp;
btime pkt_received;
};
struct babel_msg_ihu {
@ -330,6 +372,9 @@ struct babel_msg_ihu {
uint interval;
ip_addr addr;
ip_addr sender;
u32 tstamp;
u32 tstamp_rcvd;
btime pkt_received;
};
struct babel_msg_update {

View file

@ -23,9 +23,10 @@ CF_DEFINES
CF_DECLS
CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK,
NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE)
TYPE, WIRED, WIRELESS, TUNNEL, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK,
LINK, NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE,
RTT, MIN, MAX, DECAY, WINLEN, SEND, TIMESTAMPS)
CF_GRAMMAR
@ -36,6 +37,13 @@ babel_proto_start: proto_start BABEL
this_proto = proto_config_new(&proto_babel, $1);
init_list(&BABEL_CFG->iface_list);
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:
@ -43,6 +51,7 @@ babel_proto_item:
| proto_channel
| INTERFACE babel_iface
| 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:
@ -51,7 +60,7 @@ babel_proto_opts:
;
babel_proto:
babel_proto_start proto_name '{' babel_proto_opts '}';
babel_proto_start proto_name '{' babel_proto_opts '}' babel_proto_finish;
babel_iface_start:
@ -66,6 +75,10 @@ babel_iface_start:
BABEL_IFACE->limit = BABEL_HELLO_LIMIT;
BABEL_IFACE->tx_tos = IP_PREC_INTERNET_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;
};
@ -85,8 +98,16 @@ babel_iface_finish:
BABEL_IFACE->hello_interval = BABEL_HELLO_INTERVAL_WIRED;
if (!BABEL_IFACE->rxcost)
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 */
if (!BABEL_IFACE->update_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"); }
| TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; }
| 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"); }
| 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"); }
@ -146,6 +168,11 @@ babel_iface_item:
| 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 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
;

View file

@ -58,6 +58,13 @@ struct babel_tlv_ihu {
u8 addr[0];
} PACKED;
struct babel_subtlv_timestamp {
u8 type;
u8 length;
u32 tstamp;
u32 tstamp_rcvd; /* only used in IHU */
} PACKED;
struct babel_tlv_router_id {
u8 type;
u8 length;
@ -161,6 +168,7 @@ struct babel_parse_state {
const struct babel_tlv_data* (*get_subtlv_data)(u8 type);
struct babel_proto *proto;
struct babel_iface *ifa;
btime received_time;
ip_addr saddr;
ip_addr next_hop_ip4;
ip_addr next_hop_ip6;
@ -170,6 +178,7 @@ struct babel_parse_state {
u8 router_id_seen; /* router_id field is valid */
u8 def_ip6_prefix_seen; /* def_ip6_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 sadr_enabled;
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_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_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_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_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_timestamp(struct babel_tlv *hdr, u32 tstamp, u32 tstamp_rcvd, uint max_len);
static const struct babel_tlv_data tlv_data[BABEL_TLV_MAX] = {
[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;
}
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 = {
sizeof(struct babel_subtlv_source_prefix),
babel_read_source_prefix,
@ -428,6 +446,8 @@ static const struct babel_tlv_data *get_packet_subtlv_data(u8 type)
{
switch (type)
{
case BABEL_SUBTLV_TIMESTAMP:
return &timestamp_tlv_data;
case BABEL_SUBTLV_SOURCE_PREFIX:
return &source_prefix_tlv_data;
@ -489,16 +509,34 @@ babel_read_hello(struct babel_tlv *hdr, union babel_msg *m,
static uint
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_msg_hello *msg = &m->hello;
uint len = sizeof(struct babel_tlv_hello);
TLV_HDR0(tlv, BABEL_TLV_HELLO);
put_u16(&tlv->seqno, msg->seqno);
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
@ -556,6 +594,7 @@ babel_write_ihu(struct babel_tlv *hdr, union babel_msg *m,
{
struct babel_tlv_ihu *tlv = (void *) hdr;
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)
return 0;
@ -567,12 +606,24 @@ babel_write_ihu(struct babel_tlv *hdr, union babel_msg *m,
if (!ipa_is_link_local(msg->addr))
{
tlv->ae = BABEL_AE_WILDCARD;
return sizeof(struct babel_tlv_ihu);
goto out;
}
put_ip6_ll(&tlv->addr, msg->addr);
tlv->ae = BABEL_AE_IP6_LL;
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
@ -1200,6 +1251,66 @@ babel_write_source_prefix(struct babel_tlv *hdr, net_addr *n, uint max_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
babel_read_subtlvs(struct babel_tlv *hdr,
union babel_msg *msg,
@ -1318,7 +1429,6 @@ babel_send_to(struct babel_iface *ifa, ip_addr dest)
static uint
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 };
if (EMPTY_LIST(*queue))
@ -1346,7 +1456,7 @@ babel_write_queue(struct babel_iface *ifa, list *queue)
pos += len;
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);
@ -1470,6 +1580,13 @@ babel_process_packet(struct babel_iface *ifa,
.saddr = saddr,
.next_hop_ip6 = saddr,
.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))
@ -1507,13 +1624,13 @@ babel_process_packet(struct babel_iface *ifa,
else if (res == PARSE_IGNORE)
{
DBG("Babel: Ignoring TLV of type %d\n", tlv->type);
sl_free(p->msg_slab, msg);
sl_free(msg);
}
else /* 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));
sl_free(p->msg_slab, msg);
sl_free(msg);
break;
}
}
@ -1525,7 +1642,7 @@ babel_process_packet(struct babel_iface *ifa,
if (tlv_data[msg->msg.type].handle_tlv)
tlv_data[msg->msg.type].handle_tlv(&msg->msg, ifa);
rem_node(NODE msg);
sl_free(p->msg_slab, msg);
sl_free(msg);
}
}

View file

@ -3,4 +3,4 @@ obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -113,8 +113,8 @@
#define HASH_IP_EQ(a1,n1,a2,n2) ipa_equal(a1, a2) && n1 == n2
#define HASH_IP_FN(a,n) ipa_hash(a) ^ u32_hash(n)
static list bfd_proto_list;
static list bfd_wait_list;
static list STATIC_LIST_INIT(bfd_proto_list);
static list STATIC_LIST_INIT(bfd_wait_list);
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_ip, HASH_IP, s);
sl_free(p->session_slab, s);
sl_free(s);
TRACE(D_EVENTS, "Session to %I removed", ip);
@ -1007,13 +1007,6 @@ bfd_notify_init(struct bfd_proto *p)
* BFD protocol glue
*/
void
bfd_init_all(void)
{
init_list(&bfd_proto_list);
init_list(&bfd_wait_list);
}
static struct proto *
bfd_init(struct proto_config *c)
{
@ -1199,3 +1192,9 @@ struct protocol proto_bfd = {
.reconfigure = bfd_reconfigure,
.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);
tmp_init(loop->pool);
pthread_mutex_lock(&loop->mutex);
while (1)
{

View file

@ -3,4 +3,4 @@ obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)
tests_objs := $(tests_objs) $(src-o-files)

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; })
#define UNSET(a) \
({ a->type = EAF_TYPE_UNDEF; return; })
({ a->undef = 1; return; })
#define REJECT(msg, args...) \
({ 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;
}
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
@ -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);
}
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
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,
.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] = {
.name = "mpls_label_stack",
.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;
/* 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_PARTIAL;
@ -1154,7 +1180,7 @@ bgp_export_attr(struct bgp_export_state *s, eattr *a, ea_list *to)
CALL(desc->export, s, a);
/* Attribute might become undefined in hook */
if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
if (a->undef)
return;
}
else
@ -1445,6 +1471,29 @@ bgp_finish_attrs(struct bgp_parse_state *s, rta *a)
REPORT("Discarding AIGP attribute received on non-AIGP session");
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);
if (c->prefix_slab)
sl_free(c->prefix_slab, px);
sl_free(px);
else
mb_free(px);
}
@ -1669,12 +1718,16 @@ bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px)
*/
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->attrs->src->proto;
struct bgp_proto *p = (struct bgp_proto *) P;
struct proto *SRC = e->src->proto;
struct bgp_proto *p = (struct bgp_proto *) C->proto;
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 */
if (src == p)
@ -1702,11 +1755,11 @@ bgp_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
}
/* Handle well-known communities, RFC 1997 */
struct eattr *c;
struct eattr *a;
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 */
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;
}
/* 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;
}
static ea_list *
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_export_state s = { .proto = p, .channel = c, .pool = pool, .src = src, .route = e, .mpls = c->desc->mpls };
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 */
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);
}
@ -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
* 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;
u32 path;
/* Ignore non-BGP channels */
if (C->channel != &channel_bgp)
return;
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 */
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 */
buck = attrs ? bgp_get_bucket(c, attrs) : bgp_get_withdraw_bucket(c);
path = new->attrs->src->global_id;
lp_flush(bgp_linpool2);
path = new->src->global_id;
}
else
{
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);
@ -1890,34 +1965,44 @@ bgp_get_neighbor(rte *r)
return as;
/* 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;
}
static inline int
rte_stale(rte *r)
{
if (r->u.bgp.stale < 0)
{
/* 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);
}
if (r->pflags & BGP_REF_STALE)
return 1;
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
bgp_rte_better(rte *new, rte *old)
{
struct bgp_proto *new_bgp = (struct bgp_proto *) new->attrs->src->proto;
struct bgp_proto *old_bgp = (struct bgp_proto *) old->attrs->src->proto;
struct bgp_proto *new_bgp = (struct bgp_proto *) new->src->proto;
struct bgp_proto *old_bgp = (struct bgp_proto *) old->src->proto;
eattr *x, *y;
u32 n, o;
/* Skip suppressed routes (see bgp_rte_recalculate()) */
n = new->u.bgp.suppressed;
o = old->u.bgp.suppressed;
n = new->pflags & BGP_REF_SUPPRESSED;
o = old->pflags & BGP_REF_SUPPRESSED;
if (n > o)
return 0;
if (n < o)
@ -2055,13 +2140,13 @@ bgp_rte_better(rte *new, rte *old)
int
bgp_rte_mergable(rte *pri, rte *sec)
{
struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->attrs->src->proto;
struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->attrs->src->proto;
struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->src->proto;
struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->src->proto;
eattr *x, *y;
u32 p, s;
/* 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;
/* RFC 4271 9.1.2.1. Route resolvability test */
@ -2134,13 +2219,13 @@ bgp_rte_mergable(rte *pri, rte *sec)
static inline int
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
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;
}
@ -2149,9 +2234,9 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
{
rte *r, *s;
rte *key = new ? new : old;
u32 lpref = key->pref;
u32 lpref = key->attrs->pref;
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
@ -2203,11 +2288,11 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
*/
if (new)
new->u.bgp.suppressed = 1;
new->pflags |= BGP_REF_SUPPRESSED;
if (old)
{
old->u.bgp.suppressed = 1;
old->pflags |= BGP_REF_SUPPRESSED;
/* The fast case - replace not best with worse (or remove not best) */
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)
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))
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 */
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 */
for (s=net->routes; rte_is_valid(s); s=s->next)
if (use_deterministic_med(s) && same_group(s, lpref, lasn))
if ((s != r) && bgp_rte_mergable(r, s))
s->u.bgp.suppressed = 0;
s->pflags &= ~BGP_REF_SUPPRESSED;
/* Found best-in-group */
r->u.bgp.suppressed = 0;
r->pflags &= ~BGP_REF_SUPPRESSED;
/*
* 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);
bgp_set_attr_ptr(&(r->attrs->eattrs), pool, BA_COMMUNITY, flags,
int_set_add(pool, ad, BGP_COMM_LLGR_STALE));
r->u.bgp.stale = 1;
r->pflags |= BGP_REF_STALE;
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));
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, "-");
if (rte_stale(e))

View file

@ -102,6 +102,7 @@
* RFC 8212 - Default EBGP Route Propagation Behavior without Policies
* RFC 8654 - Extended Message Support for BGP
* 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-uttaro-idr-bgp-persistence-04
* draft-walton-bgp-hostname-capability-02
@ -126,9 +127,7 @@
#include "bgp.h"
struct linpool *bgp_linpool; /* Global temporary pool */
struct linpool *bgp_linpool2; /* Global temporary pool for bgp_rt_notify() */
static list bgp_sockets; /* Global list of listening sockets */
static list STATIC_LIST_INIT(bgp_sockets); /* Global list of listening sockets */
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 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 */
WALK_LIST(bs, bgp_sockets)
@ -204,12 +199,6 @@ bgp_open(struct bgp_proto *p)
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;
err:
@ -238,15 +227,6 @@ bgp_close(struct bgp_proto *p)
rfree(bs->sk);
rem_node(&bs->n);
mb_free(bs);
if (!EMPTY_LIST(bgp_sockets))
return;
rfree(bgp_linpool);
bgp_linpool = NULL;
rfree(bgp_linpool2);
bgp_linpool2 = NULL;
}
static inline int
@ -280,7 +260,7 @@ static inline struct bgp_channel *
bgp_find_channel(struct bgp_proto *p, u32 afi)
{
struct bgp_channel *c;
WALK_LIST(c, p->p.channels)
BGP_WALK_CHANNELS(p, c)
if (c->afi == afi)
return c;
@ -606,7 +586,7 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
/* Summary state of ADD_PATH RX for active channels */
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 *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->summary_add_path_rx = summary_add_path_rx;
WALK_LIST(c, p->p.channels)
BGP_WALK_CHANNELS(p, c)
{
if (c->c.disabled)
continue;
@ -767,7 +747,7 @@ bgp_handle_graceful_restart(struct bgp_proto *p)
p->gr_active_num = 0;
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? */
if (c->c.disabled)
@ -862,7 +842,7 @@ bgp_graceful_restart_timeout(timer *t)
if (p->llgr_ready)
{
struct bgp_channel *c;
WALK_LIST(c, p->p.channels)
BGP_WALK_CHANNELS(p, c)
{
/* Channel is not in GR and is already flushed */
if (!c->gr_active)
@ -1414,6 +1394,10 @@ bgp_reload_routes(struct channel *C)
struct bgp_proto *p = (void *) C->proto;
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));
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_channel *c = (void *) C;
/* Ignore non-BGP channels */
if (C->channel != &channel_bgp)
return;
/* This should not happen */
if (!p->conn)
return;
@ -1453,6 +1441,10 @@ bgp_feed_end(struct channel *C)
struct bgp_proto *p = (void *) C->proto;
struct bgp_channel *c = (void *) C;
/* Ignore non-BGP channels */
if (C->channel != &channel_bgp)
return;
/* This should not happen */
if (!p->conn)
return;
@ -1567,7 +1559,7 @@ bgp_start(struct proto *P)
if (p->p.gr_recovery && p->cf->gr_mode)
{
struct bgp_channel *c;
WALK_LIST(c, p->p.channels)
BGP_WALK_CHANNELS(p, 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_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL;
P->rte_modify = bgp_rte_modify_stale;
P->rte_igp_metric = bgp_rte_igp_metric;
p->cf = cf;
p->is_internal = (cf->local_as == cf->remote_as);
@ -1720,7 +1713,7 @@ bgp_init(struct proto_config *CF)
/* Add all channels */
struct bgp_channel_config *cc;
WALK_LIST(cc, CF->channels)
BGP_CF_WALK_CHANNELS(cf, cc)
proto_add_channel(P, &cc->c);
return P;
@ -1866,7 +1859,7 @@ bgp_find_channel_config(struct bgp_config *cf, u32 afi)
{
struct bgp_channel_config *cc;
WALK_LIST(cc, cf->c.channels)
BGP_CF_WALK_CHANNELS(cf, cc)
if (cc->afi == afi)
return cc;
@ -1983,6 +1976,15 @@ bgp_postconfig(struct proto_config *CF)
if (internal && cf->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)
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)
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;
WALK_LIST(cc, CF->channels)
BGP_CF_WALK_CHANNELS(cf, cc)
{
/* Handle undefined import filter */
if (cc->c.in_filter == FILTER_UNDEF)
@ -2035,6 +2052,10 @@ bgp_postconfig(struct proto_config *CF)
if (!cc->gw_mode)
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 */
if (cc->gr_able == 0xff)
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)
C->stale = 1;
WALK_LIST(cc, new->c.channels)
BGP_CF_WALK_CHANNELS(new, cc)
{
C = (struct channel *) bgp_find_channel(p, cc->afi);
same = proto_configure_channel(P, &C, &cc->c) && same;
if (C)
C->stale = 0;
}
WALK_LIST_DELSAFE(C, C2, p->p.channels)
if (C->stale)
same = proto_configure_channel(P, &C, NULL) && same;
if (same && (p->start_state > BSS_PREPARE))
bgp_update_bfd(p, new->bfd);
@ -2169,6 +2186,7 @@ bgp_channel_reconfigure(struct channel *C, struct channel_config *CC, int *impor
return 0;
if ((new->gw_mode != old->gw_mode) ||
(new->next_hop_prefer != old->next_hop_prefer) ||
(new->aigp != old->aigp) ||
(new->cost != old->cost))
{
@ -2345,6 +2363,15 @@ bgp_show_afis(int code, char *s, u32 *afis, uint count)
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
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)
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
@ -2560,6 +2590,9 @@ bgp_show_proto_info(struct proto *P)
{
channel_show_info(&c->c);
if (c->c.channel != &channel_bgp)
continue;
if (p->gr_active_num)
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,
.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/socket.h"
struct linpool;
struct eattr;
@ -114,13 +113,18 @@ struct bgp_config {
int gr_mode; /* Graceful restart mode (BGP_GR_*) */
int llgr_mode; /* Long-lived graceful restart mode (BGP_LLGR_*) */
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 */
unsigned gr_time; /* Graceful restart timeout */
unsigned llgr_time; /* Long-lived graceful restart stale time */
unsigned connect_delay_time; /* Minimum delay between 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 min_keepalive_time; /* Minimum accepted keepalive time */
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_max;
@ -144,6 +148,7 @@ struct bgp_channel_config {
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_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 gw_mode; /* How we compute route gateway from next_hop attr, see GW_* */
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_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_ALL 1
#define NH_IBGP 2
@ -179,6 +191,9 @@ struct bgp_channel_config {
#define GW_DIRECT 1
#define GW_RECURSIVE 2
#define NHP_GLOBAL 1
#define NHP_LOCAL 2
#define BGP_ADD_PATH_RX 1
#define BGP_ADD_PATH_TX 2
#define BGP_ADD_PATH_FULL 3
@ -203,6 +218,10 @@ struct bgp_channel_config {
#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 {
u32 afi;
@ -223,6 +242,7 @@ struct bgp_caps {
u8 ext_messages; /* Extended message length, RFC draft */
u8 route_refresh; /* Route refresh capability, RFC 2918 */
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_flags; /* Graceful restart flags */
@ -454,7 +474,6 @@ struct bgp_parse_state {
jmp_buf err_jmpbuf;
struct hostentry *hostentry;
struct rtable *base_table;
adata *mpls_labels;
/* Cached state for bgp_rte_update() */
@ -473,6 +492,9 @@ struct bgp_parse_state {
#define BGP_RX_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)
{ 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)
{ 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)
{ 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);
}
extern struct linpool *bgp_linpool;
extern struct linpool *bgp_linpool2;
void bgp_start_timer(timer *t, uint value);
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);
}
static inline void
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; }
#define bgp_unset_attr(to, pool, code) ea_unset_attr(to, pool, 0, code)
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_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best);
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);
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);
void bgp_get_route_info(struct rte *, byte *buf);
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_AIGP 0x1a /* RFC 7311 */
#define BA_LARGE_COMMUNITY 0x20 /* RFC 8092 */
#define BA_ONLY_TO_CUSTOMER 0x23 /* RFC 9234 */
/* Bird's private internal BGP attributes */
#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,
LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS,
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 <i32> bgp_afi
@ -40,7 +41,7 @@ CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION,
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
@ -72,6 +73,7 @@ bgp_proto_start: proto_start BGP {
BGP_CFG->llgr_mode = -1;
BGP_CFG->llgr_time = 3600;
BGP_CFG->setkey = 1;
BGP_CFG->local_role = BGP_ROLE_UNDEFINED;
BGP_CFG->dynamic_name = "dynbgp";
BGP_CFG->check_link = -1;
}
@ -114,6 +116,14 @@ bgp_cease_flag:
| 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_start proto_name '{'
| bgp_proto proto_item ';'
@ -143,7 +153,8 @@ bgp_proto:
| bgp_proto RS CLIENT bool ';' { BGP_CFG->rs_client = $4; }
| bgp_proto CONFEDERATION expr ';' { BGP_CFG->confederation = $3; }
| 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 DIRECT ';' { BGP_CFG->multihop = 0; }
| 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 CONNECT DELAY TIME expr ';' { BGP_CFG->connect_delay_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 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; }
@ -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 { 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 LOCAL ROLE bgp_role_name ';' { BGP_CFG->local_role = $4; }
| bgp_proto REQUIRE ROLES bool ';' { BGP_CFG->require_roles = $4; }
;
bgp_afi:
@ -251,6 +265,7 @@ bgp_channel_item:
| NEXT HOP ADDRESS ipa { BGP_CC->next_hop_addr = $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 PREFER GLOBAL { BGP_CC->next_hop_prefer = NHP_GLOBAL; }
| 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); }
| 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)); } ;
dynamic_attr: BGP_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->route_refresh = p->cf->enable_refresh;
caps->enhanced_refresh = p->cf->enable_refresh;
caps->role = p->cf->local_role;
if (caps->as4_support)
caps->as4_number = p->public_as;
@ -261,7 +262,7 @@ bgp_prepare_capabilities(struct bgp_conn *conn)
}
/* 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->afi = c->afi;
@ -350,6 +351,13 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
*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)
{
*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_caps *caps;
struct bgp_af_caps *ac;
uint err_subcode = 0;
int i, cl;
u32 af;
if (!conn->remote_caps)
{
caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + sizeof(struct bgp_af_caps));
caps->role = BGP_ROLE_UNDEFINED;
}
else
{
caps = conn->remote_caps;
@ -513,6 +525,21 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
caps->ext_messages = 1;
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 */
if (cl % 4 != 2)
goto err;
@ -638,7 +665,7 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
err:
mb_free(caps);
bgp_error(conn, 2, 0, NULL, 0);
bgp_error(conn, 2, err_subcode, NULL, 0);
return -1;
}
@ -654,7 +681,7 @@ bgp_check_capabilities(struct bgp_conn *conn)
/* This is partially overlapping with bgp_conn_enter_established_state(),
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 *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)
return;
/* RFC 4271 4.2 - hold time must be either 0 or at least 3 */
if (hold > 0 && hold < 3)
{ 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 */
if (!id || (p->is_internal && id == p->local_id))
{ 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;
}
/* 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 */
other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn;
switch (other->state)
@ -904,8 +963,8 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
}
/* Update our local variables */
conn->hold_time = MIN(hold, p->cf->hold_time);
conn->keepalive_time = p->cf->keepalive_time ? : conn->hold_time / 3;
conn->hold_time = hold_time;
conn->keepalive_time = keepalive_time;
conn->as4_session = conn->local_caps->as4_support && caps->as4_support;
conn->ext_messages = conn->local_caps->ext_messages && caps->ext_messages;
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");
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)
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);
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 */
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;
/* Keep it when explicitly set in export filter */
if (a->type & EAF_FRESH)
if (a->fresh)
return 1;
/* Check for non-matching AF */
if ((ipa_is_ip4(*nh) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop)
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 */
if (p->is_interior && ipa_nonzero(*nh))
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)
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 */
if (p->is_interior)
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;
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
{
@ -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;
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 */
if (s->cached_rta == NULL)
{
a0->src = s->last_src;
/* Workaround for rta_lookup() breaking eattrs */
ea_list *ea = a0->eattrs;
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);
rte *e = rte_get_temp(a);
rte *e = rte_get_temp(a, s->last_src);
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);
}
@ -2331,11 +2395,14 @@ bgp_create_update(struct bgp_channel *c, byte *buf)
again: ;
struct lp_state tmpp;
lp_save(tmp_linpool, &tmpp);
/* Initialize write state */
struct bgp_write_state s = {
.proto = p,
.channel = c,
.pool = bgp_linpool,
.pool = tmp_linpool,
.mp_reach = (c->afi != BGP_AF_IPV4) || c->ext_next_hop,
.as4_session = p->as4_session,
.add_path = c->add_path_tx,
@ -2361,6 +2428,7 @@ again: ;
if (EMPTY_LIST(buck->prefixes))
{
bgp_free_bucket(c, buck);
lp_restore(tmp_linpool, &tmpp);
goto again;
}
@ -2374,7 +2442,10 @@ again: ;
bgp_defer_bucket(c, buck);
if (!res)
{
lp_restore(tmp_linpool, &tmpp);
goto again;
}
goto done;
}
@ -2385,7 +2456,7 @@ again: ;
done:
BGP_TRACE_RL(&rl_snd_update, D_PACKETS, "Sending UPDATE");
p->stats.tx_updates++;
lp_flush(s.pool);
lp_restore(tmp_linpool, &tmpp);
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_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
* 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->from = s->proto->remote_ip;
a->eattrs = ea;
a->pref = c->c.preference;
c->desc->decode_next_hop(s, nh, nh_len, 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);
struct lp_state tmpp;
lp_save(tmp_linpool, &tmpp);
/* Initialize parse state */
struct bgp_parse_state s = {
.proto = p,
.pool = bgp_linpool,
.pool = tmp_linpool,
.as4_session = p->as4_session,
};
@ -2596,7 +2669,7 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
done:
rta_free(s.cached_rta);
lp_flush(s.pool);
lp_restore(tmp_linpool, &tmpp);
return;
}
@ -2984,6 +3057,7 @@ static struct {
{ 2, 6, "Unacceptable hold time" },
{ 2, 7, "Required capability missing" }, /* [RFC5492] */
{ 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, 1, "Malformed attribute list" },
{ 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)
{
/* Bad peer AS - we would like to print the AS */
if ((code == 2) && (subcode == 2) && ((len == 2) || (len == 4)))
/* Bad peer AS / unacceptable hold time - print the value as decimal number */
if ((code == 2) && ((subcode == 2) || (subcode == 6)) && ((len == 2) || (len == 4)))
{
t += bsprintf(t, ": %u", (len == 2) ? get_u16(data) : get_u32(data));
goto done;

View file

@ -3,4 +3,4 @@ obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -472,9 +472,9 @@ mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r)
#ifdef CONFIG_BGP
/* 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 =
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 */
if (s->add_path)
mrt_put_u32(b, r->attrs->src->private_id);
mrt_put_u32(b, r->src->private_id);
/* Route Attributes */
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;
/* 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;
continue;
}
rte_make_tmp_attrs(&rt, s->linpool, NULL);
if (f_run(s->filter, &rt, s->linpool, 0) <= F_ACCEPT)
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));
s->pool = pool;
s->linpool = lp_new(pool, 4080);
s->peer_lp = lp_new(pool, 4080);
s->linpool = lp_new(pool);
s->peer_lp = lp_new(pool);
mrt_buffer_init(&s->buf, pool, 2 * MRT_ATTR_BUFFER_SIZE);
/* We lock the current config as we may reference it indirectly by filter */
@ -918,3 +916,9 @@ struct protocol proto_mrt = {
.reconfigure = mrt_reconfigure,
.copy_config = mrt_copy_config,
};
void
mrt_build(void)
{
proto_build(&proto_mrt);
}

View file

@ -3,4 +3,4 @@ obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -522,7 +522,10 @@ static inline void
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));
n->n = (node) {};
add_tail(&ifa->nbma_list, NODE n);
n->ip = src->ip;
n->eligible = src->eligible;
n->found = found;

View file

@ -107,12 +107,10 @@
#include <stdlib.h>
#include "ospf.h"
static int ospf_preexport(struct proto *P, rte **new, struct linpool *pool);
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 int ospf_preexport(struct channel *P, rte *new);
static void ospf_reload_routes(struct channel *C);
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);
@ -301,7 +299,7 @@ ospf_start(struct proto *P)
p->lsab_size = 256;
p->lsab_used = 0;
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->area_list));
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->feed_begin = ospf_feed_begin;
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_same = ospf_rte_same;
P->rte_igp_metric = ospf_rte_igp_metric;
return P;
}
@ -390,7 +386,9 @@ ospf_init(struct proto_config *CF)
static int
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;
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->u.ospf.metric2 < old->u.ospf.metric2) return 1;
if(new->u.ospf.metric2 > old->u.ospf.metric2) return 0;
u32 old_metric2 = ea_get_int(old->attrs->eattrs, EA_OSPF_METRIC2, LSINFINITY);
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 0; /* Old is shorter or same */
}
static int
ospf_rte_same(struct rte *new, struct rte *old)
static u32
ospf_rte_igp_metric(struct rte *rt)
{
/* new->attrs == old->attrs always */
return
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;
}
if (rt->attrs->source == RTS_OSPF_EXT2)
return IGP_METRIC_UNKNOWN;
return ea_get_int(rt->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
}
void
ospf_schedule_rtcalc(struct ospf_proto *p)
@ -484,14 +482,13 @@ ospf_disp(timer * timer)
* import to the filters.
*/
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);
rte *e = *new;
/* Reject our own routes */
if (e->attrs->src->proto == P)
if (e->src->proto == &p->p)
return -1;
/* Do not export routes to stub areas */
@ -501,26 +498,6 @@ ospf_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
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
* @P: OSPF protocol instance
@ -558,6 +535,9 @@ ospf_shutdown(struct proto *P)
}
FIB_WALK_END;
if (tm_active(p->disp_timer))
tm_stop(p->disp_timer);
return PS_DOWN;
}
@ -607,16 +587,20 @@ ospf_get_route_info(rte * rte, byte * buf)
}
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)
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, ")");
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
@ -1550,3 +1534,9 @@ struct protocol proto_ospf = {
.get_attr = ospf_get_attr,
.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;
if (old->type == RTS_DUMMY)
if (!old->type)
return 1;
/* 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;
if (old->type == RTS_DUMMY)
if (!old->type)
return 1;
if (!p->rfc1583)
@ -225,7 +225,7 @@ orta_compare_ext(const struct ospf_proto *p, const orta *new, const orta *old)
{
int r;
if (old->type == RTS_DUMMY)
if (!old->type)
return 1;
/* 16.4 (6a) - prefer routes with lower type */
@ -2053,32 +2053,54 @@ again1:
if (nf->n.type) /* Add the route */
{
rta a0 = {
.src = p->p.main_source,
.source = nf->n.type,
.scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST,
.nh = *(nf->n.nhs),
.pref = p->p.main_channel->preference,
};
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);
rte *e = rte_get_temp(a);
rte *e = rte_get_temp(a, p->p.main_source);
rta_free(nf->old_rta);
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",
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)
{
*ee = e->next;
sl_free(f->hash_slab, e);
sl_free(e);
if (f->hash_entries-- < f->hash_entries_min)
ospf_top_rehash(f, -HASH_LO_STEP);
return;

View file

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

View file

@ -3,4 +3,4 @@ obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -43,6 +43,10 @@
#include "pipe.h"
#ifdef CONFIG_BGP
#include "proto/bgp/bgp.h"
#endif
static void
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)
{
src = new->src;
a = alloca(rta_size(new->attrs));
memcpy(a, new->attrs, rta_size(new->attrs));
a->aflags = 0;
a->cached = 0;
a->hostentry = NULL;
e = rte_get_temp(a);
e->pflags = 0;
/* Copy protocol specific embedded attributes. */
memcpy(&(e->u), &(new->u), sizeof(e->u));
e->pref = new->pref;
e = rte_get_temp(a, src);
e->pflags = new->pflags;
#ifdef CONFIG_BGP
/* Hack to cleanup cached value */
if (e->attrs->src->proto->proto == &proto_bgp)
{
e->u.bgp.stale = -1;
e->u.bgp.base_table = NULL;
}
if (e->src->proto->proto == &proto_bgp)
e->pflags &= ~(BGP_REF_STALE | BGP_REF_NOT_STALE);
#endif
src = a->src;
}
else
{
e = NULL;
src = old->attrs->src;
src = old->src;
}
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
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 0;
@ -307,3 +303,9 @@ struct protocol proto_pipe = {
.get_status = pipe_get_status,
.show_proto_info = pipe_show_proto_info
};
void
pipe_build(void)
{
proto_build(&proto_pipe);
}

View file

@ -3,4 +3,4 @@ obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -391,12 +391,12 @@ radv_net_match_trigger(struct radv_config *cf, net *n)
}
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_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;
if (cf->propagate_routes)
@ -555,7 +555,7 @@ radv_check_active(struct radv_proto *p)
return 1;
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
@ -771,3 +771,9 @@ struct protocol proto_radv = {
.get_status = radv_get_status,
.get_attr = radv_get_attr
};
void
radv_build(void)
{
proto_build(&proto_radv);
}

View file

@ -3,4 +3,4 @@ obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -108,14 +108,14 @@ rip_add_rte(struct rip_proto *p, struct rip_rte **rp, struct rip_rte *src)
}
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;
rip_unlock_neighbor(rt->from);
*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)
@ -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)
{ 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
* @p: RIP instance
@ -145,7 +150,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
{
/* Update */
rta a0 = {
.src = p->p.main_source,
.pref = p->p.main_channel->preference,
.source = RTS_RIP,
.scope = SCOPE_UNIVERSE,
.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;
}
rta *a = rta_lookup(&a0);
rte *e = rte_get_temp(a);
struct {
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;
e->u.rip.metric = rt_metric;
e->u.rip.tag = rt_tag;
e->pflags = EA_ID_FLAG(EA_RIP_METRIC) | EA_ID_FLAG(EA_RIP_TAG);
a0.eattrs = &ea_block.l;
rta *a = rta_lookup(&a0);
rte *e = rte_get_temp(a, p->p.main_source);
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)
{
/* 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_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)
{
@ -339,7 +372,7 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
en->valid = RIP_ENTRY_VALID;
en->metric = rt_metric;
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->next_hop = new->attrs->nh.gw;
}
@ -1068,37 +1101,18 @@ rip_reload_routes(struct channel *C)
rip_kick_timer(p);
}
static void
rip_make_tmp_attrs(struct rte *rt, struct linpool *pool)
static u32
rip_rte_igp_metric(struct rte *rt)
{
rte_init_tmp_attrs(rt, pool, 2);
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);
return ea_get_int(rt->attrs->eattrs, EA_RIP_METRIC, IGP_METRIC_UNKNOWN);
}
static int
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
rip_postconfig(struct proto_config *CF)
{
@ -1120,10 +1134,8 @@ rip_init(struct proto_config *CF)
P->rt_notify = rip_rt_notify;
P->neigh_notify = rip_neigh_notify;
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_same = rip_rte_same;
P->rte_igp_metric = rip_rte_igp_metric;
return P;
}
@ -1198,10 +1210,14 @@ rip_reconfigure(struct proto *P, struct proto_config *CF)
static void
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)
bsprintf(buf, " [%04x]", rte->u.rip.tag);
buf += bsprintf(buf, " (%d/%d)", rte->attrs->pref, rt_metric);
if (rt_tag)
bsprintf(buf, " [%04x]", rt_tag);
}
static int
@ -1338,3 +1354,9 @@ struct protocol proto_rip = {
.get_route_info = rip_get_route_info,
.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_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)
{ return p->rip2; }

View file

@ -3,4 +3,4 @@ obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)
tests_objs := $(tests_objs) $(src-o-files)

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;
rta a0 = {
.src = p->p.main_source,
.pref = channel->preference,
.source = RTS_RPKI,
.scope = SCOPE_UNIVERSE,
.dest = RTD_NONE,
};
rta *a = rta_lookup(&a0);
rte *e = rte_get_temp(a);
rte *e = rte_get_temp(a, p->p.main_source);
e->pflags = 0;
rte_update2(channel, &pfxr->n, e, a0.src);
rte_update2(channel, &pfxr->n, e, e->src);
}
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:
/* 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:
/* 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;
case RPKI_CS_ERROR_FATAL:
@ -453,6 +454,11 @@ rpki_retry_hook(timer *tm)
}
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:
rpki_cache_change_state(cache, RPKI_CS_CONNECTING);
break;
@ -960,3 +966,9 @@ struct protocol proto_rpki = {
.reconfigure = rpki_reconfigure,
.get_status = rpki_get_status,
};
void
rpki_build(void)
{
proto_build(&proto_rpki);
}

View file

@ -3,4 +3,4 @@ obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -40,7 +40,7 @@ static_route_finish(void)
if (net_type_match(this_srt->net, NB_DEST) == !this_srt->dest)
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

View file

@ -56,10 +56,11 @@ static void
static_announce_rte(struct static_proto *p, struct static_route *r)
{
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->scope = SCOPE_UNIVERSE;
a->dest = r->dest;
a->pref = p->p.main_channel->preference;
if (r->dest == RTD_UNICAST)
{
@ -102,7 +103,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
return;
/* We skip rta_lookup() here */
rte *e = rte_get_temp(a);
rte *e = rte_get_temp(a, src);
e->pflags = 0;
if (r->cmds)
@ -119,7 +120,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
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;
if (r->cmds)
@ -131,7 +132,7 @@ withdraw:
if (r->state == SRS_DOWN)
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;
}
@ -485,7 +486,7 @@ static_start(struct proto *P)
struct static_route *r;
if (!static_lp)
static_lp = lp_new(&root_pool, LP_GOOD_SIZE(1024));
static_lp = lp_new(&root_pool);
if (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);
if (a)
buf += bsprintf(buf, " (%d/%u)", rte->pref, a->u.data);
buf += bsprintf(buf, " (%d/%u)", rte->attrs->pref, a->u.data);
else
buf += bsprintf(buf, " (%d)", rte->pref);
buf += bsprintf(buf, " (%d)", rte->attrs->pref);
}
static void
@ -792,3 +793,9 @@ struct protocol proto_static = {
.copy_config = static_copy_config,
.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)
{
uint len = OFFSETOF(struct sockaddr_dl, sdl_data);
/* Workaround for FreeBSD 13.0 */
len = MAX(len, sizeof(struct sockaddr));
memset(sa, 0, len);
sa->sdl_len = len;
sa->sdl_family = AF_LINK;
@ -347,7 +351,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
}
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;
@ -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);
rta a = {
.src = p->p.main_source,
.source = RTS_INHERIT,
.scope = SCOPE_UNIVERSE,
};
@ -580,18 +583,23 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
}
done:
e = rte_get_temp(&a);
e = rte_get_temp(&a, p->p.main_source);
e->net = net;
e->u.krt.src = src;
e->u.krt.proto = src2;
e->u.krt.seen = 0;
e->u.krt.best = 0;
e->u.krt.metric = 0;
ea_list *ea = alloca(sizeof(ea_list) + 1 * sizeof(eattr));
*ea = (ea_list) { .count = 1, .next = e->attrs->eattrs };
e->attrs->eattrs = ea;
ea->attrs[0] = (eattr) {
.id = EA_KRT_SOURCE,
.type = EAF_TYPE_INT,
.u.data = src2,
};
if (scan)
krt_got_route(p, e);
krt_got_route(p, e, src);
else
krt_got_route_async(p, e, new);
krt_got_route_async(p, e, new, src);
}
static void
@ -1198,7 +1206,7 @@ kif_update_sysdep_addr(struct iface *i)
return 0;
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);
}

View file

@ -4,7 +4,6 @@ Available configuration variables:
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_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_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_SELF_CONSCIOUS
#define CONFIG_MULTIPLE_TABLES
#define CONFIG_ALL_TABLES_AT_ONCE
#define CONFIG_IP6_SADR_KERNEL
#define CONFIG_MC_PROPER_SRC
@ -18,9 +17,12 @@
#define CONFIG_INCLUDE_SYSIO_H "sysdep/linux/sysio.h"
#define CONFIG_INCLUDE_KRTSYS_H "sysdep/linux/krt-sys.h"
#define CONFIG_LINUX_NETLINK
#define CONFIG_RESTRICTED_PRIVILEGES
#define CONFIG_INCLUDE_SYSPRIV_H "sysdep/linux/syspriv.h"
#define CONFIG_MADV_DONTNEED_TO_FREE
#ifndef AF_MPLS
#define AF_MPLS 28

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