0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

Merge branch 'thread-next' of gitlab.nic.cz:labs/bird into thread-next

This commit is contained in:
Katerina Kubecova 2024-12-03 15:48:33 +01:00
commit b8d3f41d5d
80 changed files with 2011 additions and 368 deletions

2
.mailmap Normal file
View File

@ -0,0 +1,2 @@
Maria Matejka <mq@ucw.cz>
Maria Matejka <mq@jmq.cz>

109
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,109 @@
# Contributing to BIRD
We welcome a broad range of contributions to BIRD with some limitations and
caveats.
BIRD is highly optimized for performance in both memory and computation time.
We generally don't accept obviously inefficient code and even though the
quality of the existing codebase quite varies, there should be good reasons
why to commit something slow or greedy.
There are several basic rules for contributing:
- your branch must have understandable commit messages
- your branch must be either:
- rooted in the current thread-next, aiming for inclusion in BIRD 3
- or rooted in the master branch; in this case, we may refuse your patch
if it's completely unmergeable with thread-next
- when incorporating proposed fixes, you may have to rebase your branch
- please add automatic tests (see below)
- upfront and continuous consultation with the development team gives you a
fast track for merging
- don't forget to update documentation
## Security issues
Please contact us on bird-support@network.cz for private disclosure of any
security issues. This includes any crash in or related to filters, induced by
CLI or by receiving a malformed message by a protocol.
## How to contribute
You can either send a patch (prepared by git format-patch) to our mailing-list
bird-users@network.cz, or you can send just a link to your repository and the
commit hash you're contributing.
## What if your contribution isn't mergable
If your code needs minor updates to align with our standards / taste, we'll
just do these modifications ourselves and either add these as a separate commit
or just update your commit noting this fact in the commit message.
If your code has some major flaws, misses the point or introduces another
problem (e.g. performance issues), we'll refuse your patch. Then we'll either
try to tell you how we prefer to reach the goal, or we may reimplement your
ideas ourselves. We'll mention your original contribution in the commit message.
## Specific kinds of contributions
### Substantial updates
If you feel like the BIRD internals need some major changes and you wish to
implement it, please contact the development team first. We're (as of May 2024)
developing two versions at once and we have some raw thoughts about BIRD's future
which we haven't published yet.
Beware that BIRD is more convoluted inside than it looks like on the surface,
and in many places the learning curve is _very_ steep.
### New protocol implementations
We generally welcome broadening of BIRD capabilities. Upfront consultation is
very much appreciated to align all parties on the development principles,
internal APIs, coding style and more.
### Refactoring and reformatting
Please don't send us _any_ refactoring proposals without previous explicit approval.
### Programmer's documentation, user documentation or tutorials
We welcome updates to enhance the documentation, including the algorithmic
principles, internal libraries and API. We keep our right to reject low quality
contributions altogether.
### Minor changes
Feel free to propose minor fixes in any part of BIRD.
## Testing
There is another repository, https://gitlab.nic.cz/labs/bird-tools.git, where
we store our automatic tests in the netlab/ directory. This repository is quite
messy and you may need some help with it. We're planning to move the Netlab
suite into the main git repository; after we do that, we'll require every
contribution to add tests (if applicable, of course).
## Crediting policy
The credits are scattered over all the source code files; in the commentary
section, you may find typically the original authors of these files or some
major contributors who felt like adding their names there. Overall, if you feel
like your name should be there, include this change in your commits please.
If your name should be changed, please do that change in the source code files.
If your name should be changed in the displayed git commit author / commiter
logs, please submit a patch on the `.mailmap` file.
We are planning to centralize the credits one day; we'll then update this file
accordingly.
## Meta
If some of these rules are breached, you may complain either at the mailing
list, or directly to CZ.NIC who is currently BIRD's maintainer.
If we don't reply within 3 weeks, please ping us. We don't intend to ghost you,
we are just overloaded.
This contributing policy also applies to itself.

15
README
View File

@ -68,12 +68,23 @@ User support
If you want to help us debugging, enhancing and porting BIRD or just lurk
around to see what's going to develop, feel free to subscribe to the BIRD
users mailing list bird-users@network.cz, just send `subscribe' to
bird-request@network.cz. Bug reports, suggestions, feature requests and
code are welcome! We don't use gitlab issues for reporting, sorry.
bird-request@network.cz.
Subscribe: http://bird.network.cz/mailman/listinfo/bird-users/
Archive: http://bird.network.cz/pipermail/bird-users/
Please don't send security issues to the mailing-list, contact us instead at
bird-support@network.cz which is a private e-mail address where you also can
get commercial support for your BIRD deployment.
We don't use our gitlab issues for reporting but we're partially tracking
the core developent team's work there publicly.
Contributing
============
Please see the CONTRIBUTING.md file to find how to contribute to BIRD.
Licence
=======

View File

@ -28,7 +28,7 @@ class BIRDFValPrinter(BIRDPrinter):
"T_ENUM_SCOPE": "i",
"T_ENUM_RTD": "i",
"T_ENUM_ROA": "i",
"T_ENUM_NETTYPE": "i",
"T_ENUM_NET_TYPE": "i",
"T_ENUM_RA_PREFERENCE": "i",
"T_ENUM_AF": "i",
"T_IP": "ip",

View File

@ -715,7 +715,7 @@ cf_lex_symbol(const char *data)
int val = sym->keyword->value;
if (val > 0) return val;
cf_lval.i = -val;
return ENUM;
return ENUM_TOKEN;
}
case SYM_METHOD:
return (sym->method->arg_num > 1) ? CF_SYM_METHOD_ARGS : CF_SYM_METHOD_BARE;

View File

@ -589,6 +589,7 @@ order_shutdown(int gr)
init_list(&c->mpls_domains);
init_list(&c->symbols);
obstacle_target_init(&c->obstacles, &c->obstacles_cleared, c->pool, "Config");
c->cli = (struct cli_config_list) {};
memset(c->def_tables, 0, sizeof(c->def_tables));
c->shutdown = 1;
c->gr_down = gr;

View File

@ -15,6 +15,7 @@
#include "lib/resource.h"
#include "lib/obstacle.h"
#include "lib/timer.h"
#include "lib/tlists.h"
/* Configuration structure */
struct config {
@ -26,6 +27,7 @@ struct config {
list logfiles; /* Configured log files (sysdep) */
list tests; /* Configured unit tests (f_bt_test_suite) */
list symbols; /* Configured symbols in config order */
TLIST_STRUCT_DEF(cli_config, struct cli_config) cli; /* Configured CLI sockets */
struct rfile *mrtdump_file; /* Configured MRTDump file */
const char *syslog_name; /* Name used for syslog (NULL -> no syslog) */

View File

@ -100,7 +100,8 @@ CF_DECLS
struct f_prefix px;
struct proto_spec ps;
struct channel_limit cl;
struct timeformat *tf;
struct timeformat tf;
struct timeformat *tfp;
struct settle_config settle;
struct adata *ad;
const struct adata *bs;
@ -110,7 +111,7 @@ CF_DECLS
%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT
%token GEQ LEQ NEQ AND OR IMP
%token PO PC
%token <i> NUM ENUM
%token <i> NUM ENUM_TOKEN
%token <ip4> IP4
%token <ip6> IP6
%token <i64> VPN_RD
@ -124,7 +125,7 @@ CF_DECLS
%type <settle> settle
%type <a> ipa net_ip6_slash
%type <net> net_ip4_ net_ip4 net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_
%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_ net_aspa_
%type <ad> label_stack_start label_stack
%type <t> text opttext
@ -325,6 +326,12 @@ net_mpls_: MPLS NUM
net_fill_mpls($$, $2);
}
net_aspa_: ASPA NUM
{
$$ = cfg_alloc(sizeof(net_addr_aspa));
net_fill_aspa($$, $2);
}
net_ip_: net_ip4_ | net_ip6_ ;
net_vpn_: net_vpn4_ | net_vpn6_ ;
net_roa_: net_roa4_ | net_roa6_ ;
@ -336,6 +343,7 @@ net_:
| net_flow_
| net_ip6_sadr_
| net_mpls_
| net_aspa_
;

View File

@ -34,10 +34,11 @@ m4_define(CF_CLI, `CF_KEYWORDS(m4_translit($1, [[ ]], [[,]]))
# Enums are translated to C initializers: use CF_ENUM(typename, prefix, values)
# For different prefix: CF_ENUM_PX(typename, external prefix, C prefix, values)
# The typename is converted to a keyword by removing T_ENUM_ prefix
m4_define(CF_enum, `m4_divert(1){ "CF_enum_prefix_ext[[]]$1", -((CF_enum_type<<16) | CF_enum_prefix_int[[]]$1) },
m4_divert(-1)')
m4_define(CF_ENUM, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$2)CF_iterate([[CF_enum]], [[m4_shift(m4_shift($@))]])DNL')
m4_define(CF_ENUM_PX, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$3)CF_iterate([[CF_enum]], [[m4_shift(m4_shift(m4_shift($@)))]])DNL')
m4_define(CF_ENUM, `CF_keywd(m4_substr($1, 7))m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$2)CF_iterate([[CF_enum]], [[m4_shift(m4_shift($@))]])DNL')
m4_define(CF_ENUM_PX, `CF_keywd(m4_substr($1, 7))m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$3)CF_iterate([[CF_enum]], [[m4_shift(m4_shift(m4_shift($@)))]])DNL')
# After all configuration templates end, we generate the keyword list
m4_m4wrap(`

View File

@ -29,6 +29,8 @@ m4_define(CF_END, `m4_divert(-1)')
m4_define(CF_itera, `m4_ifelse($#, 1, [[CF_iter($1)]], [[CF_iter($1)[[]]CF_itera(m4_shift($@))]])')
m4_define(CF_iterate, `m4_define([[CF_iter]], m4_defn([[$1]]))CF_itera($2)')
m4_define(CF_append, `m4_define([[$1]], m4_ifdef([[$1]], m4_defn([[$1]])[[$3]])[[$2]])')
# Keywords act as untyped %token
m4_define(CF_keywd, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)m4_define([[CF_toks]],CF_toks $1)]])')
m4_define(CF_KEYWORDS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token<s>[[]]CF_toks
@ -46,8 +48,10 @@ m4_define(CF_CLI_OPT, `')
m4_define(CF_CLI_HELP, `')
# ENUM declarations are ignored
m4_define(CF_ENUM, `')
m4_define(CF_ENUM_PX, `')
m4_define(CF_token, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)%token<s> $1]])')
m4_define(CF_enum, `CF_append([[CF_enum_type]],[[$1 { $$ = $2; }]],[[ | ]])CF_token($1)')
m4_define(CF_ENUM, `CF_enum(m4_substr($1, 7), $1)')
m4_define(CF_ENUM_PX, `CF_enum(m4_substr($1, 7), $1)')
# After all configuration templates end, we finally generate the grammar file.
m4_m4wrap(`
@ -58,7 +62,11 @@ m4_undivert(1)DNL
m4_undivert(2)DNL
%type <i> enum_type
%%
enum_type: CF_enum_type;
m4_undivert(3)DNL
%%

View File

@ -114,7 +114,7 @@ AC_SEARCH_LIBS([clock_gettime], [rt posix4],
AC_CANONICAL_HOST
# Store this value because ac_test_CFLAGS is overwritten by AC_PROG_CC
if test "$ac_test_CFLAGS" != set ; then
if ! test "$ac_test_CFLAGS" ; then
bird_cflags_default=yes
fi

View File

@ -125,11 +125,11 @@ Anyway, it will probably work well also on older systems.
and Perl, installing BIRD should be as easy as:
<code>
./configure
make
make install
vi /usr/local/etc/bird.conf
bird
./configure
make
make install
vi /usr/local/etc/bird.conf
bird
</code>
<p>You can use <tt>./configure --help</tt> to get a list of configure
@ -992,7 +992,7 @@ agreement").
<tag><label id="proto-pass-gen-from">generate from "<m/time/"</tag>
The start time of the usage of the password for packet signing.
The format of <cf><m/time/</cf> is <tt>dd-mm-yyyy HH:MM:SS</tt>.
The format of <cf><m/time/</cf> is <tt>YYYY-MM-DD [hh:mm:ss[.sss]]</tt>.
<tag><label id="proto-pass-gen-to">generate to "<m/time/"</tag>
The last time of the usage of the password for packet signing.
@ -1068,14 +1068,15 @@ inherited from templates can be updated by new definitions.
<tag><label id="proto-rpki-reload">rpki reload <m/switch/</tag>
Import or export filters may depend on route RPKI status (using
<cf/roa_check()/ operator). In contrast to to other filter operators,
this status for the same route may change as the content of ROA tables
changes. When this option is active, BIRD activates automatic reload of
the appropriate subset of prefixes imported or exported by the channels
whenever ROA tables are updated (after a short settle
time). When disabled, route reloads have to be requested manually. The
option is ignored if <cf/roa_check()/ is not used in channel filters.
Note that for BGP channels, automatic reload requires
<cf/roa_check()/ or <cf/aspa_check()/ operators). In contrast to other
filter operators, this status for the same route may change as the
content of ROA and ASPA tables changes. When this option is active, BIRD
activates automatic reload of the appropriate subset of prefixes imported
or exported by the channels whenever ROA and ASPA
tables are updated (after a short settle time). When disabled, route
reloads have to be requested manually. The option is ignored if neither
<cf/roa_check()/ nor <cf/aspa_check()/ is used in channel filters. Note
that for BGP channels, automatic reload requires
<ref id="bgp-import-table" name="import table"> or
<ref id="bgp-export-table" name="export table"> (for respective
direction). Default: on.
@ -1264,9 +1265,11 @@ protocol bgp {
<chapt>Remote control
<label id="remote-control">
<sect>Overview
<label id="remote-control-overview">
<p>You can use the command-line client <file>birdc</file> to talk with a running
BIRD. Communication is done using a <file/bird.ctl/ UNIX domain socket (unless
changed with the <tt/-s/ option given to both the server and the client). The
BIRD. Communication is done using the appropriate UNIX domain socket. The
commands can perform simple actions such as enabling/disabling of protocols,
telling BIRD to show various information, telling it to show routing table
filtered by filter, or asking BIRD to reconfigure. Press <tt/?/ at any time to
@ -1282,10 +1285,32 @@ does not support command line editing and history and has minimal dependencies.
This is useful for running BIRD in resource constrained environments, where
Readline library (required for regular BIRD client) is not available.
<p>Many commands have the <m/name/ of the protocol instance as an argument.
This argument can be omitted if there exists only a single instance.
<sect>Configuration
<label id="remote-control-configuration">
<p>Here is a brief list of supported functions:
<p>By default, BIRD opens <file/bird.ctl/ UNIX domain socket and the CLI tool
connects to it. If changed on the command line by the <tt/-s/ option,
BIRD or the CLI tool connects there instead.
<p>It's also possible to configure additional remote control sockets in the
configuration file by <cf/cli "name";/ and you can open how many
sockets you wish. There are no checks whether the user configured the same
socket multiple times and BIRD may behave weirdly if this happens. On shutdown,
the additional sockets get removed immediately and only the main socket stays
until the very end.
<p>The remote control socket can be also set as restricted by
<cf/cli "name" { restrict; };/ instead of sending the <cf/restrict/ command
after connecting. The user may still overload the daemon by requesting insanely
complex filters so you shouldn't expose this socket to public anyway.
<sect>Usage
<label id="remote-control-usage">
<p>Here is a brief list of supported functions.
<p>Note: Many commands have the <m/name/ of the protocol instance as an argument.
This argument can be omitted if there exists only a single instance.
<descrip>
<tag><label id="cli-show-status">show status</tag>
@ -1465,6 +1490,13 @@ This argument can be omitted if there exists only a single instance.
pipe protocol, both directions are always reloaded together (<cf/in/ or
<cf/out/ options are ignored in that case).
<tag><label id="cli-timeformat">timeformat "<m/format1/" [<m/limit/ "<m/format2/"]</tag>
Override format of date/time used by BIRD in this CLI session.
Meaning of "<m/format1/", <m/limit/, and "<m/format2/" is the same as in the
<ref id="opt-timeformat" name="timeformat"> configuration option. Also, the
same <cf/iso .../ shorthands may be used.
<tag><label id="cli-down">down</tag>
Shut BIRD down.
@ -1627,9 +1659,9 @@ in the foot).
a shell pattern (represented also as a string).
<tag><label id="type-bytestring">bytestring</tag>
This is a sequences of arbitrary bytes. There are no ways to modify
bytestrings in filters. You can pass them between function, assign
them to variables of type <cf/bytestring/, print such values,
This is a sequence of arbitrary bytes. There are no ways to modify
bytestrings in filters. You can pass them between functions, assign
them to variables of type <cf/bytestring/, print such values, and
compare bytestings (<cf/=, !=/).
Bytestring literals are written as a sequence of hexadecimal digit
@ -1678,7 +1710,7 @@ in the foot).
Route Distinguisher (<rfc id="4364">). They support the same special
operators as IP prefixes, and also <cf/.rd/ which extracts the Route
Distinguisher. Their literals are written
as <cf><m/vpnrd/ <m/ipprefix/</cf>
as <cf><m/rd/ <m/ipprefix/</cf>
<cf/NET_ROA4/ and <cf/NET_ROA6/ prefixes hold an IP prefix range
together with an ASN. They support the same special operators as IP
@ -1693,9 +1725,9 @@ in the foot).
<cf/NET_MPLS/ holds a single MPLS label and its handling is currently
not implemented.
<tag><label id="type-vpnrd">vpnrd</tag>
<tag><label id="type-rd"><label id="type-vpnrd">rd</tag>
This is a route distinguisher according to <rfc id="4364">. There are
three kinds of RD's: <cf><m/asn/:<m/32bit int/</cf>, <cf><m/asn4/:<m/16bit int/</cf>
three kinds of RDs: <cf><m/asn/:<m/32bit int/</cf>, <cf><m/asn4/:<m/16bit int/</cf>
and <cf><m/IPv4 address/:<m/32bit int/</cf>
<tag><label id="type-ec">ec</tag>
@ -1722,9 +1754,9 @@ in the foot).
to extract corresponding components of LCs:
<cf>(<m/asn/, <m/data1/, <m/data2/)</cf>.
<tag><label id="type-set">int|pair|quad|ip|prefix|ec|lc|enum set</tag>
Filters recognize four types of sets. Sets are similar to strings: you
can pass them around but you can't modify them. Literals of type <cf>int
<tag><label id="type-set">int|pair|quad|ip|prefix|ec|lc|rd|enum set</tag>
Filters recognize several types of sets. Sets are similar to strings: you
can pass them around but you cannot modify them. Literals of type <cf>int
set</cf> look like <cf> [ 1, 2, 5..7 ]</cf>. As you can see, both simple
values and ranges are permitted in sets.
@ -1747,21 +1779,20 @@ in the foot).
is valid, while <cf/(10, *, 20..30)/ or <cf/(10, 20..30, 40)/ is not
valid.
You can also use expressions for int, pair, EC and LC set values.
However, it must be possible to evaluate these expressions before daemon
boots. So you can use only constants inside them. E.g.
You can also use named constants or compound expressions for non-prefix
set values. However, it must be possible to evaluate these expressions
before daemon boots. So you can use only constants inside them. Also,
in case of compound expressions, they require parentheses around them.
E.g.
<code>
define one=1;
define myas=64500;
int set odds;
pair set ps;
ec set es;
<code>
define one=1;
define myas=64500;
odds = [ one, 2+1, 6-one, 2*2*2-1, 9, 11 ];
ps = [ (1,one+one), (3,4)..(4,8), (5,*), (6,3..6), (7..9,*) ];
es = [ (rt, myas, 3*10), (rt, myas+one, 0..16*16*16-1), (ro, myas+2, *) ];
</code>
int set odds = [ one, (2+1), (6-one), (2*2*2-1), 9, 11 ];
pair set ps = [ (1,one+one), (3,4)..(4,8), (5,*), (6,3..6), (7..9,*) ];
ec set es = [ (rt, myas, *), (rt, myas+2, 0..16*16*16-1) ];
</code>
Sets of prefixes are special: their literals does not allow ranges, but
allows prefix patterns that are written
@ -1923,32 +1954,93 @@ in the foot).
<label id="operators">
<p>The filter language supports common integer operators <cf>(+,-,*,/)</cf>,
parentheses <cf/(a*(b+c))/, comparison <cf/(a=b, a!=b, a&lt;b, a&gt;=b)/.
Logical operations include unary not (<cf/!/), and (<cf/&amp;&amp;/), and or
(<cf/&verbar;&verbar;/). Special operators include (<cf/&tilde;/,
<cf/!&tilde;/) for "is (not) element of a set" operation - it can be used on
element and set of elements of the same type (returning true if element is
contained in the given set), or on two strings (returning true if first string
matches a shell-like pattern stored in second string) or on IP and prefix
(returning true if IP is within the range defined by that prefix), or on prefix
and prefix (returning true if first prefix is more specific than second one) or
on bgppath and bgpmask (returning true if the path matches the mask) or on
number and bgppath (returning true if the number is in the path) or on bgppath
and int (number) set (returning true if any ASN from the path is in the set) or
on pair/quad and clist (returning true if the pair/quad is element of the
clist) or on clist and pair/quad set (returning true if there is an element of
the clist that is also a member of the pair/quad set).
parentheses <cf/(a*(b+c))/, comparison <cf/(a=b, a!=b, a&lt;b, a&gt;=b)/.</p>
<p>There is one operator related to ROA infrastructure - <cf/roa_check()/. It
examines a ROA table and does <rfc id="6483"> route origin validation for a
given network prefix. The basic usage is <cf>roa_check(<m/table/)</cf>, which
checks the current route (which should be from BGP to have AS_PATH argument) in
the specified ROA table and returns ROA_UNKNOWN if there is no relevant ROA,
ROA_VALID if there is a matching ROA, or ROA_INVALID if there are some relevant
ROAs but none of them match. There is also an extended variant
<cf>roa_check(<m/table/, <m/prefix/, <m/asn/)</cf>, which allows to specify a
prefix and an ASN as arguments.
<p>Logical operations include unary not (<cf/!/), and (<cf/&amp;&amp;/), and or
(<cf/&verbar;&verbar;/).</p>
<p>Special operators include (<cf/&tilde;/, <cf/!&tilde;/) for "is (not) element
of a set" operation - it can be used on:
<itemize>
<item>element and set of elements of the same type (returning true if
element is contained in the given set)
<item>two strings (returning true if the first string matches a shell-like
pattern stored in the second string)
<item>IP and prefix (returning true if IP is within the range defined by
that prefix)
<item>prefix and prefix (returning true if the first prefix is more specific
than the second one)
<item>bgppath and bgpmask (returning true if the path matches the mask)
<item>number and bgppath (returning true if the number is in the path)
<item>bgppath and int (number) set (returning true if any ASN from the
path is in the set)
<item>pair/quad and clist (returning true if the pair/quad is element of
the clist)
<item>clist and pair/quad set (returning true if there is an element of the
clist that is also a member of the pair/quad set).
</itemize>
<p>There are also operators related to RPKI infrastructure used to run
<rfc id="6483"> route origin validation and (draft) AS path validation.
<itemize>
<item><cf>roa_check(<m/table/)</cf> checks the current route in the specified
ROA table and returns <cf>ROA_UNKNOWN</cf>, <cf>ROA_INVALID</cf> or <cf>ROA_VALID</cf>,
if the validation result is unknown, invalid, or valid, respectively. The result is
valid if there is a matching ROA, it is invalid if there is either matching ROA
with a different ASN, or any covering ROA with shorter maximal prefix length.
<item><cf>roa_check(<m/table/, <m/prefix/, <m/asn/)</cf> is an explicit version
of the ROA check if the user for whatever reason needs to check a different prefix
or different ASN than the default one. The equivalent call of the short variant
is <cf>roa_check(<m/table/, net, bgp_path.last)</cf> and it is faster
to call the short variant.
<item><cf>aspa_check_downstream(<m/table/)</cf> checks the current route
in the specified ASPA table and returns <cf>ASPA_UNKNOWN</cf>, <cf>ASPA_INVALID</cf>,
or <cf>ASPA_VALID</cf> if the validation result is unknown, invalid, or valid,
respectively. The result is valid if there is a full coverage of matching
ASPA records according to the Algorithm for Downstream Paths by the (draft).
This operator is not present if BGP is not compiled in.
<item><cf>aspa_check_upstream(<m/table/)</cf> checks the current route
in the specified ASPA table as the former operator, but it applies the
(stricter) Algorithm for Upstream Paths by the (draft).
This operator is not present if BGP is not compiled in.
<item><cf>aspa_check(<m/table/, <m/path/, <m/is_upstream/)</cf> is
an explicit version of the former two ASPA check operators. The equivalent
of <cf>aspa_check_downstream</cf> is <cf>aspa_check(<m/table/, bgp_path, false)</cf>
and for <cf>aspa_check_upstream</cf> it is
<cf>aspa_check(<m/table/, bgp_path, true)</cf>.
Note: the ASPA check does not include the local ASN in the AS path.
Also, <cf>ASPA_INVALID</cf> is returned for an empty AS path
or for AS path containing <cf>CONFED_SET</cf> or <cf>CONFED_SEQUENCE</cf> blocks,
as the (draft) stipulates.
</itemize>
<p>The following example checks for ROA and ASPA on routes from a customer:
<code>
roa6 table r6;
aspa table at;
attribute int valid_roa;
attribute int valid_aspa;
filter customer_check {
case roa_check(r6) {
ROA_INVALID: reject "Invalid ROA";
ROA_VALID: valid_roa = 1;
}
case aspa_check_upstream(at) {
ASPA_INVALID: reject "Invalid ASPA";
ASPA_VALID: valid_aspa = 1;
}
accept;
}
</code>
<sect>Control structures
<label id="control-structures">
@ -1971,13 +2063,15 @@ 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
on the left side of the &tilde; operator and anything that could be a member of
a set is allowed before <cf/:/. Multiple commands are allowed without <cf/{}/
grouping. If <cf><m/expr/</cf> matches one of the <cf/:/ clauses, statements
between it and next <cf/:/ statement are executed. If <cf><m/expr/</cf> matches
neither of the <cf/:/ clauses, the statements after <cf/else:/ are executed.
<m/expr/ { else: | <m/set_body_expr/ /: <m/statement/ ; [... ] }</cf>.
The expression after <cf>case</cf> can be of any type that could be a member of
a set, while the <m/set_body_expr/ before <cf/:/ can be anything (constants,
intervals, expressions) that could be a part of a set literal. One exception is
prefix type, which can be used in sets bud not in <cf/case/ structure. Multiple
commands must be grouped by <cf/{}/. If <cf><m/expr/</cf> matches one
of the <cf/:/ clauses, the statement or block after it is
executed. If <cf><m/expr/</cf> matches neither of the <cf/:/ clauses, the
statement or block after <cf/else:/ is executed.
<p>Here is example that uses <cf/if/ and <cf/case/ structures:
@ -1993,7 +2087,7 @@ for int asn in bgp_path do {
}
case arg1 {
2: print "two"; print "I can do more commands without {}";
2: { print "two"; print "Multiple commands must brace themselves."; }
3 .. 5: print "three to five";
else: print "something else";
}
@ -2492,9 +2586,9 @@ protocols are notified and act accordingly (e.g. break an OSPF adjacency when
the BFD session went down).
<p>BIRD implements basic BFD behavior as defined in <rfc id="5880"> (some
advanced features like the echo mode or authentication are not implemented), IP
transport for BFD as defined in <rfc id="5881"> and <rfc id="5883"> and
interaction with client protocols as defined in <rfc id="5882">.
advanced features like the echo mode are not implemented), IP transport for BFD
as defined in <rfc id="5881"> and <rfc id="5883"> and interaction with client
protocols as defined in <rfc id="5882">.
<p>BFD packets are sent with a dynamic source port number. Linux systems use by
default a bit different dynamic port range than the IANA approved one
@ -2534,6 +2628,8 @@ milliseconds.
<code>
protocol bfd [&lt;name&gt;] {
accept [ipv4|ipv6] [direct|multihop];
strict bind &lt;switch&gt;;
zero udp6 checksum rx &lt;switch&gt;;
interface &lt;interface pattern&gt; {
interval &lt;time&gt;;
min rx interval &lt;time&gt;;
@ -2583,6 +2679,14 @@ protocol bfd [&lt;name&gt;] {
in cases like running multiple BIRD instances on a machine, each
handling a different set of interfaces. Default: disabled.
<tag><label id="bfd-zero-udp6-checksum-rx">zero udp6 checksum rx <m/switch/</tag>
UDP checksum computation is optional in IPv4 while it is mandatory in
IPv6. Some BFD implementations send UDP datagrams with zero (blank)
checksum even in IPv6 case. This option configures BFD listening sockets
to accept such datagrams. It is available only on platforms that support
the relevant socket option (e.g. <cf/UDP_NO_CHECK6_RX/ on Linux).
Default: disabled.
<tag><label id="bfd-iface">interface <m/pattern/ [, <m/.../] { <m/options/ }</tag>
Interface definitions allow to specify options for sessions associated
with such interfaces and also may contain interface specific options.
@ -2771,6 +2875,7 @@ avoid routing loops.
<item> <rfc id="9072"> - Extended Optional Parameters Length for BGP OPEN Message
<item> <rfc id="9117"> - Revised Validation Procedure for BGP Flow Specifications
<item> <rfc id="9234"> - Route Leak Prevention and Detection Using Roles
<item> <rfc id="9687"> - Send Hold Timer
</itemize>
<sect1>Route selection rules
@ -2915,10 +3020,9 @@ using the following configuration parameters:
restarted. Optionally, it can be configured (by <cf/graceful/ argument)
to trigger graceful restart instead of regular restart. It is also
possible to specify section with per-peer BFD session options instead of
just switch argument. Most BFD session specific options are allowed here
with the exception of authentication options. here Note that BFD
protocol also has to be configured, see <ref id="bfd" name="BFD">
section for details. Default: disabled.
just the switch argument. All BFD session-specific options are allowed
here. Note that BFD protocol also has to be configured, see
<ref id="bfd" name="BFD"> section for details. Default: disabled.
<tag><label id="bgp-ttl-security">ttl security <m/switch/</tag>
Use GTSM (<rfc id="5082"> - the generalized TTL security mechanism). GTSM
@ -3113,7 +3217,7 @@ using the following configuration parameters:
<tag><label id="bgp-interpret-communities">interpret communities <m/switch/</tag>
<rfc id="1997"> demands that BGP speaker should process well-known
communities like no-export (65535, 65281) or no-advertise (65535,
65282). For example, received route carrying a no-adverise community
65282). For example, received route carrying a no-advertise community
should not be advertised to any of its neighbors. If this option is
enabled (which is by default), BIRD has such behavior automatically (it
is evaluated when a route is exported to the BGP protocol just before
@ -3210,8 +3314,7 @@ using the following configuration parameters:
Send hold timer drops the session if the neighbor is sending keepalives,
but does not receive our messages, causing the TCP connection to stall.
This may happen due to malfunctioning or overwhelmed neighbor. See
<HTMLURL URL="https://datatracker.ietf.org/doc/draft-ietf-idr-bgp-sendholdtimer/"
name="draft-ietf-idr-bgp-sendholdtimer"> for more details.
<rfc id="9687"> for more details.
Like the option <cf/keepalive time/, the effective value depends on the
negotiated hold time, as it is scaled to maintain proportion between the
@ -4087,11 +4190,11 @@ could have up to 5 channels: <cf/ipv4/, <cf/ipv6/, <cf/vpn4/, <cf/vpn6/, and
<cf/mpls/.
<p><descrip>
<tag><label id="l3vpn-route-distinguisher">route distinguisher <m/vpnrd/</tag>
<tag><label id="l3vpn-route-distinguisher">route distinguisher <m/rd/</tag>
The route distinguisher that is attached to routes in the export
direction. Mandatory.
<tag><label id="l3vpn-rd">rd <m/vpnrd/</tag>
<tag><label id="l3vpn-rd">rd <m/rd/</tag>
A shorthand for the option <cf/route distinguisher/.
<tag><label id="l3vpn-import-target">import target <m/ec/|<m/ec-set/</tag>
@ -4846,14 +4949,14 @@ protocol ospf MyOSPF {
authentication cryptographic;
password "abc" {
id 1;
generate to "22-04-2003 11:00:06";
accept from "17-01-2001 12:01:05";
generate to "2023-04-22 11:00:06";
accept from "2021-01-17 12:01:05";
algorithm hmac sha384;
};
password "def" {
id 2;
generate to "22-07-2005 17:03:21";
accept from "22-02-2001 11:34:06";
generate to "2025-07-22";
accept from "2021-02-22";
algorithm hmac sha512;
};
};
@ -5671,6 +5774,10 @@ affected routes after RPKI update, see option <ref id="proto-rpki-reload"
name="rpki reload">. Or you can use a BIRD client command <cf>reload in
<m/bgp_protocol_name/</cf> for manual call of revalidation of all routes.
<p>The same protocol, since version 2, also receives and maintains a set
of ASPAs. You can then validate AS paths using function <cf/aspa_check()/
in (import) filters.
<sect1>Supported transports
<p>
<itemize>
@ -5690,22 +5797,28 @@ define more RPKI protocols generally.
protocol rpki [&lt;name&gt;] {
roa4 { table &lt;tab&gt;; };
roa6 { table &lt;tab&gt;; };
aspa { table &lt;tab&gt;; };
remote &lt;ip&gt; | "&lt;domain&gt;" [port &lt;num&gt;];
port &lt;num&gt;;
local address &lt;ip&gt;;
refresh [keep] &lt;num&gt;;
retry [keep] &lt;num&gt;;
expire [keep] &lt;num&gt;;
transport tcp;
transport tcp {
authentication none|md5;
password "&lt;text&gt;";
};
transport ssh {
bird private key "&lt;/path/to/id_rsa&gt;";
remote public key "&lt;/path/to/known_host&gt;";
user "&lt;name&gt;";
};
max version 2;
min version 2;
}
</code>
<p>Alse note that you have to specify the ROA channel. If you want to import
<p>Alse note that you have to specify the ROA and ASPA channels. If you want to import
only IPv4 prefixes you have to specify only roa4 channel. Similarly with IPv6
prefixes only. If you want to fetch both IPv4 and even IPv6 ROAs you have to
specify both channels.
@ -5752,15 +5865,37 @@ specify both channels.
instead. This may be useful for implementing loose RPKI check for
blackholes. Default: disabled.
<tag>transport tcp</tag> Unprotected transport over TCP. It's a default
transport. Should be used only on secure private networks.
Default: tcp
<tag>min version <m/num/</tag>
Minimal allowed version of the RTR protocol. BIRD will refuse to
downgrade a connection below this version and drop the session instead.
Default: 0
<tag>max version <m/num/</tag>
Maximal allowed version of the RTR protocol. BIRD will start with this
version. Use this option if sending version 2 to your cache causes
problems. Default: 2
<tag>transport tcp { <m/TCP transport options.../ }</tag> Transport over
TCP, it's the default transport. Cannot be combined with a SSH transport.
Default: TCP, no authentication.
<tag>transport ssh { <m/SSH transport options.../ }</tag> It enables a
SSHv2 transport encryption. Cannot be combined with a TCP transport.
Default: off
</descrip>
<sect3>TCP transport options
<p>
<descrip>
<tag>authentication none|md5</tag>
Select authentication method to be used. <cf/none/ means no
authentication, <cf/md5/ is TCP-MD5 authentication (<rfc id="2385">).
Default: no authentication.
<tag>password "<m>text</m>"</tag>
Use this password for TCP-MD5 authentication of the RPKI-To-Router session.
</descrip>
<sect3>SSH transport options
<p>
<descrip>
@ -5966,6 +6101,13 @@ options (<cf/bfd/ and <cf/weight 1/), the second nexthop has just <cf/weight 2/.
<p>The ROA config is just <cf>route <m/prefix/ max <m/int/ as <m/int/</cf> with no nexthop.
<sect1>Autonomous System Provider Authorization
<p>The ASPA config is <cf>route aspa <m/int/ providers <m/int/ [, <m/int/ ...]</cf> with no nexthop.
The first ASN is client and the following are a list of providers.
For a transit, you can also write <cf>route aspa <m/int/ transit</cf> to get
the no-provider ASPA.
<sect1>Flowspec
<label id="flowspec-network-type">

View File

@ -60,6 +60,11 @@ the given prefix. Experimental.
Reload of filters is now done by `reload filters` command, contrary to just `reload` in BIRD 2.
## Filters
We have removed the exception for `case` where multiple commands could be written
after the case label without braces. This caused unneeded complexity in the parser.
## Route attributes
All protocol attributes have been renamed in CLI to align with the filter language tokens.
@ -72,6 +77,11 @@ how to implement it properly.
The `scope` route attribute has been removed. Use custom route attributes instead.
## Protocols common
There is now a guard against too frequent restarts due to limits, called
`restart time`, set by default to 5 seconds. To disable, set this to 1 us.
## Pipe
It's now impossible to check immediately whether the route has entered a pipe

View File

@ -77,3 +77,4 @@ Reply codes of BIRD command-line interface
9000 Command too long
9001 Parse error
9002 Invalid symbol type
9003 Argument too long

View File

@ -362,13 +362,13 @@ CF_DECLS
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
ACCEPT, REJECT, ERROR,
INT, BOOL, IP, PREFIX, RD, PAIR, QUAD, EC, LC,
INT, BOOL, IP, PREFIX, RD, PAIR, QUAD, EC, LC, ENUM,
SET, STRING, BYTESTRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
IF, THEN, ELSE, CASE,
FOR, IN, DO,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
FROM, GW, NET, PROTO, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
ROA_CHECK,
ROA_CHECK, ASPA_CHECK,
DEFINED,
ADD, DELETE, RESET,
PREPEND,
@ -381,7 +381,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%nonassoc ELSE
%type <xp> cmds_int cmd_prep
%type <x> term term_bs cmd cmd_var cmds cmds_scoped constant constructor var var_list var_list_r function_call bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont
%type <x> term term_bs cmd cmd_var cmds constant constructor var var_list var_list_r function_call bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont
%type <fsa> static_attr
%type <f> filter where_filter
%type <fl> filter_body function_body
@ -393,7 +393,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%type <i32> cnum
%type <e> pair_item ec_item lc_item set_item switch_item ec_items set_items switch_items switch_body
%type <trie> fprefix_set
%type <v> set_atom switch_atom fipa
%type <v> set_atom0 set_atom switch_atom fipa
%type <px> fprefix
%type <t> get_cf_position
%type <s> for_var
@ -498,6 +498,7 @@ type:
case T_INT:
case T_PAIR:
case T_QUAD:
case T_ENUM:
case T_EC:
case T_LC:
case T_RD:
@ -513,6 +514,13 @@ type:
cf_error( "You can't create sets of this type." );
}
}
| ENUM
{ $<i>$ = cf_maybe_exit_filters(); }
enum_type
{
if ($<i>2) cf_enter_filters();
$$ = $3;
}
;
function_argsn:
@ -625,8 +633,6 @@ cmds: /* EMPTY */ { $$ = NULL; }
| cmds_int { $$ = $1.begin; }
;
cmds_scoped: { cf_push_soft_scope(new_config); } cmds { cf_pop_soft_scope(new_config); $$ = $2; } ;
cmd_var: var | cmd ;
cmd_prep: cmd_var {
@ -668,27 +674,30 @@ fipa:
* as a function call in switch case cmds.
*/
set_atom:
set_atom0:
NUM { $$.type = T_INT; $$.val.i = $1; }
| fipa { $$ = $1; }
| VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
| ENUM_TOKEN { $$.type = pair_a($1); $$.val.i = pair_b($1); }
| '(' term ')' {
$$ = cf_eval_tmp($2, T_VOID);
if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
if (!f_valid_set_type($$.type))
cf_error("Set-incompatible type (%s)", f_type_name($$.type));
}
;
set_atom:
set_atom0
| CF_SYM_KNOWN {
cf_assert_symbol($1, SYM_CONSTANT);
if (!f_valid_set_type(SYM_TYPE($1))) cf_error("%s: set-incompatible type", $1->name);
if (!f_valid_set_type(SYM_TYPE($1)))
cf_error("%s: Set-incompatible type (%s)", $1->name, f_type_name(SYM_TYPE($1)));
$$ = *$1->val;
}
;
switch_atom:
NUM { $$.type = T_INT; $$.val.i = $1; }
| '(' term ')' { $$ = cf_eval_tmp($2, T_INT); }
| fipa { $$ = $1; }
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
set_atom0
;
cnum:
@ -781,14 +790,14 @@ fprefix_set:
;
switch_body: /* EMPTY */ { $$ = NULL; }
| switch_body switch_items ':' cmds_scoped {
| switch_body switch_items ':' cmd {
/* Fill data fields */
struct f_tree *t;
for (t = $2; t; t = t->left)
t->data = $4;
$$ = f_merge_items($1, $2);
}
| switch_body ELSECOL cmds_scoped {
| switch_body ELSECOL cmd {
struct f_tree *t = f_new_tree();
t->from.type = t->to.type = T_VOID;
t->right = t;
@ -837,7 +846,7 @@ constant:
DBG( "ook\n" );
}
| '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PREFIX_SET, .val.ti = $2, }); }
| ENUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = $1 >> 16, .val.i = $1 & 0xffff, }); }
| ENUM_TOKEN { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = $1 >> 16, .val.i = $1 & 0xffff, }); }
;
constructor:
@ -956,6 +965,7 @@ term:
| ROA_CHECK '(' rtable ')' { $$ = f_implicit_roa_check($3); }
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK, $5, $7, $3); }
| ASPA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ASPA_CHECK_EXPLICIT, $5, $7, $3); }
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
@ -990,9 +1000,7 @@ for_var:
;
cmd:
'{' cmds_scoped '}' {
$$ = $2;
}
'{' { cf_push_soft_scope(new_config); } cmds { cf_pop_soft_scope(new_config); } '}' { $$ = $3; }
| IF term THEN cmd {
$$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
}

View File

@ -43,9 +43,11 @@ static const char * const f_type_str[] = {
[T_ENUM_SCOPE] = "enum scope",
[T_ENUM_RTD] = "enum rtd",
[T_ENUM_ROA] = "enum roa",
[T_ENUM_NETTYPE] = "enum nettype",
[T_ENUM_ASPA] = "enum aspa",
[T_ENUM_NET_TYPE] = "enum net_type",
[T_ENUM_RA_PREFERENCE] = "enum ra_preference",
[T_ENUM_AF] = "enum af",
[T_ENUM_MPLS_POLICY] = "enum mpls_policy",
[T_IP] = "ip",
[T_NET] = "prefix",

View File

@ -622,12 +622,19 @@ FID_WR_PUT(11)
#pragma GCC diagnostic ignored "-Woverride-init"
#endif
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Winitializer-overrides"
#endif
static struct sym_scope f_type_method_scopes[] = {
FID_WR_PUT(12)
};
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#if defined(__GNUC__) && __GNUC__ >= 6
#pragma GCC diagnostic pop
#endif

View File

@ -503,7 +503,7 @@
RESULT(T_BOOL, i, (v1.type != T_VOID) && !val_is_undefined(v1));
}
METHOD_R(T_NET, type, T_ENUM_NETTYPE, i, v1.val.net->type);
METHOD_R(T_NET, type, T_ENUM_NET_TYPE, i, v1.val.net->type);
METHOD_R(T_IP, is_v4, T_BOOL, i, ipa_is_ip4(v1.val.ip));
/* Add initialized variable */
@ -886,6 +886,8 @@
}]]);
}
}
else if (da->empty)
RESULT_VAL(da->empty(da));
else if ((empty = f_get_empty(da->type)).type != T_VOID)
RESULT_VAL(empty);
else
@ -1520,6 +1522,22 @@
}
INST(FI_ASPA_CHECK_EXPLICIT, 2, 1) { /* ASPA Check */
NEVER_CONSTANT;
ARG(1, T_PATH);
ARG(2, T_BOOL);
RTC(3);
rtable *table = rtc->table;
if (!table)
runtime("Missing ASPA table");
if (table->addr_type != NET_ASPA)
runtime("Table type must be ASPA");
RESULT(T_ENUM_ASPA, i, [[ aspa_check(table, v1.val.ad, v2.val.i) ]]);
}
INST(FI_FROM_HEX, 1, 1) { /* Convert hex text to bytestring */
ARG(1, T_STRING);

View File

@ -112,6 +112,17 @@ attribute lclist test_ca_lclist_max19;
attribute lclist test_ca_lclist_max20;
attribute lclist test_ca_lclist_max21;
attribute enum rts test_ca_rts;
attribute enum rtd test_ca_rtd;
attribute enum scope test_ca_scope;
attribute enum roa test_ca_roa;
attribute enum aspa test_ca_aspa;
attribute enum af test_ca_af;
attribute enum net_type test_ca_nettype;
attribute enum bgp_origin test_ca_bgp_origin;
attribute enum ra_preference test_ca_ra_preference;
attribute enum mpls_policy test_ca_mpls_policy;
attribute enum net_type set test_ca_nettype_set;
/* Uncomment this to get an error */
#attribute int bgp_path;
@ -195,8 +206,11 @@ bt_test_suite(t_bool, "Testing boolean expressions");
function aux_t_int(int t; int u)
{
int v;
case t {
1: {}
2: { int u; u = 1; v = 42; }
3: if true then v = t + u + 53; else v = 35 + u + t;
else: {}
}
}
@ -249,7 +263,7 @@ function t_int()
}
case four {
4: bt_assert(true);
(2+2): bt_assert(true);
else: bt_assert(false);
}
@ -582,12 +596,31 @@ bt_test_suite(t_ip_set, "Testing sets of ip address");
function t_enum()
{
enum rts ev0 = RTS_STATIC;
enum rtd ev1 = RTD_UNICAST;
enum scope ev2 = SCOPE_UNIVERSE;
enum roa ev3 = ROA_VALID;
enum aspa ev4 = ASPA_VALID;
enum af ev5 = AF_IPV6;
enum net_type ev6 = NET_IP6;
enum bgp_origin ev7 = ORIGIN_IGP;
enum ra_preference ev8 = RA_PREF_LOW;
enum mpls_policy ev9 = MPLS_POLICY_STATIC;
enum net_type set es = [NET_IP6, NET_VPN6];
bt_assert(format(RTS_STATIC) = "(enum 31)1");
bt_assert(format(NET_IP4) = "(enum 3b)1");
bt_assert(format(NET_VPN6) = "(enum 3b)4");
bt_assert(format(ev6) = "(enum 3b)2");
bt_assert(ev0 = RTS_STATIC);
bt_assert(ev6 = NET_IP6);
bt_assert(RTS_STATIC ~ [RTS_STATIC, RTS_DEVICE]);
bt_assert(RTS_BGP !~ [RTS_STATIC, RTS_DEVICE]);
bt_assert(NET_IP6 ~ es);
bt_assert(NET_IP4 !~ es);
}
bt_test_suite(t_enum, "Testing enums");
@ -2298,9 +2331,9 @@ bool t;
krt_source = 17;
krt_metric = 19;
# krt_lock_mtu = false;
# if krt_lock_congctl then krt_lock_mtu = false;
# krt_lock_window = true;
# krt_lock_rtt = krt_lock_rttvar && krt_lock_sstresh || krt_lock_cwnd;
# krt_lock_rtt = krt_lock_rttvar && krt_lock_ssthresh || krt_lock_cwnd;
accept "ok I take that";
}
@ -2381,7 +2414,56 @@ prefix pfx;
bt_test_suite(t_roa_check, "Testing ROA");
aspa table at;
protocol static
{
aspa { table at; };
route aspa 65540 provider 65544;
route aspa 65541 provider 65545;
route aspa 65542 provider 65544, 65545;
route aspa 65543 provider 65544, 65545;
route aspa 65544 transit;
route aspa 65545 transit;
route aspa 65550 provider 65540;
route aspa 65551 provider 65543;
}
function t_aspa_check()
{
bgppath p1 = +empty+;
p1.prepend(65540);
p1.prepend(65544);
bt_assert(aspa_check(at, p1, false) = ASPA_VALID);
bt_assert(aspa_check(at, p1, true) = ASPA_VALID);
p1.prepend(65542);
bt_assert(aspa_check(at, p1, false) = ASPA_VALID);
bt_assert(aspa_check(at, p1, true) = ASPA_INVALID);
p1.prepend(65555);
bt_assert(aspa_check(at, p1, false) = ASPA_UNKNOWN);
bt_assert(aspa_check(at, p1, true) = ASPA_INVALID);
bgppath p2 = +empty+;
p2.prepend(65554);
p2.prepend(65541);
p2.prepend(65545);
bt_assert(aspa_check(at, p2, false) = ASPA_UNKNOWN);
bt_assert(aspa_check(at, p2, true) = ASPA_UNKNOWN);
p2.prepend(65543);
bt_assert(aspa_check(at, p2, false) = ASPA_UNKNOWN);
bt_assert(aspa_check(at, p2, true) = ASPA_INVALID);
bgppath p3 = +empty+;
p3.prepend(65541);
p3.prepend(65544);
bt_assert(aspa_check(at, p3, false) = ASPA_INVALID);
bt_assert(aspa_check(at, p3, true) = ASPA_INVALID);
}
bt_test_suite(t_aspa_check, "Testing ASPA");
filter vpn_filter
{

View File

@ -34,7 +34,7 @@
* the buffer to indicate truncation.
*/
int
int_set_format(const struct adata *set, int way, int from, byte *buf, uint size)
int_set_format(const struct adata *set, enum isf_way way, int from, byte *buf, uint size)
{
u32 *z = (u32 *) set->data;
byte *end = buf + size - 24;
@ -56,10 +56,18 @@ int_set_format(const struct adata *set, int way, int from, byte *buf, uint size)
if (i > from2)
*buf++ = ' ';
if (way)
buf += bsprintf(buf, "(%d,%d)", z[i] >> 16, z[i] & 0xffff);
else
buf += bsprintf(buf, "%R", z[i]);
switch (way)
{
case ISF_COMMUNITY_LIST:
buf += bsprintf(buf, "(%d,%d)", z[i] >> 16, z[i] & 0xffff);
break;
case ISF_ROUTER_ID:
buf += bsprintf(buf, "%R", z[i]);
break;
case ISF_NUMBERS:
buf += bsprintf(buf, "%u", z[i]);
break;
}
}
*buf = 0;
return 0;

View File

@ -92,11 +92,11 @@ t_set_int_union(void)
const struct adata *set_union;
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, T_BUFFER_SIZE) == 0);
bt_assert(int_set_format(set_union, ISF_ROUTER_ID, 2, buf, T_BUFFER_SIZE) == 0);
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, T_BUFFER_SIZE) == 0);
bt_assert(int_set_format(set_union, ISF_ROUTER_ID, 2, buf, T_BUFFER_SIZE) == 0);
return 1;
}
@ -106,17 +106,21 @@ t_set_int_format(void)
{
generate_set_sequence(SET_TYPE_INT, SET_SIZE_FOR_FORMAT_OUTPUT);
bt_assert(int_set_format(set_sequence, 0, 0, buf, T_BUFFER_SIZE) == 0);
bt_assert(int_set_format(set_sequence, ISF_ROUTER_ID, 0, buf, T_BUFFER_SIZE) == 0);
bt_assert(strcmp(buf, "0.0.0.0 0.0.0.1 0.0.0.2 0.0.0.3 0.0.0.4 0.0.0.5 0.0.0.6 0.0.0.7 0.0.0.8 0.0.0.9") == 0);
bzero(buf, T_BUFFER_SIZE);
bt_assert(int_set_format(set_sequence, 0, 2, buf, T_BUFFER_SIZE) == 0);
bt_assert(int_set_format(set_sequence, ISF_ROUTER_ID, 2, buf, T_BUFFER_SIZE) == 0);
bt_assert(strcmp(buf, "0.0.0.2 0.0.0.3 0.0.0.4 0.0.0.5 0.0.0.6 0.0.0.7 0.0.0.8 0.0.0.9") == 0);
bzero(buf, T_BUFFER_SIZE);
bt_assert(int_set_format(set_sequence, 1, 0, buf, T_BUFFER_SIZE) == 0);
bt_assert(int_set_format(set_sequence, ISF_COMMUNITY_LIST, 0, buf, T_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);
bzero(buf, T_BUFFER_SIZE);
bt_assert(int_set_format(set_sequence, ISF_NUMBERS, 0, buf, T_BUFFER_SIZE) == 0);
bt_assert(strcmp(buf, "0 1 2 3 4 5 6 7 8 9") == 0);
return 1;
}

View File

@ -73,7 +73,7 @@ struct adata *as_path_prepend2(struct linpool *pool, const struct adata *op, int
struct adata *as_path_to_old(struct linpool *pool, const struct adata *path);
struct adata *as_path_cut(struct linpool *pool, const struct adata *path, uint num);
const struct adata *as_path_merge(struct linpool *pool, const struct adata *p1, const struct adata *p2);
void as_path_format(const struct adata *path, byte *buf, uint size);
void as_path_format(const struct adata *path, byte *buf, uint size) ACCESS_WRITE(2, 3);
int as_path_getlen(const struct adata *path);
int as_path_getlen_int(const struct adata *path, int bs);
int as_path_get_first(const struct adata *path, u32 *orig_as);
@ -238,12 +238,17 @@ static inline int lc_match(const u32 *l, int i, lcomm v)
static inline u32 *lc_copy(u32 *dst, const u32 *src)
{ memcpy(dst, src, LCOMM_LENGTH); return dst + 3; }
enum isf_way {
ISF_NUMBERS,
ISF_COMMUNITY_LIST,
ISF_ROUTER_ID,
};
int int_set_format(const struct adata *set, int way, int from, byte *buf, uint size);
int int_set_format(const struct adata *set, enum isf_way way, int from, byte *buf, uint size) ACCESS_WRITE(4, 5);
int ec_format(byte *buf, u64 ec);
int ec_set_format(const struct adata *set, int from, byte *buf, uint size);
int ec_set_format(const struct adata *set, int from, byte *buf, uint size) ACCESS_WRITE(3, 4);
int lc_format(byte *buf, lcomm lc);
int lc_set_format(const struct adata *set, int from, byte *buf, uint size);
int lc_set_format(const struct adata *set, int from, byte *buf, uint size) ACCESS_WRITE(3, 4);
int int_set_contains(const struct adata *list, u32 val);
int ec_set_contains(const struct adata *list, u64 val);
int lc_set_contains(const struct adata *list, lcomm val);

View File

@ -114,9 +114,20 @@ static inline int u64_cmp(u64 i1, u64 i2)
#define USE_RESULT __atribute__((warn_unused_result))
#define UNUSED __attribute__((unused))
#define PACKED __attribute__((packed))
#define NONNULL(...) __attribute__((nonnull((__VA_ARGS__))))
#define NONNULL(...) __attribute__((nonnull(__VA_ARGS__)))
#define ALLOC_SIZE(...) __attribute__((alloc_size(__VA_ARGS__)))
#define CLEANUP(fun) __attribute__((cleanup(fun)))
#if __GNUC__ >= 10
#define ACCESS_READ(...) __attribute__((access(read_only, __VA_ARGS__)))
#define ACCESS_WRITE(...) __attribute__((access(write_only, __VA_ARGS__)))
#define ACCESS_RW(...) __attribute__((access(read_write, __VA_ARGS__)))
#else
#define ACCESS_READ(...)
#define ACCESS_WRITE(...)
#define ACCESS_RW(...)
#endif
#define STATIC_ASSERT(EXP) _Static_assert(EXP, #EXP)
#define STATIC_ASSERT_MSG(EXP,MSG) _Static_assert(EXP, MSG)

View File

@ -293,13 +293,9 @@ flow_read_ip4_part(const byte *part)
static inline ip6_addr
flow_read_ip6(const byte *px, uint pxlen, uint pxoffset)
{
uint floor_offset = BYTES(pxoffset - (pxoffset % 8));
uint ceil_len = BYTES(pxlen);
ip6_addr ip = IP6_NONE;
memcpy(((byte *) &ip) + floor_offset, px, ceil_len - floor_offset);
return ip6_ntoh(ip);
memcpy(&ip, px, BYTES(pxlen - pxoffset));
return ip6_shift_right(ip6_ntoh(ip), pxoffset);
}
ip6_addr
@ -476,7 +472,7 @@ flow_validate(const byte *nlri, uint len, int ipv6)
uint pxoffset = *pos++;
if (pxoffset > IP6_MAX_PREFIX_LENGTH || pxoffset > pxlen)
return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET;
bytes -= pxoffset / 8;
bytes = BYTES(pxlen - pxoffset);
}
pos += bytes;
@ -749,12 +745,12 @@ flow_builder6_add_pfx(struct flow_builder *fb, const net_addr_ip6 *n6, u32 pxoff
if (!builder_add_prepare(fb))
return 0;
ip6_addr ip6 = ip6_hton(n6->prefix);
ip6_addr ip6 = ip6_hton(ip6_shift_left(n6->prefix, pxoffset));
BUFFER_PUSH(fb->data) = fb->this_type;
BUFFER_PUSH(fb->data) = n6->pxlen;
BUFFER_PUSH(fb->data) = pxoffset;
push_pfx_to_buffer(fb, BYTES(n6->pxlen) - (pxoffset / 8), ((byte *) &ip6) + (pxoffset / 8));
push_pfx_to_buffer(fb, BYTES(n6->pxlen - pxoffset), ((byte *) &ip6));
builder_add_finish(fb);
return 1;

View File

@ -9,21 +9,37 @@
#include "test/birdtest.h"
#include "lib/flowspec.h"
#define NET_ADDR_FLOW4_(what,prefix,pxlen,data_) \
do \
{ \
what = alloca(sizeof(net_addr_flow4) + 128); \
*what = NET_ADDR_FLOW4(prefix, pxlen, sizeof(data_)); \
memcpy(what->data, &(data_), sizeof(data_)); \
} while(0)
#define NET_ADDR_FLOW4_(prefix,pxlen,nlri) \
({ \
uint _len = sizeof(nlri); \
net_addr_flow4 *_n = tmp_alloc(sizeof(net_addr_flow4) + _len); \
*_n = NET_ADDR_FLOW4(prefix, pxlen, _len); \
memcpy(_n->data, &(nlri), _len); \
if (_n->data[0] == 0) _n->data[0] = _len - 1; \
_n; \
})
#define NET_ADDR_FLOW6_(what,prefix,pxlen,data_) \
do \
{ \
what = alloca(sizeof(net_addr_flow6) + 128); \
*what = NET_ADDR_FLOW6(prefix, pxlen, sizeof(data_)); \
memcpy(what->data, &(data_), sizeof(data_)); \
} while(0)
#define NET_ADDR_FLOW4_NLRI(...) \
({ \
const byte _nlri[] = { __VA_ARGS__ }; \
NET_ADDR_FLOW4_(flow_read_ip4_part(_nlri + 1), _nlri[2], _nlri); \
})
#define NET_ADDR_FLOW6_(prefix,pxlen,nlri) \
({ \
uint _len = sizeof(nlri); \
net_addr_flow6 *_n = tmp_alloc(sizeof(net_addr_flow6) + _len); \
*_n = NET_ADDR_FLOW6(prefix, pxlen, _len); \
memcpy(_n->data, &(nlri), _len); \
if (_n->data[0] == 0) _n->data[0] = _len - 1; \
_n; \
})
#define NET_ADDR_FLOW6_NLRI(...) \
({ \
const byte _nlri[] = { __VA_ARGS__ }; \
NET_ADDR_FLOW6_(flow_read_ip6_part(_nlri + 1), _nlri[2], _nlri); \
})
static int
t_read_length(void)
@ -67,13 +83,13 @@ t_write_length(void)
static int
t_first_part(void)
{
net_addr_flow4 *f;
NET_ADDR_FLOW4_(f, ip4_build(10,0,0,1), 24, ((byte[]) { 0x00, 0x00, 0xab }));
net_addr_flow4 *f = NET_ADDR_FLOW4_(IP4_NONE, 0, ((byte[]) { 0x00, 0x00, 0xab }));
const byte *under240 = &f->data[1];
const byte *above240 = &f->data[2];
/* Case 0x00 0x00 */
f->data[0] = 0x00;
bt_assert(flow4_first_part(f) == NULL);
/* Case 0x01 0x00 */
@ -103,15 +119,14 @@ t_first_part(void)
static int
t_iterators4(void)
{
net_addr_flow4 *f;
NET_ADDR_FLOW4_(f, ip4_build(5,6,7,0), 24, ((byte[]) {
const net_addr_flow4 *f = NET_ADDR_FLOW4_NLRI(
25, /* Length */
FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
}));
);
const byte *start = f->data;
const byte *p1_dst_pfx = &f->data[1];
@ -136,15 +151,14 @@ t_iterators4(void)
static int
t_iterators6(void)
{
net_addr_flow6 *f;
NET_ADDR_FLOW6_(f, ip6_build(0,0,0x12345678,0x9a000000), 0x68, ((byte[]) {
const net_addr_flow6 *f = NET_ADDR_FLOW6_NLRI(
26, /* Length */
FLOW_TYPE_DST_PREFIX, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a,
FLOW_TYPE_SRC_PREFIX, 0x08, 0x0, 0xc0,
FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
FLOW_TYPE_LABEL, 0x80, 0x55,
}));
);
const byte *start = f->data;
const byte *p1_dst_pfx = &f->data[1];
@ -169,15 +183,14 @@ t_iterators6(void)
static int
t_accessors4(void)
{
net_addr_flow4 *f;
NET_ADDR_FLOW4_(f, ip4_build(5,6,7,0), 24, ((byte[]) {
const net_addr_flow4 *f = NET_ADDR_FLOW4_NLRI(
25, /* Length */
FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
}));
);
const byte *p1_dst_px = &f->data[1];
const ip4_addr p1_dst_addr = ip4_build(5,6,7,0);
@ -194,15 +207,14 @@ t_accessors4(void)
static int
t_accessors6(void)
{
net_addr_flow6 *f;
NET_ADDR_FLOW6_(f, ip6_build(0,0,0x12345678,0x9a000000), 0x68, ((byte[]) {
const net_addr_flow6 *f = NET_ADDR_FLOW6_NLRI(
26, /* Length */
FLOW_TYPE_DST_PREFIX, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a,
FLOW_TYPE_SRC_PREFIX, 0x08, 0x0, 0xc0,
FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
FLOW_TYPE_LABEL, 0x80, 0x55,
}));
);
const byte *p1_dst_px = &f->data[1];
const ip6_addr p1_dst_addr = ip6_build(0,0,0x12345678,0x9a000000);
@ -450,17 +462,14 @@ t_builder4(void)
/* Expectation */
static byte nlri[] = {
25,
const net_addr_flow4 *expect = NET_ADDR_FLOW4_NLRI(
0,
FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
FLOW_TYPE_IP_PROTOCOL, 0x80, 0x06,
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55
};
net_addr_flow4 *expect;
NET_ADDR_FLOW4_(expect, ip4_build(5, 6, 7, 0), 24, nlri);
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
);
/* Normal order */
@ -531,17 +540,14 @@ t_builder6(void)
/* Expectation */
byte nlri[] = {
27,
FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
const net_addr_flow6 *expect = NET_ADDR_FLOW6_NLRI(
0,
FLOW_TYPE_DST_PREFIX, 103, 61, 0x22, 0x46, 0x8a, 0xcf, 0x13, 0x00,
FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
FLOW_TYPE_NEXT_HEADER, 0x80, 0x06,
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
FLOW_TYPE_LABEL, 0x80, 0x55,
};
net_addr_flow6 *expect;
NET_ADDR_FLOW6_(expect, ip6_build(0, 1, 0x12345678, 0x98000000), 103, nlri);
);
/* Normal order */
@ -605,9 +611,11 @@ t_builder6(void)
static int
t_formatting4(void)
{
char b[1024];
const net_addr_flow4 *input[4];
const char *expect[4];
byte nlri[] = {
expect[0] = "flow4 { dst 10.0.0.0/8; proto 23; dport > 24 && < 30 || 40..50,60..70,80 && >= 90; sport > 24 && < 30 || 40,50,60..70,80; icmp type 80; icmp code 90; tcp flags 0x3/0x3 && 0x0/0xc; length 0..65535; dscp 63; fragment dont_fragment || !is_fragment; }";
input[0] = NET_ADDR_FLOW4_NLRI(
0,
FLOW_TYPE_DST_PREFIX, 0x08, 10,
FLOW_TYPE_IP_PROTOCOL, 0x81, 23,
@ -618,18 +626,44 @@ t_formatting4(void)
FLOW_TYPE_TCP_FLAGS, 0x01, 0x03, 0xc2, 0x0c,
FLOW_TYPE_PACKET_LENGTH, 0x03, 0, 0xd5, 0xff, 0xff,
FLOW_TYPE_DSCP, 0x81, 63,
FLOW_TYPE_FRAGMENT, 0x01, 0x01, 0x82, 0x02
};
*nlri = (u8) sizeof(nlri);
FLOW_TYPE_FRAGMENT, 0x01, 0x01, 0x82, 0x02,
);
net_addr_flow4 *input;
NET_ADDR_FLOW4_(input, ip4_build(5, 6, 7, 0), 24, nlri);
/* RFC 8955 4.3.1 Example 1 */
expect[1] = "flow4 { dst 192.0.2.0/24; proto 6; port 25; }";
input[1] = NET_ADDR_FLOW4_NLRI(
0x0b,
0x01, 0x18, 0xc0, 0x00, 0x02,
0x03, 0x81, 0x06,
0x04, 0x81, 0x19,
);
const char *expect = "flow4 { dst 10.0.0.0/8; proto 23; dport > 24 && < 30 || 40..50,60..70,80 && >= 90; sport > 24 && < 30 || 40,50,60..70,80; icmp type 80; icmp code 90; tcp flags 0x3/0x3 && 0x0/0xc; length 0..65535; dscp 63; fragment dont_fragment || !is_fragment; }";
/* RFC 8955 4.3.2 Example 2 */
expect[2] = "flow4 { dst 192.0.2.0/24; src 203.0.113.0/24; port 137..139,8080; }";
input[2] = NET_ADDR_FLOW4_NLRI(
0x12,
0x01, 0x18, 0xc0, 0x00, 0x02,
0x02, 0x18, 0xcb, 0x00, 0x71,
0x04, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
);
bt_assert(flow4_net_format(b, sizeof(b), input) == strlen(expect));
bt_debug(" expect: '%s',\n output: '%s'\n", expect, b);
bt_assert(strcmp(b, expect) == 0);
/* RFC 8955 4.3.3 Example 3 */
expect[3] = "flow4 { dst 192.0.2.1/32; fragment !0x0/0x5; }";
input[3] = NET_ADDR_FLOW4_NLRI(
0x09,
0x01, 0x20, 0xc0, 0x00, 0x02, 0x01,
0x0c, 0x80, 0x05,
);
/* Run the tests */
for (uint i = 0; i < ARRAY_SIZE(input); i++)
{
char buf[1024];
uint len = flow4_net_format(buf, sizeof(buf), input[i]);
bt_debug(" expect: '%s',\n output: '%s'\n", expect[i], buf);
bt_assert(!strcmp(buf, expect[i]));
bt_assert(len == strlen(expect[i]));
}
return 1;
}
@ -637,26 +671,46 @@ t_formatting4(void)
static int
t_formatting6(void)
{
char b[1024];
const net_addr_flow6 *input[3];
const char *expect[3];
byte nlri[] = {
// (ip6_build(0, 1, 0x12345678, 0x98000000), 103, nlri0);
expect[0] = "flow6 { dst ::1:1234:5678:9800:0/103 offset 61; src c000::/8; next header 6; port 20..40,273; label < 500000; }";
input[0] = NET_ADDR_FLOW6_NLRI(
0,
FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
FLOW_TYPE_DST_PREFIX, 103, 61, 0x22, 0x46, 0x8a, 0xcf, 0x13, 0x00,
FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
FLOW_TYPE_PORT, 0x03, 20, 0x45, 40, 0x91, 0x01, 0x11,
FLOW_TYPE_LABEL, 0xa4, 0x00, 0x07, 0xa1, 0x20,
};
*nlri = (u8) sizeof(nlri);
);
net_addr_flow6 *input;
NET_ADDR_FLOW6_(input, ip6_build(0, 1, 0x12345678, 0x98000000), 103, nlri);
/* RFC 8956 3.8.1 Example 1 */
expect[1] = "flow6 { dst 2001:db8::/32; src ::1234:5678:9a00:0/104 offset 64; next header 6; }";
input[1] = NET_ADDR_FLOW6_(ip6_build(0x20010db8, 0, 0, 0), 32, ((const byte[]) {
0x12,
0x01, 0x20, 0x00, 0x20, 0x01, 0x0d, 0xb8,
0x02, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a,
0x03, 0x81, 0x06,
}));
const char *expect = "flow6 { dst ::1:1234:5678:9800:0/103 offset 61; src c000::/8; next header 6; port 20..40,273; label < 500000; }";
/* RFC 8956 3.8.2 Example 2 */
expect[2] = "flow6 { dst 2001:db8::/32; src ::1234:5678:9a00:0/104 offset 65; }";
input[2] = NET_ADDR_FLOW6_(ip6_build(0x20010db8, 0, 0, 0), 32, ((const byte[]) {
0x0f,
0x01, 0x20, 0x00, 0x20, 0x01, 0x0d, 0xb8,
0x02, 0x68, 0x41, 0x24, 0x68, 0xac, 0xf1, 0x34,
}));
bt_assert(flow6_net_format(b, sizeof(b), input) == strlen(expect));
bt_debug(" expect: '%s',\n output: '%s'\n", expect, b);
bt_assert(strcmp(b, expect) == 0);
/* Run the tests */
for (uint i = 0; i < ARRAY_SIZE(input); i++)
{
char buf[1024];
uint len = flow6_net_format(buf, sizeof(buf), input[i]);
bt_debug(" expect: '%s',\n output: '%s'\n", expect[i], buf);
bt_assert(!strcmp(buf, expect[i]));
bt_assert(len == strlen(expect[i]));
}
return 1;
}

134
lib/ip.c
View File

@ -156,6 +156,131 @@ ip6_classify(ip6_addr *a)
}
/*
* IPv6 bit shifting
*/
ip6_addr
ip6_shift_left(ip6_addr a, uint bits)
{
if (bits == 0)
return a;
if (bits > 127)
return IP6_NONE;
int words = bits / 32;
int rem = bits % 32;
for (int i = 0; i < 3 - words; i++)
a.addr[i] = (a.addr[i + words] << rem) |
(rem ? (a.addr[i + words + 1] >> (32 - rem)) : 0);
a.addr[3 - words] = a.addr[3] << rem;
memset(&a.addr[4 - words], 0, words * 4);
return a;
}
ip6_addr
ip6_shift_right(ip6_addr a, uint bits)
{
if (bits == 0)
return a;
if (bits > 127)
return IP6_NONE;
int words = bits / 32;
int rem = bits % 32;
for (int i = 3; i > words; i--)
a.addr[i] = (a.addr[i - words] >> rem) |
(rem ? (a.addr[i - words - 1] << (32 - rem)) : 0);
a.addr[words] = a.addr[0] >> rem;
memset(&a.addr[0], 0, words * 4);
return a;
}
static const char numbers[] = {
'0', 0, 0, 0, '1', 0, 0, 0, '2', 0, 0, 0, '3', 0, 0, 0, '4', 0, 0, 0, '5', 0, 0, 0, '6', 0, 0, 0, '7', 0, 0, 0,
'8', 0, 0, 0, '9', 0, 0, 0, '1', '0', 0, 0, '1', '1', 0, 0, '1', '2', 0, 0, '1', '3', 0, 0, '1', '4', 0, 0, '1', '5', 0, 0,
'1', '6', 0, 0, '1', '7', 0, 0, '1', '8', 0, 0, '1', '9', 0, 0, '2', '0', 0, 0, '2', '1', 0, 0, '2', '2', 0, 0, '2', '3', 0, 0,
'2', '4', 0, 0, '2', '5', 0, 0, '2', '6', 0, 0, '2', '7', 0, 0, '2', '8', 0, 0, '2', '9', 0, 0, '3', '0', 0, 0, '3', '1', 0, 0,
'3', '2', 0, 0, '3', '3', 0, 0, '3', '4', 0, 0, '3', '5', 0, 0, '3', '6', 0, 0, '3', '7', 0, 0, '3', '8', 0, 0, '3', '9', 0, 0,
'4', '0', 0, 0, '4', '1', 0, 0, '4', '2', 0, 0, '4', '3', 0, 0, '4', '4', 0, 0, '4', '5', 0, 0, '4', '6', 0, 0, '4', '7', 0, 0,
'4', '8', 0, 0, '4', '9', 0, 0, '5', '0', 0, 0, '5', '1', 0, 0, '5', '2', 0, 0, '5', '3', 0, 0, '5', '4', 0, 0, '5', '5', 0, 0,
'5', '6', 0, 0, '5', '7', 0, 0, '5', '8', 0, 0, '5', '9', 0, 0, '6', '0', 0, 0, '6', '1', 0, 0, '6', '2', 0, 0, '6', '3', 0, 0,
'6', '4', 0, 0, '6', '5', 0, 0, '6', '6', 0, 0, '6', '7', 0, 0, '6', '8', 0, 0, '6', '9', 0, 0, '7', '0', 0, 0, '7', '1', 0, 0,
'7', '2', 0, 0, '7', '3', 0, 0, '7', '4', 0, 0, '7', '5', 0, 0, '7', '6', 0, 0, '7', '7', 0, 0, '7', '8', 0, 0, '7', '9', 0, 0,
'8', '0', 0, 0, '8', '1', 0, 0, '8', '2', 0, 0, '8', '3', 0, 0, '8', '4', 0, 0, '8', '5', 0, 0, '8', '6', 0, 0, '8', '7', 0, 0,
'8', '8', 0, 0, '8', '9', 0, 0, '9', '0', 0, 0, '9', '1', 0, 0, '9', '2', 0, 0, '9', '3', 0, 0, '9', '4', 0, 0, '9', '5', 0, 0,
'9', '6', 0, 0, '9', '7', 0, 0, '9', '8', 0, 0, '9', '9', 0, 0, '1', '0', '0', 0, '1', '0', '1', 0, '1', '0', '2', 0, '1', '0', '3', 0,
'1', '0', '4', 0, '1', '0', '5', 0, '1', '0', '6', 0, '1', '0', '7', 0, '1', '0', '8', 0, '1', '0', '9', 0, '1', '1', '0', 0, '1', '1', '1', 0,
'1', '1', '2', 0, '1', '1', '3', 0, '1', '1', '4', 0, '1', '1', '5', 0, '1', '1', '6', 0, '1', '1', '7', 0, '1', '1', '8', 0, '1', '1', '9', 0,
'1', '2', '0', 0, '1', '2', '1', 0, '1', '2', '2', 0, '1', '2', '3', 0, '1', '2', '4', 0, '1', '2', '5', 0, '1', '2', '6', 0, '1', '2', '7', 0,
'1', '2', '8', 0, '1', '2', '9', 0, '1', '3', '0', 0, '1', '3', '1', 0, '1', '3', '2', 0, '1', '3', '3', 0, '1', '3', '4', 0, '1', '3', '5', 0,
'1', '3', '6', 0, '1', '3', '7', 0, '1', '3', '8', 0, '1', '3', '9', 0, '1', '4', '0', 0, '1', '4', '1', 0, '1', '4', '2', 0, '1', '4', '3', 0,
'1', '4', '4', 0, '1', '4', '5', 0, '1', '4', '6', 0, '1', '4', '7', 0, '1', '4', '8', 0, '1', '4', '9', 0, '1', '5', '0', 0, '1', '5', '1', 0,
'1', '5', '2', 0, '1', '5', '3', 0, '1', '5', '4', 0, '1', '5', '5', 0, '1', '5', '6', 0, '1', '5', '7', 0, '1', '5', '8', 0, '1', '5', '9', 0,
'1', '6', '0', 0, '1', '6', '1', 0, '1', '6', '2', 0, '1', '6', '3', 0, '1', '6', '4', 0, '1', '6', '5', 0, '1', '6', '6', 0, '1', '6', '7', 0,
'1', '6', '8', 0, '1', '6', '9', 0, '1', '7', '0', 0, '1', '7', '1', 0, '1', '7', '2', 0, '1', '7', '3', 0, '1', '7', '4', 0, '1', '7', '5', 0,
'1', '7', '6', 0, '1', '7', '7', 0, '1', '7', '8', 0, '1', '7', '9', 0, '1', '8', '0', 0, '1', '8', '1', 0, '1', '8', '2', 0, '1', '8', '3', 0,
'1', '8', '4', 0, '1', '8', '5', 0, '1', '8', '6', 0, '1', '8', '7', 0, '1', '8', '8', 0, '1', '8', '9', 0, '1', '9', '0', 0, '1', '9', '1', 0,
'1', '9', '2', 0, '1', '9', '3', 0, '1', '9', '4', 0, '1', '9', '5', 0, '1', '9', '6', 0, '1', '9', '7', 0, '1', '9', '8', 0, '1', '9', '9', 0,
'2', '0', '0', 0, '2', '0', '1', 0, '2', '0', '2', 0, '2', '0', '3', 0, '2', '0', '4', 0, '2', '0', '5', 0, '2', '0', '6', 0, '2', '0', '7', 0,
'2', '0', '8', 0, '2', '0', '9', 0, '2', '1', '0', 0, '2', '1', '1', 0, '2', '1', '2', 0, '2', '1', '3', 0, '2', '1', '4', 0, '2', '1', '5', 0,
'2', '1', '6', 0, '2', '1', '7', 0, '2', '1', '8', 0, '2', '1', '9', 0, '2', '2', '0', 0, '2', '2', '1', 0, '2', '2', '2', 0, '2', '2', '3', 0,
'2', '2', '4', 0, '2', '2', '5', 0, '2', '2', '6', 0, '2', '2', '7', 0, '2', '2', '8', 0, '2', '2', '9', 0, '2', '3', '0', 0, '2', '3', '1', 0,
'2', '3', '2', 0, '2', '3', '3', 0, '2', '3', '4', 0, '2', '3', '5', 0, '2', '3', '6', 0, '2', '3', '7', 0, '2', '3', '8', 0, '2', '3', '9', 0,
'2', '4', '0', 0, '2', '4', '1', 0, '2', '4', '2', 0, '2', '4', '3', 0, '2', '4', '4', 0, '2', '4', '5', 0, '2', '4', '6', 0, '2', '4', '7', 0,
'2', '4', '8', 0, '2', '4', '9', 0, '2', '5', '0', 0, '2', '5', '1', 0, '2', '5', '2', 0, '2', '5', '3', 0, '2', '5', '4', 0, '2', '5', '5', 0,
};
static inline char *
print_u8(char *s, u8 n)
{
memcpy(s, numbers + n * 4, 4);
return s + 1 + (n >= 10) + (n >= 100);
}
char *
ip4_ntop(ip4_addr a, char *b)
{
u32 x = _I(a);
b = print_u8(b, (x >> 24) & 0xff);
*b++ = '.';
b = print_u8(b, (x >> 16) & 0xff);
*b++ = '.';
b = print_u8(b, (x >> 8) & 0xff);
*b++ = '.';
b = print_u8(b, x & 0xff);
return b;
}
char *
ip4_px_ntop(ip4_addr a, int len, char *b)
{
u32 x = _I(a);
b = print_u8(b, (x >> 24) & 0xff);
*b++ = '.';
b = print_u8(b, (x >> 16) & 0xff);
*b++ = '.';
b = print_u8(b, (x >> 8) & 0xff);
*b++ = '.';
b = print_u8(b, x & 0xff);
*b++ = '/';
b = print_u8(b, len & 0xff);
return b;
}
/*
* Conversion of IPv6 address to presentation format and vice versa.
@ -163,15 +288,6 @@ ip6_classify(ip6_addr *a)
* and of course by RFC 2373.
*/
char *
ip4_ntop(ip4_addr a, char *b)
{
u32 x = _I(a);
return b + bsprintf(b, "%d.%d.%d.%d", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff);
}
char *
ip6_ntop(ip6_addr a, char *b)
{

View File

@ -354,6 +354,9 @@ static inline ip4_addr ip4_setbits(ip4_addr a, uint pos, uint val)
static inline ip6_addr ip6_setbits(ip6_addr a, uint pos, uint val)
{ a.addr[pos / 32] |= val << (31 - pos % 32); return a; }
ip6_addr ip6_shift_left(ip6_addr a, uint bits);
ip6_addr ip6_shift_right(ip6_addr a, uint bits);
static inline ip4_addr ip4_opposite_m1(ip4_addr a)
{ return _MI4(_I(a) ^ 1); }
@ -398,7 +401,7 @@ typedef struct mpls_label_stack {
u32 stack[MPLS_MAX_LABEL_STACK];
} mpls_label_stack;
static inline int
static inline int ACCESS_READ(1, 2)
mpls_get(const char *buf, int buflen, u32 *stack)
{
for (int i=0; (i<MPLS_MAX_LABEL_STACK) && (i*4+3 < buflen); i++)
@ -454,9 +457,14 @@ static inline void * put_ip6(void *buf, ip6_addr a)
* Binary/text form conversions
*/
#define IP4_BUFFER_SIZE 16 /* Required buffer for ip4_ntop() */
#define IP4_PX_BUFFER_SIZE 20 /* Required buffer for ip4_ntop_px() */
char *ip4_ntop(ip4_addr a, char *b);
char *ip6_ntop(ip6_addr a, char *b);
char *ip4_px_ntop(ip4_addr a, int len, char *b);
static inline char * ip4_ntox(ip4_addr a, char *b)
{ return b + bsprintf(b, "%08x", _I(a)); }

View File

@ -231,6 +231,66 @@ t_ip6_prefix_equal(void)
return 1;
}
static int
t_ip6_shift_left(void)
{
ip6_addr a = ip6_build(0x8D0D8BDC, 0x1F04DB92, 0xE5117673, 0x70E54449);
struct { int n; ip6_addr val; } test_vectors[] = {
0, ip6_build(0x8D0D8BDC, 0x1F04DB92, 0xE5117673, 0x70E54449),
9, ip6_build(0x1B17B83E, 0x09B725CA, 0x22ECE6E1, 0xCA889200),
18, ip6_build(0x2F707C13, 0x6E4B9445, 0xD9CDC395, 0x11240000),
27, ip6_build(0xE0F826DC, 0x97288BB3, 0x9B872A22, 0x48000000),
36, ip6_build(0xF04DB92E, 0x51176737, 0x0E544490, 0x00000000),
45, ip6_build(0x9B725CA2, 0x2ECE6E1C, 0xA8892000, 0x00000000),
54, ip6_build(0xE4B9445D, 0x9CDC3951, 0x12400000, 0x00000000),
63, ip6_build(0x7288BB39, 0xB872A224, 0x80000000, 0x00000000),
72, ip6_build(0x11767370, 0xE5444900, 0x00000000, 0x00000000),
81, ip6_build(0xECE6E1CA, 0x88920000, 0x00000000, 0x00000000),
90, ip6_build(0xCDC39511, 0x24000000, 0x00000000, 0x00000000),
99, ip6_build(0x872A2248, 0x00000000, 0x00000000, 0x00000000),
108, ip6_build(0x54449000, 0x00000000, 0x00000000, 0x00000000),
117, ip6_build(0x89200000, 0x00000000, 0x00000000, 0x00000000),
126, ip6_build(0x40000000, 0x00000000, 0x00000000, 0x00000000),
128, ip6_build(0x00000000, 0x00000000, 0x00000000, 0x00000000),
};
for (uint i = 0; i < ARRAY_SIZE(test_vectors); i++)
bt_assert(ip6_equal(ip6_shift_left(a, test_vectors[i].n), test_vectors[i].val));
return 1;
}
static int
t_ip6_shift_right(void)
{
ip6_addr a = ip6_build(0x8D0D8BDC, 0x1F04DB92, 0xE5117673, 0x70E54449);
struct { int n; ip6_addr val; } test_vectors[] = {
0, ip6_build(0x8D0D8BDC, 0x1F04DB92, 0xE5117673, 0x70E54449),
9, ip6_build(0x004686C5, 0xEE0F826D, 0xC97288BB, 0x39B872A2),
18, ip6_build(0x00002343, 0x62F707C1, 0x36E4B944, 0x5D9CDC39),
27, ip6_build(0x00000011, 0xA1B17B83, 0xE09B725C, 0xA22ECE6E),
36, ip6_build(0x00000000, 0x08D0D8BD, 0xC1F04DB9, 0x2E511767),
45, ip6_build(0x00000000, 0x0004686C, 0x5EE0F826, 0xDC97288B),
54, ip6_build(0x00000000, 0x00000234, 0x362F707C, 0x136E4B94),
63, ip6_build(0x00000000, 0x00000001, 0x1A1B17B8, 0x3E09B725),
72, ip6_build(0x00000000, 0x00000000, 0x008D0D8B, 0xDC1F04DB),
81, ip6_build(0x00000000, 0x00000000, 0x00004686, 0xC5EE0F82),
90, ip6_build(0x00000000, 0x00000000, 0x00000023, 0x4362F707),
99, ip6_build(0x00000000, 0x00000000, 0x00000000, 0x11A1B17B),
108, ip6_build(0x00000000, 0x00000000, 0x00000000, 0x0008D0D8),
117, ip6_build(0x00000000, 0x00000000, 0x00000000, 0x00000468),
126, ip6_build(0x00000000, 0x00000000, 0x00000000, 0x00000002),
128, ip6_build(0x00000000, 0x00000000, 0x00000000, 0x00000000),
};
for (uint i = 0; i < ARRAY_SIZE(test_vectors); i++)
bt_assert(ip6_equal(ip6_shift_right(a, test_vectors[i].n), test_vectors[i].val));
return 1;
}
int
main(int argc, char *argv[])
{
@ -242,6 +302,8 @@ main(int argc, char *argv[])
bt_test_suite(t_ip6_ntop, "Converting ip6_addr struct to IPv6 string");
bt_test_suite(t_ip4_prefix_equal, "Testing ip4_prefix_equal()");
bt_test_suite(t_ip6_prefix_equal, "Testing ip6_prefix_equal()");
bt_test_suite(t_ip6_shift_left, "Testing ip6_shift_left()");
bt_test_suite(t_ip6_shift_right, "Testing ip6_shift_right()");
return bt_exit_value();
}

View File

@ -16,6 +16,7 @@ const char * const net_label[] = {
[NET_FLOW6] = "flow6",
[NET_IP6_SADR]= "ipv6-sadr",
[NET_MPLS] = "mpls",
[NET_ASPA] = "aspa",
};
const u16 net_addr_length[] = {
@ -29,6 +30,7 @@ const u16 net_addr_length[] = {
[NET_FLOW6] = 0,
[NET_IP6_SADR]= sizeof(net_addr_ip6_sadr),
[NET_MPLS] = sizeof(net_addr_mpls),
[NET_ASPA] = sizeof(net_addr_aspa),
};
const u8 net_max_prefix_length[] = {
@ -42,6 +44,7 @@ const u8 net_max_prefix_length[] = {
[NET_FLOW6] = IP6_MAX_PREFIX_LENGTH,
[NET_IP6_SADR]= IP6_MAX_PREFIX_LENGTH,
[NET_MPLS] = 0,
[NET_ASPA] = 0,
};
const u16 net_max_text_length[] = {
@ -55,6 +58,7 @@ const u16 net_max_text_length[] = {
[NET_FLOW6] = 0, /* "flow6 { ... }" */
[NET_IP6_SADR]= 92, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128 from ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
[NET_MPLS] = 7, /* "1048575" */
[NET_ASPA] = 10, /* "4294967295" */
};
/* There should be no implicit padding in net_addr structures */
@ -69,6 +73,7 @@ STATIC_ASSERT(sizeof(net_addr_flow4) == 8);
STATIC_ASSERT(sizeof(net_addr_flow6) == 20);
STATIC_ASSERT(sizeof(net_addr_ip6_sadr) == 40);
STATIC_ASSERT(sizeof(net_addr_mpls) == 8);
STATIC_ASSERT(sizeof(net_addr_aspa) == 8);
int
@ -95,7 +100,7 @@ net_format(const net_addr *N, char *buf, int buflen)
switch (n->n.type)
{
case NET_IP4:
return bsnprintf(buf, buflen, "%I4/%d", n->ip4.prefix, n->ip4.pxlen);
return (buflen < IP4_PX_BUFFER_SIZE) ? -1 : ip4_px_ntop(n->ip4.prefix, n->ip4.pxlen, buf) - buf;
case NET_IP6:
return bsnprintf(buf, buflen, "%I6/%d", n->ip6.prefix, n->ip6.pxlen);
case NET_VPN4:
@ -123,6 +128,8 @@ net_format(const net_addr *N, char *buf, int buflen)
return bsnprintf(buf, buflen, "%I6/%d from %I6/%d", n->ip6_sadr.dst_prefix, n->ip6_sadr.dst_pxlen, n->ip6_sadr.src_prefix, n->ip6_sadr.src_pxlen);
case NET_MPLS:
return bsnprintf(buf, buflen, "%u", n->mpls.label);
case NET_ASPA:
return bsnprintf(buf, buflen, "%u", n->aspa.asn);
}
bug("unknown network type");
@ -147,6 +154,7 @@ net_pxmask(const net_addr *a)
return ipa_from_ip6(ip6_mkmask(net6_pxlen(a)));
case NET_MPLS:
case NET_ASPA:
default:
return IPA_NONE;
}
@ -180,6 +188,8 @@ net_compare(const net_addr *a, const net_addr *b)
return net_compare_ip6_sadr((const net_addr_ip6_sadr *) a, (const net_addr_ip6_sadr *) b);
case NET_MPLS:
return net_compare_mpls((const net_addr_mpls *) a, (const net_addr_mpls *) b);
case NET_ASPA:
return net_compare_aspa((const net_addr_aspa *) a, (const net_addr_aspa *) b);
}
return 0;
}
@ -201,6 +211,7 @@ net_hash(const net_addr *n)
case NET_FLOW6: return NET_HASH(n, flow6);
case NET_IP6_SADR: return NET_HASH(n, ip6_sadr);
case NET_MPLS: return NET_HASH(n, mpls);
case NET_ASPA: return NET_HASH(n, aspa);
default: bug("invalid type");
}
}
@ -223,6 +234,7 @@ net_validate(const net_addr *n)
case NET_FLOW6: return NET_VALIDATE(n, flow6);
case NET_IP6_SADR: return NET_VALIDATE(n, ip6_sadr);
case NET_MPLS: return NET_VALIDATE(n, mpls);
case NET_ASPA: return NET_VALIDATE(n, aspa);
default: return 0;
}
}
@ -250,6 +262,7 @@ net_normalize(net_addr *N)
return net_normalize_ip6_sadr(&n->ip6_sadr);
case NET_MPLS:
case NET_ASPA:
return;
}
}
@ -277,6 +290,7 @@ net_classify(const net_addr *N)
return ip6_zero(n->ip6_sadr.dst_prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->ip6_sadr.dst_prefix);
case NET_MPLS:
case NET_ASPA:
return IADDR_HOST | SCOPE_UNIVERSE;
}
@ -310,6 +324,7 @@ ipa_in_netX(const ip_addr a, const net_addr *n)
ip6_mkmask(net6_pxlen(n))));
case NET_MPLS:
case NET_ASPA:
default:
return 0;
}

View File

@ -23,7 +23,8 @@
#define NET_FLOW6 8
#define NET_IP6_SADR 9
#define NET_MPLS 10
#define NET_MAX 11
#define NET_ASPA 11
#define NET_MAX 12
#define NB_IP4 (1 << NET_IP4)
#define NB_IP6 (1 << NET_IP6)
@ -35,6 +36,7 @@
#define NB_FLOW6 (1 << NET_FLOW6)
#define NB_IP6_SADR (1 << NET_IP6_SADR)
#define NB_MPLS (1 << NET_MPLS)
#define NB_ASPA (1 << NET_ASPA)
#define NB_IP (NB_IP4 | NB_IP6)
#define NB_VPN (NB_VPN4 | NB_VPN6)
@ -124,6 +126,13 @@ typedef struct net_addr_mpls {
u32 label;
} net_addr_mpls;
typedef struct net_addr_aspa {
u8 type;
u8 pxlen;
u16 length;
u32 asn;
} net_addr_aspa;
typedef struct net_addr_ip6_sadr {
u8 type;
u8 dst_pxlen;
@ -145,6 +154,7 @@ typedef union net_addr_union {
net_addr_flow6 flow6;
net_addr_ip6_sadr ip6_sadr;
net_addr_mpls mpls;
net_addr_aspa aspa;
} net_addr_union;
@ -201,6 +211,9 @@ extern const u16 net_max_text_length[];
#define NET_ADDR_IP6_SADR(dst_prefix,dst_pxlen,src_prefix,src_pxlen) \
((net_addr_ip6_sadr) { NET_IP6_SADR, dst_pxlen, sizeof(net_addr_ip6_sadr), dst_prefix, src_pxlen, src_prefix })
#define NET_ADDR_ASPA(asn) \
((net_addr_aspa) { NET_ASPA, 32, sizeof(net_addr_aspa), asn })
#define NET_ADDR_MPLS(label) \
((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label })
@ -229,6 +242,9 @@ static inline void net_fill_ip6_sadr(net_addr *a, ip6_addr dst_prefix, uint dst_
static inline void net_fill_mpls(net_addr *a, u32 label)
{ *(net_addr_mpls *)a = NET_ADDR_MPLS(label); }
static inline void net_fill_aspa(net_addr *a, u32 asn)
{ *(net_addr_aspa *)a = NET_ADDR_ASPA(asn); }
static inline void net_fill_ipa(net_addr *a, ip_addr prefix, uint pxlen)
{
if (ipa_is_ip4(prefix))
@ -287,6 +303,9 @@ static inline int net_is_roa(const net_addr *a)
static inline int net_is_flow(const net_addr *a)
{ return (a->type == NET_FLOW4) || (a->type == NET_FLOW6); }
static inline int net_is_aspa(const net_addr *a)
{ return (a->type == NET_ASPA); }
static inline int net_is_sadr(const net_addr *a)
{ return (a->type == NET_IP6_SADR); }
@ -314,6 +333,7 @@ static inline ip_addr net_prefix(const net_addr *a)
return ipa_from_ip6(net6_prefix(a));
case NET_MPLS:
case NET_ASPA:
default:
return IPA_NONE;
}
@ -384,6 +404,9 @@ static inline int net_equal_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_
static inline int net_equal_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
{ return !memcmp(a, b, sizeof(net_addr_mpls)); }
static inline int net_equal_aspa(const net_addr_aspa *a, const net_addr_aspa *b)
{ return !memcmp(a, b, sizeof(net_addr_aspa)); }
static inline int net_equal_prefix_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
{ return ip4_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
@ -425,6 +448,9 @@ static inline int net_zero_flow6(const net_addr_flow6 *a)
static inline int net_zero_mpls(const net_addr_mpls *a)
{ return !a->label; }
static inline int net_zero_aspa(const net_addr_aspa *a)
{ return !a->asn; }
static inline int net_compare_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
@ -460,6 +486,9 @@ static inline int net_compare_ip6_sadr(const net_addr_ip6_sadr *a, const net_add
static inline int net_compare_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
{ return uint_cmp(a->label, b->label); }
static inline int net_compare_aspa(const net_addr_aspa *a, const net_addr_aspa *b)
{ return uint_cmp(a->asn, b->asn); }
int net_compare(const net_addr *a, const net_addr *b);
@ -496,6 +525,9 @@ static inline void net_copy_ip6_sadr(net_addr_ip6_sadr *dst, const net_addr_ip6_
static inline void net_copy_mpls(net_addr_mpls *dst, const net_addr_mpls *src)
{ memcpy(dst, src, sizeof(net_addr_mpls)); }
static inline void net_copy_aspa(net_addr_aspa *dst, const net_addr_aspa *src)
{ memcpy(dst, src, sizeof(net_addr_aspa)); }
static inline u32 px4_hash(ip4_addr prefix, u32 pxlen)
{ return ip4_hash(prefix) ^ (pxlen << 26); }
@ -539,6 +571,9 @@ static inline u32 net_hash_ip6_sadr(const net_addr_ip6_sadr *n)
static inline u32 net_hash_mpls(const net_addr_mpls *n)
{ return u32_hash(n->label); }
static inline u32 net_hash_aspa(const net_addr_aspa *n)
{ return u32_hash(n->asn); }
u32 net_hash(const net_addr *a);
@ -588,6 +623,9 @@ static inline int net_validate_flow6(const net_addr_flow6 *n)
static inline int net_validate_mpls(const net_addr_mpls *n)
{ return n->label < (1 << 20); }
static inline int net_validate_aspa(const net_addr_aspa *n)
{ return n->asn > 0; }
static inline int net_validate_ip6_sadr(const net_addr_ip6_sadr *n)
{ return net_validate_px6(n->dst_prefix, n->dst_pxlen) && net_validate_px6(n->src_prefix, n->src_pxlen); }
@ -616,8 +654,8 @@ void net_normalize(net_addr *N);
int net_classify(const net_addr *N);
int net_format(const net_addr *N, char *buf, int buflen);
int rd_format(const u64 rd, char *buf, int buflen);
int net_format(const net_addr *N, char *buf, int buflen) ACCESS_WRITE(2, 3);
int rd_format(const u64 rd, char *buf, int buflen) ACCESS_WRITE(2, 3);
static inline int ipa_in_px4(ip4_addr a, ip4_addr prefix, uint pxlen)
{ return ip4_zero(ip4_and(ip4_xor(a, prefix), ip4_mkmask(pxlen))); }

View File

@ -73,10 +73,25 @@ static char * number(char * str, u64 num, uint base, int size, int precision,
i = 0;
if (num == 0)
tmp[i++]='0';
else while (num != 0) {
uint res = num % base;
num = num / base;
tmp[i++] = digits[res];
else if (base == 10) {
/* Separate cases to have fixed divisors */
while (num != 0) {
uint res = num % 10;
num = num / 10;
tmp[i++] = digits[res];
}
} else if (base == 16) {
while (num != 0) {
uint res = num % 16;
num = num / 16;
tmp[i++] = digits[res];
}
} else {
while (num != 0) {
uint res = num % base;
num = num / base;
tmp[i++] = digits[res];
}
}
if (i > precision)
precision = i;

View File

@ -81,9 +81,9 @@ static inline pool *resource_parent(resource *r)
/* Normal memory blocks */
void *mb_alloc(pool *, unsigned size);
void *mb_allocz(pool *, unsigned size);
void *mb_realloc(void *m, unsigned size);
void *mb_alloc(pool *, unsigned size) ALLOC_SIZE(2);
void *mb_allocz(pool *, unsigned size) ALLOC_SIZE(2);
void *mb_realloc(void *m, unsigned size) ALLOC_SIZE(2);
void mb_free(void *);
/* Memory pools with linear allocation */
@ -97,9 +97,9 @@ typedef struct lp_state {
} lp_state;
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 */
void *lp_alloc(linpool *, unsigned size) ALLOC_SIZE(2); /* Aligned */
void *lp_allocu(linpool *, unsigned size) ALLOC_SIZE(2); /* Unaligned */
void *lp_allocz(linpool *, unsigned size) ALLOC_SIZE(2); /* With clear */
void lp_flush(linpool *); /* Free everything, but leave linpool */
lp_state *lp_save(linpool *m); /* Save state */
void lp_restore(linpool *m, lp_state *p); /* Restore state */

View File

@ -278,6 +278,7 @@ struct ea_class {
void (*format)(const eattr *ea, byte *buf, uint size); \
void (*stored)(const eattr *ea); /* When stored into global hash */ \
void (*freed)(const eattr *ea); /* When released from global hash */ \
struct f_val (*empty)(const struct ea_class *); /* Return this instead of T_VOID as default value for filters */ \
EA_CLASS_INSIDE;
};
@ -557,6 +558,10 @@ static inline int rte_dest(const rte *r)
return nhea_dest(ea_find(r->attrs, &ea_gen_nexthop));
}
/* ASPA Providers eattr */
extern struct ea_class ea_gen_aspa_providers;
void rta_init(void);
ea_list *ea_lookup_slow(ea_list *r, u32 squash_upto, enum ea_stored oid);

View File

@ -139,6 +139,8 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou
#define SKF_HDRINCL 0x400 /* Used internally */
#define SKF_PKTINFO 0x800 /* Used internally */
#define SKF_UDP6_NO_CSUM_RX 0x1000 /* Accept zero checksums for received UDPv6 packets */
/*
* Socket types SA SP DA DP IF TTL SendTo (?=may, -=must not, *=must)
*/

View File

@ -17,8 +17,8 @@
int bsprintf(char *str, const char *fmt, ...);
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);
int bsnprintf(char *str, int size, const char *fmt, ...) ACCESS_WRITE(1, 2);
int bvsnprintf(char *str, int size, const char *fmt, va_list args) ACCESS_WRITE(1, 2);
char *mb_sprintf(pool *p, const char *fmt, ...);
char *mb_vsprintf(pool *p, const char *fmt, va_list args);
@ -34,7 +34,7 @@ u64 bstrtoul16(const char *str, char **end);
byte bstrtobyte16(const char *str);
int bstrhextobin(const char *s, byte *b);
int bstrbintohex(const byte *b, size_t len, char *buf, size_t size, char delim);
int bstrbintohex(const byte *b, size_t len, char *buf, size_t size, char delim) ACCESS_READ(1, 2) ACCESS_WRITE(3, 4);
int patmatch(const byte *pat, const byte *str);

View File

@ -68,9 +68,9 @@ tm_dump(resource *r, unsigned indent UNUSED)
if (t->randomize)
debug("rand %d, ", t->randomize);
if (t->recurrent)
debug("recur %d, ", t->recurrent);
debug("recur %ld, ", t->recurrent);
if (t->expires)
debug("in loop %p expires in %d ms)\n", t->loop, (t->expires - current_time()) TO_MS);
debug("in loop %p expires in %ld ms)\n", t->loop, (t->expires - current_time()) TO_MS);
else
debug("inactive)\n");
}

View File

@ -28,8 +28,8 @@ typedef struct timer
void *data;
btime expires; /* 0=inactive */
btime recurrent; /* Timer recurrence */
uint randomize; /* Amount of randomization */
uint recurrent; /* Timer recurrence */
struct timeloop *loop; /* Loop where the timer is active */
@ -84,7 +84,7 @@ tm_remains(timer *t)
}
static inline timer *
tm_new_init(pool *p, void (*hook)(struct timer *), void *data, uint rec, uint rand)
tm_new_init(pool *p, void (*hook)(struct timer *), void *data, btime rec, uint rand)
{
timer *t = tm_new(p);
t->hook = hook;

View File

@ -193,6 +193,7 @@ static inline void TLIST_NAME(rem_node)(TLIST_LIST_STRUCT *list, TLIST_TYPE *nod
#undef TLIST_WANT_ADD_HEAD
#undef TLIST_WANT_ADD_TAIL
#undef TLIST_WANT_UPDATE_NODE
#undef TLIST_DEFINED_BEFORE
# endif
#else

View File

@ -91,12 +91,13 @@ enum btype {
T_ENUM_LO = 0x12,
T_ENUM_HI = 0x3f,
T_ENUM_ASPA = 0x2f, /* ASPA validation result */
T_ENUM_RTS = 0x31,
T_ENUM_SCOPE = 0x33,
T_ENUM_MPLS_POLICY = 0x35,
T_ENUM_RTD = 0x37,
T_ENUM_ROA = 0x39,
T_ENUM_NETTYPE = 0x3b,
T_ENUM_NET_TYPE = 0x3b,
T_ENUM_AF = 0x3d,
/* new enums go here */

View File

@ -21,6 +21,8 @@ struct bfd_options {
u8 passive;
u8 passive_set;
u8 mode;
u8 auth_type; /* Authentication type (BFD_AUTH_*) */
list *passwords; /* Passwords for authentication */
};
struct bfd_request {

View File

@ -272,7 +272,7 @@ cli_event(void *data)
}
cli *
cli_new(struct birdsock *sock)
cli_new(struct birdsock *sock, struct cli_config *cf)
{
pool *p = rp_new(cli_pool, the_bird_domain.the_bird, "CLI");
cli *c = mb_alloc(p, sizeof(cli));
@ -286,6 +286,10 @@ cli_new(struct birdsock *sock)
c->cont = cli_hello;
c->parser_pool = lp_new_default(c->pool);
c->rx_buf = mb_alloc(c->pool, CLI_RX_BUF_SIZE);
if (cf->restricted)
c->restricted = 1;
ev_schedule(c->event);
return c;
}
@ -300,6 +304,36 @@ cli_kick(cli *c)
static list cli_log_hooks;
static int cli_log_inited;
/* Set time format override for the current session */
void
cli_set_timeformat(cli *c, const struct timeformat tf)
{
size_t len1 = strlen(tf.fmt1) + 1;
size_t len2 = tf.fmt2 ? strlen(tf.fmt2) + 1 : 0;
if (len1 > TM_DATETIME_BUFFER_SIZE || len2 > TM_DATETIME_BUFFER_SIZE)
{
cli_msg(9003, "Format string too long");
return;
}
struct timeformat *old_tf = c->tf;
struct timeformat *new_tf = mb_allocz(c->pool, sizeof(struct timeformat));
new_tf->fmt1 = memcpy(mb_alloc(c->pool, len1), tf.fmt1, len1);
new_tf->fmt2 = tf.fmt2 ? memcpy(mb_alloc(c->pool, len2), tf.fmt2, len2) : NULL;
new_tf->limit = tf.limit;
c->tf = new_tf;
if (old_tf)
{
mb_free((void *) old_tf->fmt1);
mb_free((void *) old_tf->fmt2);
mb_free(old_tf);
}
cli_msg(0, "");
}
/* Hack for scheduled undo notification */
extern cli *cmd_reconfig_stored_cli;

View File

@ -12,6 +12,9 @@
#include "lib/resource.h"
#include "lib/lists.h"
#include "lib/event.h"
#include "lib/timer.h"
#include "lib/tlists.h"
#include "conf/conf.h"
#define CLI_RX_BUF_SIZE 4096
#define CLI_TX_BUF_SIZE 4096
@ -39,12 +42,30 @@ typedef struct cli {
struct config *main_config; /* Main config currently in use */
int last_reply;
int restricted; /* CLI is restricted to read-only commands */
struct timeformat *tf; /* Time format override */
struct linpool *parser_pool; /* Pool used during parsing */
uint log_mask; /* Mask of allowed message levels */
uint log_threshold; /* When free < log_threshold, store only important messages */
uint async_msg_size; /* Total size of async messages queued in tx_buf */
} cli;
struct cli_config {
#define TLIST_PREFIX cli_config
#define TLIST_TYPE struct cli_config
#define TLIST_ITEM n
#define TLIST_DEFINED_BEFORE
#define TLIST_WANT_ADD_TAIL
#define TLIST_WANT_WALK
TLIST_DEFAULT_NODE;
const char *name;
struct config *config;
uint uid, gid, mode;
_Bool restricted;
};
#include "lib/tlists.h"
void cli_config_listen(struct cli_config *, const char *);
extern pool *cli_pool;
extern struct cli *this_cli; /* Used during parsing */
@ -54,13 +75,14 @@ extern struct cli *this_cli; /* Used during parsing */
void cli_printf(cli *, int, char *, ...);
#define cli_msg(x...) cli_printf(this_cli, x)
void cli_set_timeformat(cli *c, const struct timeformat tf);
static inline void cli_separator(cli *c)
{ if (c->last_reply) cli_printf(c, -c->last_reply, ""); };
/* Functions provided to sysdep layer */
cli *cli_new(struct birdsock *);
cli *cli_new(struct birdsock *, struct cli_config *);
void cli_init(void);
void cli_free(cli *);
void cli_kick(cli *);

View File

@ -22,11 +22,10 @@ extern int configuring;
void
cmd_show_status(void)
{
byte tim[TM_DATETIME_BUFFER_SIZE];
rcu_read_lock();
struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_acquire);
struct timeformat *tf = &gr->tf_base;
struct timeformat *tf = this_cli->tf ?: &gr->tf_base;
byte tim[TM_DATETIME_BUFFER_SIZE];
cli_msg(-1000, "BIRD " BIRD_VERSION);
tm_format_time(tim, tf, current_time());

View File

@ -153,7 +153,7 @@ CF_DECLS
CF_KEYWORDS(ROUTER, ID, HOSTNAME, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT, PIPE)
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, DEFAULT, TABLE, TABLES, STATES, ROUTES, FILTERS)
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS)
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS, ASPA)
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED, RPKI)
CF_KEYWORDS(PASSWORD, KEY, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, CHANNELS, INTERFACES)
CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512, BLAKE2S128, BLAKE2S256, BLAKE2B256, BLAKE2B512)
@ -162,20 +162,22 @@ CF_KEYWORDS(BGP, PASSWORDS, DESCRIPTION)
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, CLASS, DSCP, PARTIAL)
CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, AS)
CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
CF_KEYWORDS(CHECK, LINK)
CF_KEYWORDS(CORK, SORTED, TRIE, MIN, MAX, ROA, DIGEST, ROUTE, REFRESH, SETTLE, TIME, GC, THRESHOLD, PERIOD)
CF_KEYWORDS(MPLS_LABEL, MPLS_POLICY, MPLS_CLASS)
CF_KEYWORDS(ASPA_PROVIDERS)
/* For r_args_channel */
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC, ASPA)
CF_ENUM(T_ENUM_NET_TYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP6_SADR, MPLS, ASPA)
CF_ENUM(T_ENUM_RTS, RTS_, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL, RPKI, L3VPN,
AGGREGATED)
CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
CF_ENUM(T_ENUM_ASPA, ASPA_, UNKNOWN, VALID, INVALID)
CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE, VRF)
@ -189,6 +191,8 @@ CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE, VRF)
%type <ps> proto_patt proto_patt2
%type <cc> channel_start proto_channel
%type <cl> limit_spec
%type <tf> timeformat_spec
%type <tfp> timeformat_which
%type <net> r_args_for_val
%type <net_ptr> r_args_for
%type <t> channel_sym
@ -241,6 +245,7 @@ net_type_base:
| ROA6 { $$ = NET_ROA6; }
| FLOW4{ $$ = NET_FLOW4; }
| FLOW6{ $$ = NET_FLOW6; }
| ASPA { $$ = NET_ASPA; }
;
net_type:
@ -248,8 +253,6 @@ net_type:
| MPLS { $$ = NET_MPLS; }
;
CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP6_SADR, MPLS)
/* Creation of routing tables */
@ -452,6 +455,10 @@ debug_default:
conf: timeformat_base ;
timeformat_base:
TIMEFORMAT timeformat_which timeformat_spec ';' { *$2 = $3; }
;
timeformat_which:
ROUTE { $$ = &new_config->tf_route; }
| PROTOCOL { $$ = &new_config->tf_proto; }
@ -460,18 +467,14 @@ timeformat_which:
;
timeformat_spec:
timeformat_which TEXT { *$1 = (struct timeformat){$2, NULL, 0}; }
| timeformat_which TEXT expr TEXT { *$1 = (struct timeformat){$2, $4, (s64) $3 S_}; }
| timeformat_which ISO SHORT { *$1 = TM_ISO_SHORT_S; }
| timeformat_which ISO SHORT MS { *$1 = TM_ISO_SHORT_MS; }
| timeformat_which ISO SHORT US { *$1 = TM_ISO_SHORT_US; }
| timeformat_which ISO LONG { *$1 = TM_ISO_LONG_S; }
| timeformat_which ISO LONG MS { *$1 = TM_ISO_LONG_MS; }
| timeformat_which ISO LONG US { *$1 = TM_ISO_LONG_US; }
;
timeformat_base:
TIMEFORMAT timeformat_spec ';'
TEXT { $$ = (struct timeformat){$1, NULL, 0}; }
| TEXT expr TEXT { $$ = (struct timeformat){$1, $3, (s64) $2 S_}; }
| ISO SHORT { $$ = TM_ISO_SHORT_S; }
| ISO SHORT MS { $$ = TM_ISO_SHORT_MS; }
| ISO SHORT US { $$ = TM_ISO_SHORT_US; }
| ISO LONG { $$ = TM_ISO_LONG_S; }
| ISO LONG MS { $$ = TM_ISO_LONG_MS; }
| ISO LONG US { $$ = TM_ISO_LONG_US; }
;
/* Interface patterns */
@ -662,26 +665,9 @@ password_item_end:
};
/* BFD options */
/* BFD options - just dummy rule, rest in proto/bfd/config.Y */
bfd_opts: '{' INVALID_TOKEN '}';
bfd_item:
INTERVAL expr_us { this_bfd_opts->min_rx_int = this_bfd_opts->min_tx_int = $2; }
| MIN RX INTERVAL expr_us { this_bfd_opts->min_rx_int = $4; }
| MIN TX INTERVAL expr_us { this_bfd_opts->min_tx_int = $4; }
| IDLE TX INTERVAL expr_us { this_bfd_opts->idle_tx_int = $4; }
| MULTIPLIER expr { this_bfd_opts->multiplier = $2; }
| PASSIVE bool { this_bfd_opts->passive = $2; this_bfd_opts->passive_set = 1; }
| GRACEFUL { this_bfd_opts->mode = BGP_BFD_GRACEFUL; }
;
bfd_items:
/* empty */
| bfd_items bfd_item ';'
;
bfd_opts:
'{' bfd_items '}'
;
/* Core commands */
CF_CLI_HELP(SHOW, ..., [[Show status information]])
@ -908,6 +894,7 @@ channel_sym:
| FLOW4 { $$ = "flow4"; }
| FLOW6 { $$ = "flow6"; }
| MPLS { $$ = "mpls"; }
| ASPA { $$ = "aspa"; }
| PRI { $$ = "pri"; }
| SEC { $$ = "sec"; }
;
@ -1019,6 +1006,16 @@ CF_CLI(MRTDUMP, proto_patt mrtdump_mask, (<protocol> | \"<pattern>\" | all) (all
CF_CLI(RESTRICT,,,[[Restrict current CLI session to safe commands]])
{ this_cli->restricted = 1; cli_msg(16, "Access restricted"); } ;
CF_CLI_HELP(TIMEFORMAT, ..., [[Set time format for this CLI session]])
CF_CLI(TIMEFORMAT, timeformat_spec, \"<format1>\" [limit \"format2\"] | iso (short | long) [ ms | us ], [[Set time format for this CLI session]])
{ cli_set_timeformat(this_cli, $2); } ;
CF_CLI_OPT(TIMEFORMAT ISO)
CF_CLI_OPT(TIMEFORMAT SHORT)
CF_CLI_OPT(TIMEFORMAT LONG)
CF_CLI_OPT(TIMEFORMAT MS)
CF_CLI_OPT(TIMEFORMAT US)
proto_patt:
CF_SYM_KNOWN { cf_assert_symbol($1, SYM_PROTO); $$.ptr = $1; $$.patt = 0; }
| ALL { $$.ptr = NULL; $$.patt = 1; }

View File

@ -232,7 +232,8 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
struct ifa *addr = NULL;
WALK_LIST(n, neigh_hash_table[h]) /* Search the cache */
if ((n->proto == p) && ipa_equal(n->addr, a) && (n->ifreq == iface))
if ((n->proto == p) && ipa_equal(n->addr, a) && (n->ifreq == iface) &&
((n->flags & NEF_ONLINK) == (flags & NEF_ONLINK)))
{
IFACE_UNLOCK;
return n;

View File

@ -613,6 +613,11 @@ channel_roa_subscribe_filter(struct channel *c, int dir)
found = 1;
break;
case FI_ASPA_CHECK_EXPLICIT:
tab = fi->i_FI_ASPA_CHECK_EXPLICIT.rtc->table;
if (valid) channel_roa_subscribe(c, tab, dir);
found = 1;
break;
default:
break;
}
@ -2610,7 +2615,7 @@ proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt)
p->proto->get_status(p, buf);
rcu_read_lock();
tm_format_time(tbuf, &atomic_load_explicit(&global_runtime, memory_order_acquire)->tf_proto, p->last_state_change);
tm_format_time(tbuf, this_cli->tf ?: &atomic_load_explicit(&global_runtime, memory_order_acquire)->tf_proto, p->last_state_change);
rcu_read_unlock();
cli_msg(-1002, "%-10s %-10s %-10s %-6s %-12s %s",
p->name,

View File

@ -33,6 +33,7 @@
#include <stdatomic.h>
struct ea_list;
struct adata;
struct protocol;
struct proto;
struct channel;
@ -928,6 +929,13 @@ void ea_show_nexthop_list(struct cli *c, struct nexthop_adata *nhad);
#define ROA_VALID 1
#define ROA_INVALID 2
enum aspa_result {
ASPA_UNKNOWN = 0,
ASPA_VALID,
ASPA_INVALID,
};
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
enum aspa_result aspa_check(rtable *tab, const struct adata *path, bool force_upstream);
#endif

View File

@ -188,6 +188,18 @@ const char * flowspec_valid_names[FLOWSPEC__MAX] = {
[FLOWSPEC_INVALID] = "invalid",
};
static void
ea_gen_aspa_providers_format(const eattr *a, byte *buf, uint size)
{
int_set_format(a->u.ad, ISF_NUMBERS, -1, buf, size - 5);
}
struct ea_class ea_gen_aspa_providers = {
.name = "aspa_providers",
.type = T_CLIST,
.format = ea_gen_aspa_providers_format,
};
DOMAIN(attrs) attrs_domain;
pool *rta_pool;
@ -1454,7 +1466,7 @@ ea_show(struct cli *c, const eattr *e)
as_path_format(ad, pos, end - pos);
break;
case T_CLIST:
ea_show_int_set(c, cls->name, ad, 1, buf);
ea_show_int_set(c, cls->name, ad, ISF_COMMUNITY_LIST, buf);
return;
case T_ECLIST:
ea_show_ec_set(c, cls->name, ad, buf);
@ -1876,6 +1888,9 @@ rta_init(void)
ea_register_init(&ea_gen_mpls_policy);
ea_register_init(&ea_gen_mpls_class);
ea_register_init(&ea_gen_mpls_label);
/* ASPA providers */
ea_register_init(&ea_gen_aspa_providers);
}
/*

View File

@ -279,6 +279,7 @@ fib_find(struct fib *f, const net_addr *a)
case NET_FLOW6: return FIB_FIND(f, a, flow6);
case NET_IP6_SADR: return FIB_FIND(f, a, ip6_sadr);
case NET_MPLS: return FIB_FIND(f, a, mpls);
case NET_ASPA: return FIB_FIND(f, a, aspa);
default: bug("invalid type");
}
}
@ -300,6 +301,7 @@ fib_insert(struct fib *f, const net_addr *a, struct fib_node *e)
case NET_FLOW6: FIB_INSERT(f, a, e, flow6); return;
case NET_IP6_SADR: FIB_INSERT(f, a, e, ip6_sadr); return;
case NET_MPLS: FIB_INSERT(f, a, e, mpls); return;
case NET_ASPA: FIB_INSERT(f, a, e, aspa); return;
default: bug("invalid type");
}
}

View File

@ -50,7 +50,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
int dest = nhad ? (NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest) : RTD_NONE;
int flowspec_valid = net_is_flow(e->net) ? rt_get_flowspec_valid(e) : FLOWSPEC_UNKNOWN;
tm_format_time(tm, &d->tf_route, e->lastmod);
tm_format_time(tm, c->tf ?: &d->tf_route, e->lastmod);
ip_addr a_from = ea_get_ip(a, &ea_gen_from, IPA_NONE);
if (ipa_nonzero(a_from) && (!nhad || !ipa_equal(a_from, nhad->nh.gw)))
bsprintf(from, " from %I", a_from);

View File

@ -717,6 +717,136 @@ net_roa_check(rtable *tp, const net_addr *n, u32 asn)
#undef TW
}
/**
* aspa_check - check validity of AS Path in an ASPA table
* @tab: ASPA table
* @path: AS Path to check
*
* Implements draft-ietf-sidrops-aspa-verification-16.
*/
enum aspa_result aspa_check(rtable *tab, const adata *path, bool force_upstream)
{
/* Restore tmp linpool state after this check */
CLEANUP(lp_saved_cleanup) struct lp_state *_lps = lp_save(tmp_linpool);
/* No support for confed paths */
if (as_path_contains_confed(path))
return ASPA_INVALID;
/* Check path length */
uint len = as_path_getlen(path);
if (len == 0)
return ASPA_INVALID;
/* Normalize the AS Path: drop stuffings */
u32 *asns = alloca(sizeof(u32) * len);
uint ppos = 0;
uint nsz = 0;
while (as_path_walk(path, &ppos, &asns[nsz]))
if ((nsz == 0) || (asns[nsz] != asns[nsz-1]))
nsz++;
/* Find the provider blocks for every AS on the path
* and check allowed directions */
uint max_up = 0, min_up = 0, max_down = 0, min_down = 0;
RT_READ(tab, tr);
for (uint ap=0; ap<nsz; ap++)
{
net_addr_union nau = { .aspa = NET_ADDR_ASPA(asns[ap]), };
/* Find some ASPAs */
struct netindex *ni = net_find_index(tr->t->netindex, &nau.n);
net *n = ni ? net_find(tr, ni) : NULL;
bool found = false, down = false, up = false;
if (n) NET_READ_WALK_ROUTES(tr, n, ep, e)
{
if (!rte_is_valid(&e->rte))
continue;
eattr *ea = ea_find(e->rte.attrs, &ea_gen_aspa_providers);
if (!ea)
continue;
/* Actually found some ASPA */
found = true;
for (uint i=0; i * sizeof(u32) < ea->u.ptr->length; i++)
{
if ((ap > 0) && ((u32 *) ea->u.ptr->data)[i] == asns[ap-1])
up = true;
if ((ap + 1 < nsz) && ((u32 *) ea->u.ptr->data)[i] == asns[ap+1])
down = true;
if (down && up)
/* Both peers found */
goto end_of_aspa;
}
}
end_of_aspa:;
/* Fast path for the upstream check */
if (force_upstream)
{
if (!found)
/* Move min-upstream */
min_up = ap;
else if (ap && !up)
/* Exists but doesn't allow this upstream */
return ASPA_INVALID;
}
/* Fast path for no ASPA here */
else if (!found)
{
/* Extend max-downstream (min-downstream is stopped by unknown) */
max_down = ap+1;
/* Move min-upstream (can't include unknown) */
min_up = ap;
}
/* ASPA exists and downstream may be extended */
else if (down)
{
/* Extending max-downstream always */
max_down = ap+1;
/* Extending min-downstream unless unknown seen */
if (min_down == ap)
min_down = ap+1;
/* Downstream only */
if (!up)
min_up = max_up = ap;
}
/* No extension for downstream, force upstream only from now */
else
{
force_upstream = 1;
/* Not even upstream, move the ending here */
if (!up)
min_up = max_up = ap;
}
}
/* Is the path surely valid? */
if (min_up <= min_down)
return ASPA_VALID;
/* Is the path maybe valid? */
if (max_up <= max_down)
return ASPA_UNKNOWN;
/* Now there is surely a valley there. */
return ASPA_INVALID;
}
struct rte_storage *
rte_store(const rte *r, struct netindex *i, struct rtable_private *tab)
{

View File

@ -181,6 +181,8 @@ bfd_merge_options(const struct bfd_iface_config *cf, const struct bfd_options *o
.idle_tx_int = opts->idle_tx_int ?: cf->idle_tx_int,
.multiplier = opts->multiplier ?: cf->multiplier,
.passive = opts->passive_set ? opts->passive : cf->passive,
.auth_type = opts->auth_type ?: cf->auth_type,
.passwords = opts->passwords ?: cf->passwords,
};
}
@ -1187,7 +1189,8 @@ bfd_reconfigure(struct proto *P, struct proto_config *c)
(new->accept_ipv6 != old->accept_ipv6) ||
(new->accept_direct != old->accept_direct) ||
(new->accept_multihop != old->accept_multihop) ||
(new->strict_bind != old->strict_bind))
(new->strict_bind != old->strict_bind) ||
(new->zero_udp6_checksum_rx != old->zero_udp6_checksum_rx))
return 0;
birdloop_mask_wakeups(p->p.loop);
@ -1235,7 +1238,7 @@ bfd_show_session(struct bfd_session *s, int details)
const char *ifname = (s->ifa && s->ifa->iface) ? s->ifa->iface->name : "---";
btime tx_int = s->last_tx ? MAX(s->des_min_tx_int, s->rem_min_rx_int) : 0;
btime timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult;
u8 auth_type = s->ifa->cf->auth_type;
u8 auth_type = s->cf.auth_type;
loc_state = (loc_state < 4) ? loc_state : 0;
rem_state = (rem_state < 4) ? rem_state : 0;
@ -1245,7 +1248,7 @@ bfd_show_session(struct bfd_session *s, int details)
rcu_read_lock();
struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_relaxed);
tm_format_time(tbuf, &gr->tf_proto, s->last_state_change);
tm_format_time(tbuf, this_cli->tf ?: &gr->tf_proto, s->last_state_change);
rcu_read_unlock();
if (!details)

View File

@ -48,6 +48,7 @@ struct bfd_config
u8 accept_direct;
u8 accept_multihop;
u8 strict_bind;
u8 zero_udp6_checksum_rx;
};
struct bfd_iface_config
@ -69,6 +70,8 @@ struct bfd_session_config
u32 idle_tx_int;
u8 multiplier;
u8 passive;
u8 auth_type; /* Authentication type (BFD_AUTH_*) */
list *passwords; /* Passwords for authentication */
};
struct bfd_neighbor

View File

@ -24,7 +24,7 @@ CF_DECLS
CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE,
INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL, AUTHENTICATION,
NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1, IPV4, IPV6, DIRECT,
STRICT, BIND)
STRICT, BIND, ZERO, UDP6, CHECKSUM, RX)
%type <iface> bfd_neigh_iface
%type <a> bfd_neigh_local
@ -53,6 +53,7 @@ bfd_proto_item:
| MULTIHOP bfd_multihop
| NEIGHBOR bfd_neighbor
| STRICT BIND bool { BFD_CFG->strict_bind = $3; }
| ZERO UDP6 CHECKSUM RX bool { BFD_CFG->zero_udp6_checksum_rx = $5; }
;
bfd_proto_opts:
@ -185,6 +186,52 @@ bfd_neighbor: ipa bfd_neigh_iface bfd_neigh_local bfd_neigh_multihop
};
/* BFD options */
bfd_item:
INTERVAL expr_us { this_bfd_opts->min_rx_int = this_bfd_opts->min_tx_int = $2; }
| MIN RX INTERVAL expr_us { this_bfd_opts->min_rx_int = $4; }
| MIN TX INTERVAL expr_us { this_bfd_opts->min_tx_int = $4; }
| IDLE TX INTERVAL expr_us { this_bfd_opts->idle_tx_int = $4; }
| MULTIPLIER expr { this_bfd_opts->multiplier = $2; }
| PASSIVE bool { this_bfd_opts->passive = $2; this_bfd_opts->passive_set = 1; }
| GRACEFUL { this_bfd_opts->mode = BGP_BFD_GRACEFUL; }
| AUTHENTICATION bfd_auth_type { this_bfd_opts->auth_type = $2; }
| password_list {}
;
bfd_items:
/* empty */
| bfd_items bfd_item ';'
;
bfd_opts_start:
{ reset_passwords(); } ;
bfd_opts_end:
{
this_bfd_opts->passwords = get_passwords();
if (!this_bfd_opts->auth_type != !this_bfd_opts->passwords)
cf_warn("Authentication and password options should be used together");
if (this_bfd_opts->passwords)
{
struct password_item *pass;
WALK_LIST(pass, *this_bfd_opts->passwords)
{
if (pass->alg)
cf_error("Password algorithm option not available in BFD protocol");
pass->alg = bfd_auth_type_to_hash_alg[this_bfd_opts->auth_type];
}
}
};
bfd_opts:
'{' bfd_opts_start bfd_items '}' bfd_opts_end;
CF_CLI_HELP(SHOW BFD, ..., [[Show information about BFD protocol]]);
CF_CLI_HELP(SHOW BFD SESSIONS, ..., [[Show information about BFD sessions]]);

View File

@ -109,7 +109,7 @@ const u8 bfd_auth_type_to_hash_alg[] = {
static void
bfd_fill_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt)
{
struct bfd_iface_config *cf = s->ifa->cf;
struct bfd_session_config *cf = &s->cf;
struct password_item *pass = password_find(cf->passwords, 0);
uint meticulous = 0;
@ -179,7 +179,7 @@ bfd_fill_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_c
static int
bfd_check_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt)
{
struct bfd_iface_config *cf = s->ifa->cf;
struct bfd_session_config *cf = &s->cf;
const char *err_dsc = NULL;
uint err_val = 0;
uint auth_type = 0;
@ -306,7 +306,7 @@ bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final)
else if (s->poll_active)
pkt->flags |= BFD_FLAG_POLL;
if (s->ifa->cf->auth_type)
if (s->cf.auth_type)
bfd_fill_authentication(p, s, pkt);
if (sk->tbuf != sk->tpos)
@ -416,6 +416,8 @@ bfd_err_hook(sock *sk, int err)
sock *
bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af)
{
struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
sock *sk = sk_new(p->p.pool);
sk->type = SK_UDP;
sk->subtype = af;
@ -432,6 +434,9 @@ bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af)
sk->priority = sk_priority_control;
sk->flags = SKF_LADDR_RX | (!multihop ? SKF_TTL_RX : 0);
if (cf->zero_udp6_checksum_rx)
sk->flags |= SKF_UDP6_NO_CSUM_RX;
if (sk_open(sk, p->p.loop) < 0)
goto err;
@ -446,6 +451,8 @@ err:
sock *
bfd_open_rx_sk_bound(struct bfd_proto *p, ip_addr local, struct iface *ifa)
{
struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
sock *sk = sk_new(p->tpool);
sk->type = SK_UDP;
sk->saddr = local;
@ -463,6 +470,9 @@ bfd_open_rx_sk_bound(struct bfd_proto *p, ip_addr local, struct iface *ifa)
sk->priority = sk_priority_control;
sk->flags = SKF_BIND | (ifa ? SKF_TTL_RX : 0);
if (cf->zero_udp6_checksum_rx)
sk->flags |= SKF_UDP6_NO_CSUM_RX;
if (sk_open(sk, p->p.loop) < 0)
goto err;

View File

@ -729,7 +729,7 @@ static void
bgp_format_cluster_list(const eattr *a, byte *buf, uint size)
{
/* Truncates cluster lists larger than buflen, probably not a problem */
int_set_format(a->u.ptr, 0, -1, buf, size);
int_set_format(a->u.ptr, ISF_ROUTER_ID, -1, buf, size);
}

View File

@ -104,6 +104,7 @@
* RFC 9072 - Extended Optional Parameters Length for BGP OPEN Message
* RFC 9117 - Revised Validation Procedure for BGP Flow Specifications
* RFC 9234 - Route Leak Prevention and Detection Using Roles
* RFC 9687 - Send Hold Timer
* draft-uttaro-idr-bgp-persistence-04
* draft-walton-bgp-hostname-capability-02
*/

View File

@ -33,12 +33,14 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER,
RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND)
CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
%type <i> bgp_nh
%type <i32> bgp_afi
CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION,
OUT, OF, RESOURCES)
OUT, OF, RESOURCES, ASPA_CHECK_UPSTREAM, ASPA_CHECK_DOWNSTREAM)
%type<i> bgp_cease_mask bgp_cease_list bgp_cease_flag bgp_role_name
@ -365,8 +367,6 @@ custom_attr: ATTRIBUTE BGP NUM type symbol ';' {
cf_define_symbol(new_config, $5, SYM_ATTRIBUTE, attribute, ac);
};
CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
CF_CLI(RELOAD BGP, proto_patt, [<name>], [[Send and request route refresh to/from neighbor]])
{
proto_apply_cmd($3, bgp_reload_in, 1, 0);
@ -383,6 +383,29 @@ CF_CLI(RELOAD BGP OUT, proto_patt, [<name>], [[Refresh routes to neighbor]])
proto_apply_cmd($4, bgp_reload_out, 1, 0);
}
/* ASPA shortcuts */
term: ASPA_CHECK_DOWNSTREAM '(' rtable ')' { $$ =
f_new_inst(FI_ASPA_CHECK_EXPLICIT,
f_new_inst(FI_EA_GET,
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL, }),
ea_class_find_by_name("bgp_path")
),
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }),
$3
);
}
term: ASPA_CHECK_UPSTREAM '(' rtable ')' { $$ =
f_new_inst(FI_ASPA_CHECK_EXPLICIT,
f_new_inst(FI_EA_GET,
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL, }),
ea_class_find_by_name("bgp_path")
),
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }),
$3
);
}
CF_CODE
CF_END

View File

@ -3342,7 +3342,7 @@ static struct {
{ 6, 8, "Out of Resources" },
{ 7, 0, "Invalid ROUTE-REFRESH message" }, /* [RFC7313] */
{ 7, 1, "Invalid ROUTE-REFRESH message length" }, /* [RFC7313] */
{ 8, 0, "Send hold timer expired" }, /* [draft-ietf-idr-bgp-sendholdtimer] */
{ 8, 0, "Send hold timer expired" }, /* [RFC9687] */
};
/**

View File

@ -252,6 +252,7 @@ struct ospf_proto
u32 last_vlink_id; /* Interface IDs for vlinks (starts at 0x80000000) */
struct tbf log_pkt_tbf; /* TBF for packet messages */
struct tbf log_lsa_tbf; /* TBF for LSA messages */
ip_addr loopback_addr; /* IP address used as common next hop (in OSPFv3-IPv4) */
};
struct ospf_area
@ -331,6 +332,7 @@ struct ospf_iface
struct top_hash_entry **flood_queue; /* LSAs queued for LSUPD */
u8 update_link_lsa;
u8 update_net_lsa;
u8 loopback_addr_used; /* The Link-LSA depends on p->loopback_addr */
u16 flood_queue_used; /* The current number of LSAs in flood_queue */
u16 flood_queue_size; /* The maximum number of LSAs in flood_queue */
int fadj; /* Number of fully adjacent neighbors */

View File

@ -2029,6 +2029,14 @@ again1:
{
neighbor *nbr = neigh_find(&p->p, nh->gw, nh->iface,
(nh->flags & RNF_ONLINK) ? NEF_ONLINK : 0);
/* According to RFC 5838 2.5 Direct Interface Address */
if (ospf_is_v3(p) && !nbr && ipa_is_ip4(nh->gw))
{
nh->flags |= RNF_ONLINK;
nbr = neigh_find(&p->p, nh->gw, nh->iface, NEF_ONLINK);
}
if (!nbr || (nbr->scope == SCOPE_HOST))
{ reset_ri(nf); break; }
}

View File

@ -1381,6 +1381,46 @@ lsab_put_prefix(struct ospf_proto *p, net_addr *n, u32 cost)
ospf3_put_prefix(buf, n, flags, cost);
}
static inline void
update_loopback_addr(struct ospf_proto *p)
{
ip_addr old_addr = p->loopback_addr;
ip_addr best_addr = IPA_NONE;
int best_pref = 0;
struct ospf_iface *ifa;
WALK_LIST(ifa, p->iface_list)
{
if (ifa->type == OSPF_IT_VLINK)
continue;
struct ifa *a;
WALK_LIST(a, ifa->iface->addrs)
{
if ((a->prefix.type != ospf_get_af(p)) ||
(a->flags & IA_SECONDARY) ||
(a->scope <= SCOPE_LINK))
continue;
int pref = (a->flags & IA_HOST) ? 3 : (ifa->stub ? 2 : 1);
if ((pref > best_pref) || ((pref == best_pref) && ipa_equal(a->ip, old_addr)))
{
best_addr = a->ip;
best_pref = pref;
}
}
}
if (ipa_equal(best_addr, old_addr))
return;
p->loopback_addr = best_addr;
WALK_LIST(ifa, p->iface_list)
if (ifa->loopback_addr_used)
ospf_notify_link_lsa(ifa);
}
static void
prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
{
@ -1406,6 +1446,12 @@ prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
i++;
}
if (ospf_is_ip4(p) && ipa_zero(nh))
{
nh = p->loopback_addr;
ifa->loopback_addr_used = 1;
}
/* Filling the preallocated header */
struct ospf_lsa_link *ll = p->lsab;
ll->options = ifa->oa->options | (ifa->priority << 24);
@ -1832,6 +1878,9 @@ ospf_update_topology(struct ospf_proto *p)
}
}
if (ospf_is_v3(p) && ospf_is_ip4(p))
update_loopback_addr(p);
WALK_LIST(ifa, p->iface_list)
{
if (ifa->type == OSPF_IT_VLINK)
@ -1839,6 +1888,8 @@ ospf_update_topology(struct ospf_proto *p)
if (ifa->update_link_lsa)
{
ifa->loopback_addr_used = 0;
if ((ifa->state > OSPF_IS_LOOP) && !ifa->link_lsa_suppression)
ospf_originate_link_lsa(p, ifa);
else

View File

@ -13,6 +13,7 @@ CF_HDR
CF_DEFINES
#define RPKI_CFG ((struct rpki_config *) this_proto)
#define RPKI_TR_TCP_CFG ((struct rpki_tr_tcp_config *) RPKI_CFG->tr_config.spec)
#define RPKI_TR_SSH_CFG ((struct rpki_tr_ssh_config *) RPKI_CFG->tr_config.spec)
static void
@ -32,7 +33,8 @@ rpki_check_unused_transport(void)
CF_DECLS
CF_KEYWORDS(RPKI, REMOTE, BIRD, PRIVATE, PUBLIC, KEY, TCP, SSH, TRANSPORT, USER,
RETRY, REFRESH, EXPIRE, KEEP, IGNORE, MAX, LENGTH, LOCAL, ADDRESS)
RETRY, REFRESH, EXPIRE, KEEP, IGNORE, MAX, MIN, LENGTH, LOCAL, ADDRESS,
AUTHENTICATION, NONE, MD5, PASSWORD, VERSION)
%type <i> rpki_keep_interval
@ -46,6 +48,8 @@ rpki_proto_start: proto_start RPKI {
RPKI_CFG->retry_interval = RPKI_RETRY_INTERVAL;
RPKI_CFG->refresh_interval = RPKI_REFRESH_INTERVAL;
RPKI_CFG->expire_interval = RPKI_EXPIRE_INTERVAL;
RPKI_CFG->min_version = 0;
RPKI_CFG->max_version = RPKI_MAX_VERSION;
};
rpki_proto: rpki_proto_start proto_name '{' rpki_proto_opts '}' { rpki_check_config(RPKI_CFG); };
@ -82,6 +86,14 @@ rpki_proto_item:
RPKI_CFG->keep_expire_interval = $2;
}
| IGNORE MAX LENGTH bool { RPKI_CFG->ignore_max_length = $4; }
| MIN VERSION expr {
if ($3 > RPKI_MAX_VERSION) cf_error("RPKI version %u unsupported, min version must be in range 0-%u", $3, RPKI_MAX_VERSION);
RPKI_CFG->min_version = $3;
}
| MAX VERSION expr {
if ($3 > RPKI_MAX_VERSION) cf_error("RPKI version %u unsupported, max version must be in range 0-%u", $3, RPKI_MAX_VERSION);
RPKI_CFG->max_version = $3;
}
;
rpki_keep_interval:
@ -109,7 +121,7 @@ rpki_cache_addr: text_or_ipa
};
rpki_transport:
TCP rpki_transport_tcp_init
TCP rpki_transport_tcp_init rpki_transport_tcp_opts_list rpki_transport_tcp_check
| SSH rpki_transport_ssh_init '{' rpki_transport_ssh_opts '}' rpki_transport_ssh_check
;
@ -120,6 +132,28 @@ rpki_transport_tcp_init:
RPKI_CFG->tr_config.type = RPKI_TR_TCP;
};
rpki_transport_tcp_opts_list:
/* empty */
| '{' rpki_transport_tcp_opts '}'
;
rpki_transport_tcp_opts:
/* empty */
| rpki_transport_tcp_opts rpki_transport_tcp_item ';'
;
rpki_transport_tcp_item:
AUTHENTICATION NONE { RPKI_TR_TCP_CFG->auth_type = RPKI_TCP_AUTH_NONE; }
| AUTHENTICATION MD5 { RPKI_TR_TCP_CFG->auth_type = RPKI_TCP_AUTH_MD5; }
| PASSWORD text { RPKI_TR_TCP_CFG->password = $2; }
;
rpki_transport_tcp_check:
{
if (!RPKI_TR_TCP_CFG->auth_type != !RPKI_TR_TCP_CFG->password)
cf_error("Authentication and password options should be used together");
};
rpki_transport_ssh_init:
{
#if HAVE_LIBSSH

View File

@ -62,6 +62,7 @@ enum pdu_type {
CACHE_RESET = 8,
ROUTER_KEY = 9,
ERROR = 10,
ASPA = 11,
PDU_TYPE_MAX
};
@ -76,7 +77,8 @@ static const char *str_pdu_type_[] = {
[END_OF_DATA] = "End of Data",
[CACHE_RESET] = "Cache Reset",
[ROUTER_KEY] = "Router Key",
[ERROR] = "Error"
[ERROR] = "Error",
[ASPA] = "ASPA",
};
static const char *str_pdu_type(uint type) {
@ -193,6 +195,35 @@ struct pdu_error {
* Error Diagnostic Message */
} PACKED;
/*
*0 8 16 24 31
* .-------------------------------------------.
* | Protocol | PDU | | |
* | Version | Type | Flags | zero |
* | 2 | 11 | | |
* +-------------------------------------------+
* | |
* | Length |
* | |
* +-------------------------------------------+
* | |
* | Customer Autonomous System Number |
* | |
* +-------------------------------------------+
* | |
* ~ Provider Autonomous System Numbers ~
* | |
* ~-------------------------------------------~ */
struct pdu_aspa {
u8 ver;
u8 type;
u8 flags;
u8 zero;
u32 len;
u32 customer_as_num;
u32 provider_as_nums[0];
} PACKED;
struct pdu_reset_query {
u8 ver;
u8 type;
@ -230,9 +261,13 @@ static const size_t min_pdu_size[] = {
[END_OF_DATA] = sizeof(struct pdu_end_of_data_v0),
[CACHE_RESET] = sizeof(struct pdu_cache_response),
[ROUTER_KEY] = sizeof(struct pdu_header), /* FIXME */
[ASPA] = sizeof(struct pdu_aspa),
[ERROR] = 16,
};
static inline int rpki_pdu_aspa_provider_asn_count(const struct pdu_aspa *pdu)
{ return (pdu->len - sizeof(struct pdu_aspa)) / (sizeof(u32)); }
static int rpki_send_error_pdu_(struct rpki_cache *cache, const enum pdu_error_type error_code, const u32 err_pdu_len, const struct pdu_header *erroneous_pdu, const char *fmt, ...);
#define rpki_send_error_pdu(cache, error_code, err_pdu_len, erroneous_pdu, fmt...) ({ \
@ -276,29 +311,25 @@ rpki_pdu_to_network_byte_order(struct pdu_header *pdu)
static void
rpki_pdu_to_host_byte_order(struct pdu_header *pdu)
{
/* The Router Key PDU has two one-byte fields instead of one two-bytes field. */
if (pdu->type != ROUTER_KEY)
pdu->reserved = ntohs(pdu->reserved);
pdu->len = ntohl(pdu->len);
switch (pdu->type)
{
case SERIAL_NOTIFY:
{
/* Note that a session_id is converted using converting header->reserved */
struct pdu_serial_notify *sn_pdu = (void *) pdu;
sn_pdu->session_id = ntohs(sn_pdu->session_id);
sn_pdu->serial_num = ntohl(sn_pdu->serial_num);
break;
}
case END_OF_DATA:
{
/* Note that a session_id is converted using converting header->reserved */
struct pdu_end_of_data_v0 *eod0 = (void *) pdu;
eod0->session_id = ntohs(eod0->session_id);
eod0->serial_num = ntohl(eod0->serial_num); /* Same either for version 1 */
if (pdu->ver == RPKI_VERSION_1)
if (pdu->ver > RPKI_VERSION_0)
{
struct pdu_end_of_data_v1 *eod1 = (void *) pdu;
eod1->expire_interval = ntohl(eod1->expire_interval);
@ -326,14 +357,29 @@ rpki_pdu_to_host_byte_order(struct pdu_header *pdu)
case ERROR:
{
/* Note that a error_code is converted using converting header->reserved */
struct pdu_error *err = (void *) pdu;
err->error_code = ntohs(err->error_code);
err->len_enc_pdu = ntohl(err->len_enc_pdu);
u32 *err_text_len = (u32 *)(err->rest + err->len_enc_pdu);
*err_text_len = htonl(*err_text_len);
break;
}
case ASPA:
{
struct pdu_aspa *aspa = (void *) pdu;
int provider_asn_count = rpki_pdu_aspa_provider_asn_count(aspa);
/* Convert customer ASN */
aspa->customer_as_num = ntohl(aspa->customer_as_num);
/* Convert provider ASNs */
for (int i = 0; i < provider_asn_count ; i++)
aspa->provider_as_nums[i] = ntohl(aspa->provider_as_nums[i]);
break;
}
case ROUTER_KEY:
/* Router Key PDU is not supported yet */
@ -343,8 +389,14 @@ rpki_pdu_to_host_byte_order(struct pdu_header *pdu)
* We don't care here. */
case CACHE_RESPONSE:
{
struct pdu_cache_response *cr = (void *) pdu;
cr->session_id = ntohs(cr->session_id);
break;
}
case CACHE_RESET:
/* Converted with pdu->reserved */
/* Nothing to convert */
break;
}
}
@ -359,6 +411,9 @@ rpki_pdu_to_host_byte_order(struct pdu_header *pdu)
static struct pdu_header *
rpki_pdu_back_to_network_byte_order(struct pdu_header *out, const struct pdu_header *in)
{
/* Only valid for fixed-length PDUs */
ASSERT_DIE(in->type != ERROR && in->type != ASPA);
memcpy(out, in, in->len);
rpki_pdu_to_host_byte_order(out);
return out;
@ -392,7 +447,7 @@ rpki_log_packet(struct rpki_cache *cache, const struct pdu_header *pdu, const en
case END_OF_DATA:
{
const struct pdu_end_of_data_v1 *eod = (void *) pdu;
if (eod->ver == RPKI_VERSION_1)
if (eod->ver > RPKI_VERSION_0)
SAVE(bsnprintf(detail, sizeof(detail), "(session id: %u, serial number: %u, refresh: %us, retry: %us, expire: %us)", eod->session_id, eod->serial_num, eod->refresh_interval, eod->retry_interval, eod->expire_interval));
else
SAVE(bsnprintf(detail, sizeof(detail), "(session id: %u, serial number: %u)", eod->session_id, eod->serial_num));
@ -460,6 +515,25 @@ rpki_log_packet(struct rpki_cache *cache, const struct pdu_header *pdu, const en
break;
}
case ASPA:
{
const struct pdu_aspa *aspa = (void *) pdu;
int provider_asn_count = rpki_pdu_aspa_provider_asn_count(aspa);
if (provider_asn_count <= 0)
SAVE(bsnprintf(detail + strlen(detail), sizeof(detail) - strlen(detail),
"%u transit", aspa->customer_as_num));
else
{
SAVE(bsnprintf(detail + strlen(detail), sizeof(detail) - strlen(detail),
"%u (providers", aspa->customer_as_num));
for (int i = 0; i < provider_asn_count; i++)
SAVE(bsnprintf(detail + strlen(detail), sizeof(detail) - strlen(detail),
" %u%c", aspa->provider_as_nums[i], (i == provider_asn_count-1) ? ')' : ','));
}
break;
}
default:
*detail = '\0';
}
@ -561,7 +635,9 @@ rpki_check_receive_packet(struct rpki_cache *cache, const struct pdu_header *pdu
}
else if (!cache->last_update &&
(pdu->ver <= RPKI_MAX_VERSION) &&
(pdu->ver < cache->version))
(pdu->ver < cache->version) &&
(pdu->ver >= cache->min_version)
)
{
CACHE_TRACE(D_EVENTS, cache, "Downgrade session to %s from %u to %u version", rpki_get_cache_ident(cache), cache->version, pdu->ver);
cache->version = pdu->ver;
@ -576,15 +652,21 @@ rpki_check_receive_packet(struct rpki_cache *cache, const struct pdu_header *pdu
}
}
if ((pdu->type >= PDU_TYPE_MAX) || (pdu->ver == RPKI_VERSION_0 && pdu->type == ROUTER_KEY))
if ((pdu->type >= PDU_TYPE_MAX) ||
(pdu->ver < RPKI_VERSION_1 && pdu->type == ROUTER_KEY) ||
(pdu->ver < RPKI_VERSION_2 && pdu->type == ASPA))
{
rpki_send_error_pdu(cache, UNSUPPORTED_PDU_TYPE, pdu_len, pdu, "Unsupported PDU type %u received", pdu->type);
return RPKI_ERROR;
}
if (pdu_len < min_pdu_size[pdu->type])
uint min_pdu_length = min_pdu_size[pdu->type];
if (pdu->type == END_OF_DATA && pdu->ver >= RPKI_VERSION_1)
min_pdu_length = sizeof(struct pdu_end_of_data_v1);
if (pdu_len < min_pdu_length)
{
rpki_send_error_pdu(cache, CORRUPT_DATA, pdu_len, pdu, "Received %s packet with %d bytes, but expected at least %d bytes", str_pdu_type(pdu->type), pdu_len, min_pdu_size[pdu->type]);
rpki_send_error_pdu(cache, CORRUPT_DATA, pdu_len, pdu, "Received %s packet with %u bytes, but expected at least %u bytes", str_pdu_type(pdu->type), pdu_len, min_pdu_length);
return RPKI_ERROR;
}
@ -611,7 +693,8 @@ rpki_handle_error_pdu(struct rpki_cache *cache, const struct pdu_error *pdu)
case UNSUPPORTED_PROTOCOL_VER:
CACHE_TRACE(D_PACKETS, cache, "Client uses unsupported protocol version");
if (pdu->ver <= RPKI_MAX_VERSION &&
pdu->ver < cache->version)
pdu->ver < cache->version &&
pdu->ver >= cache->min_version)
{
CACHE_TRACE(D_EVENTS, cache, "Downgrading from protocol version %d to version %d", cache->version, pdu->ver);
cache->version = pdu->ver;
@ -789,6 +872,29 @@ rpki_handle_prefix_pdu(struct rpki_cache *cache, const struct pdu_header *pdu)
return RPKI_SUCCESS;
}
static int
rpki_handle_aspa_pdu(struct rpki_cache *cache, const struct pdu_header *pdu)
{
struct pdu_aspa *aspa = (void *) pdu;
struct channel *channel = cache->p->aspa_channel;
uint providers_length = aspa->len - sizeof(struct pdu_aspa);
if (!channel)
{
CACHE_TRACE(D_ROUTES, cache, "Skip AS%u, missing aspa channel", aspa->customer_as_num);
return RPKI_ERROR;
}
cache->last_rx_prefix = current_time();
if (aspa->flags & RPKI_ADD_FLAG)
rpki_table_add_aspa(cache, channel, aspa->customer_as_num, aspa->provider_as_nums, providers_length);
else
rpki_table_remove_aspa(cache, channel, aspa->customer_as_num);
return RPKI_SUCCESS;
}
static uint
rpki_check_interval(struct rpki_cache *cache, const char *(check_fn)(uint), uint interval)
{
@ -814,7 +920,7 @@ rpki_handle_end_of_data_pdu(struct rpki_cache *cache, const struct pdu_end_of_da
return;
}
if (pdu->ver == RPKI_VERSION_1)
if (pdu->ver > RPKI_VERSION_0)
{
if (!cf->keep_refresh_interval && rpki_check_interval(cache, rpki_check_refresh_interval, pdu->refresh_interval))
cache->refresh_interval = pdu->refresh_interval;
@ -898,6 +1004,10 @@ rpki_rx_packet(struct rpki_cache *cache, struct pdu_header *pdu)
/* TODO: Implement Router Key PDU handling */
break;
case ASPA:
rpki_handle_aspa_pdu(cache, (void *) pdu);
break;
default:
CACHE_TRACE(D_PACKETS, cache, "Received unsupported type (%u)", pdu->type);
};

View File

@ -23,8 +23,10 @@
* +4 bytes (Length of inserted text)
* +800 bytes (UTF-8 text 400*2 bytes)
* ------------
* = 848 bytes (Maximal expected PDU size) */
#define RPKI_PDU_MAX_LEN 848
* = 848 bytes (Maximal expected PDU size)
*
* Received ASPA PDU can have any size, so let's start with 4k */
#define RPKI_PDU_MAX_LEN 4096
/* RX buffer size has a great impact to scheduler granularity */
#define RPKI_RX_BUFFER_SIZE 4096

View File

@ -137,6 +137,33 @@ rpki_table_remove_roa(struct rpki_cache *cache, struct channel *channel, const n
rte_update(channel, &pfxr->n, NULL, p->p.main_source);
}
void
rpki_table_add_aspa(struct rpki_cache *cache, struct channel *channel,
u32 customer, void *providers, uint providers_length)
{
struct rpki_proto *p = cache->p;
net_addr_union n = { .aspa = NET_ADDR_ASPA(customer) };
ea_list *ea = NULL;
ea_set_attr_u32(&ea, &ea_gen_preference, 0, channel->preference);
ea_set_attr_u32(&ea, &ea_gen_source, 0, RTS_RPKI);
ea_set_attr_data(&ea, &ea_gen_aspa_providers, 0, providers, providers_length);
rte e0 = { .attrs = ea, .src = p->p.main_source, };
rte_update(channel, &n.n, &e0, p->p.main_source);
}
void
rpki_table_remove_aspa(struct rpki_cache *cache, struct channel *channel, u32 customer)
{
struct rpki_proto *p = cache->p;
net_addr_union n = { .aspa = NET_ADDR_ASPA(customer) };
rte_update(channel, &n.n, NULL, p->p.main_source);
}
void
rpki_start_refresh(struct rpki_proto *p)
{
@ -144,6 +171,8 @@ rpki_start_refresh(struct rpki_proto *p)
rt_refresh_begin(&p->roa4_channel->in_req);
if (p->roa6_channel)
rt_refresh_begin(&p->roa6_channel->in_req);
if (p->aspa_channel)
rt_refresh_begin(&p->aspa_channel->in_req);
p->refresh_channels = 1;
}
@ -160,6 +189,8 @@ rpki_stop_refresh(struct rpki_proto *p)
rt_refresh_end(&p->roa4_channel->in_req);
if (p->roa6_channel)
rt_refresh_end(&p->roa6_channel->in_req);
if (p->aspa_channel)
rt_refresh_end(&p->aspa_channel->in_req);
}
/*
@ -608,7 +639,8 @@ rpki_init_cache(struct rpki_proto *p, struct rpki_config *cf)
cache->state = RPKI_CS_SHUTDOWN;
cache->request_session_id = 1;
cache->version = RPKI_MAX_VERSION;
cache->version = cf->max_version;
cache->min_version = cf->min_version;
cache->refresh_interval = cf->refresh_interval;
cache->retry_interval = cf->retry_interval;
@ -714,6 +746,23 @@ rpki_reconfigure_cache(struct rpki_proto *p UNUSED, struct rpki_cache *cache, st
return NEED_RESTART;
}
if (new->min_version > cache->version)
{
CACHE_TRACE(D_EVENTS, cache, "Protocol min version %u higher than current version %u",
new->min_version, cache->version);
return NEED_RESTART;
}
else
cache->min_version = new->min_version;
if (new->max_version < cache->version)
{
CACHE_TRACE(D_EVENTS, cache, "Protocol max version %u lower than current version %u",
new->max_version, cache->version);
cache->version = new->max_version;
try_reset = 1;
}
if (old->tr_config.type != new->tr_config.type)
{
CACHE_TRACE(D_EVENTS, cache, "Transport type changed");
@ -726,6 +775,24 @@ rpki_reconfigure_cache(struct rpki_proto *p UNUSED, struct rpki_cache *cache, st
try_reset = 1;
}
if (new->tr_config.type == RPKI_TR_TCP)
{
struct rpki_tr_tcp_config *tcp_old = (void *) old->tr_config.spec;
struct rpki_tr_tcp_config *tcp_new = (void *) new->tr_config.spec;
if (tcp_old->auth_type != tcp_new->auth_type)
{
CACHE_TRACE(D_EVENTS, cache, "Authentication type changed");
return NEED_RESTART;
}
if (bstrcmp(tcp_old->password, tcp_new->password))
{
CACHE_TRACE(D_EVENTS, cache, "MD5 password changed");
return NEED_RESTART;
}
}
#if HAVE_LIBSSH
else if (new->tr_config.type == RPKI_TR_SSH)
{
@ -796,7 +863,8 @@ rpki_reconfigure(struct proto *P, struct proto_config *CF)
struct rpki_cache *cache = p->cache;
if (!proto_configure_channel(&p->p, &p->roa4_channel, proto_cf_find_channel(CF, NET_ROA4)) ||
!proto_configure_channel(&p->p, &p->roa6_channel, proto_cf_find_channel(CF, NET_ROA6)))
!proto_configure_channel(&p->p, &p->roa6_channel, proto_cf_find_channel(CF, NET_ROA6)) ||
!proto_configure_channel(&p->p, &p->aspa_channel, proto_cf_find_channel(CF, NET_ASPA)))
return NEED_RESTART;
if (rpki_reconfigure_cache(p, cache, new, old) != SUCCESSFUL_RECONF)
@ -818,6 +886,7 @@ rpki_init(struct proto_config *CF)
proto_configure_channel(&p->p, &p->roa4_channel, proto_cf_find_channel(CF, NET_ROA4));
proto_configure_channel(&p->p, &p->roa6_channel, proto_cf_find_channel(CF, NET_ROA6));
proto_configure_channel(&p->p, &p->aspa_channel, proto_cf_find_channel(CF, NET_ASPA));
return P;
}
@ -883,8 +952,12 @@ rpki_show_proto_info(struct proto *P)
default_port = RPKI_SSH_PORT;
break;
#endif
case RPKI_TR_TCP:
transport_name = "Unprotected over TCP";
case RPKI_TR_TCP:;
struct rpki_tr_tcp_config *tcp_cf = (void *) cf->tr_config.spec;
if (tcp_cf->auth_type == RPKI_TCP_AUTH_MD5)
transport_name = "TCP-MD5";
else
transport_name = "Unprotected over TCP";
default_port = RPKI_TCP_PORT;
break;
};
@ -927,6 +1000,11 @@ rpki_show_proto_info(struct proto *P)
channel_show_info(p->roa6_channel);
else
cli_msg(-1006, " No roa6 channel");
if (p->aspa_channel)
channel_show_info(p->aspa_channel);
else
cli_msg(-1006, " No aspa channel");
}
}
@ -944,6 +1022,9 @@ rpki_show_proto_info(struct proto *P)
void
rpki_check_config(struct rpki_config *cf)
{
if (cf->min_version > cf->max_version)
cf_error("Impossible min/max version for RPKI: %u/%u", cf->min_version, cf->max_version);
/* Do not check templates at all */
if (cf->c.class == SYM_TEMPLATE)
return;
@ -998,7 +1079,7 @@ struct protocol proto_rpki = {
.init = rpki_init,
.start = rpki_start,
.postconfig = rpki_postconfig,
.channel_mask = (NB_ROA4 | NB_ROA6),
.channel_mask = (NB_ROA4 | NB_ROA6 | NB_ASPA),
.show_proto_info = rpki_show_proto_info,
.shutdown = rpki_shutdown,
.copy_config = rpki_copy_config,

View File

@ -29,7 +29,8 @@
#define RPKI_VERSION_0 0
#define RPKI_VERSION_1 1
#define RPKI_MAX_VERSION RPKI_VERSION_1
#define RPKI_VERSION_2 2
#define RPKI_MAX_VERSION RPKI_VERSION_2
/*
@ -60,6 +61,7 @@ struct rpki_cache {
u8 request_session_id; /* 1: have to request new session id; 0: we have already received session id */
u32 serial_num; /* Serial number denotes the logical version of data from cache server */
u8 version; /* Protocol version */
u8 min_version; /* Minimum allowed protocol version */
btime last_update; /* Last successful synchronization with cache server */
btime last_rx_prefix; /* Last received prefix PDU */
@ -83,6 +85,9 @@ const char *rpki_cache_state_to_str(enum rpki_cache_state state);
void rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr);
void rpki_table_remove_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr);
void rpki_table_add_aspa(struct rpki_cache *cache, struct channel *channel, u32 customer, void *providers, uint providers_length);
void rpki_table_remove_aspa(struct rpki_cache *cache, struct channel *channel, u32 customer);
void rpki_start_refresh(struct rpki_proto *p);
void rpki_stop_refresh(struct rpki_proto *p);
@ -112,6 +117,7 @@ struct rpki_proto {
struct channel *roa4_channel;
struct channel *roa6_channel;
struct channel *aspa_channel;
u8 refresh_channels; /* For non-incremental updates using rt_refresh_begin(), rt_refresh_end() */
};
@ -129,6 +135,8 @@ struct rpki_config {
u8 keep_retry_interval:1; /* Do not overwrite retry interval by cache server update */
u8 keep_expire_interval:1; /* Do not overwrite expire interval by cache server update */
u8 ignore_max_length:1; /* Ignore received max length and use MAX_PREFIX_LENGTH instead */
u8 min_version; /* Minimum version allowed */
u8 max_version; /* Maximum version allowed (to start with) */
};
void rpki_check_config(struct rpki_config *cf);

View File

@ -24,10 +24,16 @@
static int
rpki_tr_tcp_open(struct rpki_tr_sock *tr)
{
struct rpki_cache *cache = tr->cache;
struct rpki_config *cf = (void *) cache->p->p.cf;
struct rpki_tr_tcp_config *tcp_cf = (void *) cf->tr_config.spec;
sock *sk = tr->sk;
sk->type = SK_TCP_ACTIVE;
if (tcp_cf->auth_type == RPKI_TCP_AUTH_MD5)
sk->password = tcp_cf->password;
if (sk_open(sk, tr->cache->p->p.loop) != 0)
return RPKI_TR_ERROR;

View File

@ -56,6 +56,12 @@ enum rpki_tr_type {
#endif
};
/* TCP authentication types */
enum rpki_tcp_auth {
RPKI_TCP_AUTH_NONE,
RPKI_TCP_AUTH_MD5
};
/* Common configure structure for transports */
struct rpki_tr_config {
enum rpki_tr_type type; /* RPKI_TR_TCP or RPKI_TR_SSH */
@ -63,7 +69,8 @@ struct rpki_tr_config {
};
struct rpki_tr_tcp_config {
/* No internal configuration data */
enum rpki_tcp_auth auth_type; /* Authentication type */
const char *password; /* Password used for authentication */
};
struct rpki_tr_ssh_config {

View File

@ -15,6 +15,7 @@ CF_DEFINES
#define STATIC_CFG ((struct static_config *) this_proto)
static struct static_route *this_srt, *this_snh;
static struct f_inst *this_srt_cmds, *this_srt_last_cmd;
static uint this_srt_aspa_max;
static struct static_route *
static_nexthop_new(void)
@ -47,6 +48,7 @@ CF_DECLS
CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK, DEV)
CF_KEYWORDS(ONLINK, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD, MPLS)
CF_KEYWORDS(TRANSIT, PROVIDERS)
CF_GRAMMAR
@ -148,8 +150,35 @@ stat_route:
| stat_route0 BLACKHOLE { this_srt->dest = RTD_BLACKHOLE; }
| stat_route0 UNREACHABLE { this_srt->dest = RTD_UNREACHABLE; }
| stat_route0 PROHIBIT { this_srt->dest = RTD_PROHIBIT; }
| stat_route0 PROVIDER {
if (this_srt->net->type != NET_ASPA) cf_error("Provider settings available only for ASPA");
this_srt->aspa = cfg_alloc(sizeof (adata) + (this_srt_aspa_max = 8) * sizeof (u32));
this_srt->aspa->length = 0;
} stat_aspa_providers
| stat_route0 TRANSIT {
if (this_srt->net->type != NET_ASPA) cf_error("Transit settings available only for ASPA");
/* Allocate an explicit zero */
this_srt->aspa = cfg_alloc(sizeof (adata) + sizeof (u32));
this_srt->aspa->length = sizeof(u32);
((u32 *) this_srt->aspa->data)[0] = 0;
}
;
stat_aspa_provider: NUM {
if (this_srt->aspa->length == this_srt_aspa_max * sizeof(u32))
{
adata *new = cfg_alloc(sizeof (adata) + (this_srt_aspa_max * 2) * sizeof (u32));
memcpy(new, this_srt->aspa, this_srt->aspa->length + sizeof(adata));
this_srt->aspa = new;
this_srt_aspa_max *= 2;
}
((u32 *) this_srt->aspa->data)[this_srt->aspa->length / sizeof(u32)] = $1;
this_srt->aspa->length += sizeof(u32);
}
stat_aspa_providers: stat_aspa_provider | stat_aspa_providers ',' stat_aspa_provider ;
stat_route_item:
cmd {
if (this_srt_last_cmd)

View File

@ -112,6 +112,26 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
else if (r->dest)
ea_set_dest(&ea, 0, r->dest);
if (r->net->type == NET_ASPA)
{
if (r->dest != RTD_NONE)
{
log(L_WARN "%s: ASPA %u configured with nexthop, ignoring the nexthop",
p->p.name, ((struct net_addr_aspa *) r->net)->asn);
r->dest = RTD_NONE;
}
if (!r->aspa)
{
log(L_WARN "%s: ASPA %u configured with no provider list, ignoring the whole rule",
p->p.name, ((struct net_addr_aspa *) r->net)->asn);
goto withdraw;
}
ea_set_attr(&ea, EA_LITERAL_DIRECT_ADATA(
&ea_gen_aspa_providers, 0, r->aspa));
}
if (p->p.mpls_channel)
{
struct mpls_channel *mc = (void *) p->p.mpls_channel;

View File

@ -50,7 +50,10 @@ struct static_route {
byte use_bfd; /* Configured to use BFD */
uint mpls_label; /* Local MPLS label, -1 if unused */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
struct adata *mls; /* MPLS label stack; may be NULL */
union {
adata *mls; /* MPLS label stack; may be NULL */
adata *aspa; /* ASPA provider list; may be NULL */
};
};
/*

View File

@ -309,3 +309,9 @@ sk_set_freebind(sock *s)
{
ERR_MSG("Freebind is not supported");
}
static inline int
sk_set_udp6_no_csum_rx(sock *s)
{
ERR_MSG("UDPv6 zero checksum is not supported");
}

View File

@ -51,6 +51,8 @@ struct nl_parse_state
#define KRT_FEATURES_MAX 4
static void krt_bitfield_format(const eattr *e, byte *buf, uint buflen);
static struct f_val krt_bitfield_empty(const struct ea_class *cls UNUSED)
{ return (struct f_val) { .type = T_INT }; }
static struct ea_class
ea_krt_prefsrc = {
@ -71,11 +73,13 @@ static struct ea_class ea_krt_metrics[] = {
.name = "krt_lock",
.type = T_INT,
.format = krt_bitfield_format,
.empty = krt_bitfield_empty,
},
[RTAX_FEATURES] = {
.name = "krt_features",
.type = T_INT,
.format = krt_bitfield_format,
.empty = krt_bitfield_empty,
},
[RTAX_CC_ALGO] = {
.name = "krt_congctl",
@ -1589,8 +1593,24 @@ done:
}
static inline int
nl_allow_replace(struct krt_proto *p, rte *new)
nl_allow_replace(struct krt_proto *p, const rte *new, const rte *old)
{
/*
* In kernel routing tables, (net, metric) is the primary key. Therefore, we
* can use NL_OP_REPLACE only if the new and and the old route have the same
* kernel metric, otherwise the replace would just add the new route while
* keep the old one.
*/
if ((p->af != AF_MPLS) && (KRT_CF->sys.metric == 0))
{
uint new_metric = ea_get_int(new->attrs, &ea_krt_metric, 0);
uint old_metric = ea_get_int(old->attrs, &ea_krt_metric, 0);
if (new_metric != old_metric)
return 0;
}
/*
* We use NL_OP_REPLACE for IPv4, it has an issue with not checking for
* matching rtm_protocol, but that is OK when dedicated priority is used.
@ -1622,7 +1642,7 @@ krt_replace_rte(struct krt_proto *p, const net_addr *n UNUSED, rte *new, const r
{
int err = 0;
if (old && new && nl_allow_replace(p, new))
if (old && new && nl_allow_replace(p, new, old))
{
err = nl_send_route(p, new, NL_OP_REPLACE);
}

View File

@ -285,3 +285,14 @@ sk_set_freebind(sock *s)
return 0;
}
static inline int
sk_set_udp6_no_csum_rx(sock *s)
{
int y = 1;
if (setsockopt(s->fd, SOL_UDP, UDP_NO_CHECK6_RX, &y, sizeof(y)) < 0)
ERR("UDP_NO_CHECK6_RX");
return 0;
}

View File

@ -14,17 +14,17 @@ CF_HDR
CF_DEFINES
static struct log_config *this_log;
static struct cli_config *this_cli_config;
CF_DECLS
CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT, UDP, PORT)
CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT, UDP, PORT, CLI)
CF_KEYWORDS(NAME, CONFIRM, UNDO, CHECK, TIMEOUT, DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING, STATUS)
CF_KEYWORDS(PING, WAKEUP, SOCKETS, SCHEDULING, EVENTS, TIMERS, ALLOCATOR)
CF_KEYWORDS(GRACEFUL, RESTART, FIXED)
%type <i> log_mask log_mask_list log_cat cfg_timeout debug_unix latency_debug_mask latency_debug_flag latency_debug_list
%type <t> cfg_name
%type <tf> timeformat_which
%type <t> syslog_name
CF_GRAMMAR
@ -122,6 +122,28 @@ mrtdump_base:
}
;
conf: cli ;
cli: CLI text cli_opts {
this_cli_config->name = $2;
cli_config_add_tail(&new_config->cli, this_cli_config);
this_cli_config = NULL;
} ;
cli_opts: cli_opts_begin '{' cli_opts_block '}' ';' | cli_opts_begin ';' ;
cli_opts_begin: {
this_cli_config = cfg_alloc(sizeof *this_cli_config);
*this_cli_config = (typeof (*this_cli_config)) {
.config = new_config,
.mode = 0660,
};
};
cli_opts_block:
/* EMPTY */ |
cli_opts_block RESTRICT { this_cli_config->restricted = 1; }
;
conf: THREADS expr {
if ($2 < 1) cf_error("Number of threads must be at least one.");

View File

@ -668,6 +668,40 @@ sk_set_high_port(sock *s UNUSED)
return 0;
}
static inline int
sk_set_min_rcvbuf_(sock *s, int bufsize)
{
int oldsize = 0, oldsize_s = sizeof(oldsize);
if (getsockopt(s->fd, SOL_SOCKET, SO_RCVBUF, &oldsize, &oldsize_s) < 0)
ERR("SO_RCVBUF");
if (oldsize >= bufsize)
return 0;
bufsize = BIRD_ALIGN(bufsize, 64);
if (setsockopt(s->fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) < 0)
ERR("SO_RCVBUF");
/*
int newsize = 0, newsize_s = sizeof(newsize);
if (getsockopt(s->fd, SOL_SOCKET, SO_RCVBUF, &newsize, &newsize_s) < 0)
ERR("SO_RCVBUF");
log(L_INFO "Setting rcvbuf on %s from %d to %d",
s->iface ? s->iface->name : "*", oldsize, newsize);
*/
return 0;
}
static void
sk_set_min_rcvbuf(sock *s, int bufsize)
{
if (sk_set_min_rcvbuf_(s, bufsize) < 0)
log(L_WARN "Socket error: %s%#m", s->err);
}
static inline byte *
sk_skip_ip_header(byte *pkt, int *len)
{
@ -999,6 +1033,9 @@ sk_set_rbsize(sock *s, uint val)
xfree(s->rbuf_alloc);
s->rbuf_alloc = xmalloc(val);
s->rpos = s->rbuf = s->rbuf_alloc;
if ((s->type == SK_UDP) || (s->type == SK_IP))
sk_set_min_rcvbuf(s, s->rbsize);
}
void
@ -1108,10 +1145,11 @@ sk_setup(sock *s)
}
#endif
if (s->vrf && (s->vrf != &default_vrf) && !s->iface)
if (s->vrf && (s->vrf != &default_vrf) && !s->iface && (s->type != SK_TCP))
{
/* Bind socket to associated VRF interface.
This is Linux-specific, but so is SO_BINDTODEVICE. */
This is Linux-specific, but so is SO_BINDTODEVICE.
For accepted TCP sockets it is inherited from the listening one. */
#ifdef SO_BINDTODEVICE
struct ifreq ifr = {};
strcpy(ifr.ifr_name, s->vrf->name);
@ -1183,6 +1221,10 @@ sk_setup(sock *s)
if (s->tos >= 0)
if (sk_set_tos6(s, s->tos) < 0)
return -1;
if ((s->flags & SKF_UDP6_NO_CSUM_RX) && (s->type == SK_UDP))
if (sk_set_udp6_no_csum_rx(s) < 0)
return -1;
}
/* Must be after sk_set_tos4() as setting ToS on Linux also mangles priority */
@ -1190,6 +1232,9 @@ sk_setup(sock *s)
if (sk_set_priority(s, s->priority) < 0)
return -1;
if ((s->type == SK_UDP) || (s->type == SK_IP))
sk_set_min_rcvbuf(s, s->rbsize);
return 0;
}
@ -1680,7 +1725,7 @@ err:
}
int
sk_open_unix(sock *s, struct birdloop *loop, char *name)
sk_open_unix(sock *s, struct birdloop *loop, const char *name)
{
struct sockaddr_un sa;
int fd;
@ -2572,7 +2617,7 @@ io_loop(void)
}
void
test_old_bird(char *path)
test_old_bird(const char *path)
{
int fd;
struct sockaddr_un sa;

View File

@ -51,7 +51,7 @@ static struct log_channel * _Atomic global_logs;
/* Logging flags to validly prepare logging messages */
static _Atomic uint logging_flags;
static _Atomic uint logging_mask;
static _Atomic uint logging_mask = ~0U;
#ifdef HAVE_SYSLOG_H
#include <sys/syslog.h>
@ -189,9 +189,20 @@ log_commit(log_buffer *buf)
memcpy(buf->buf.end - sizeof TOO_LONG, TOO_LONG, sizeof TOO_LONG);
#undef TOO_LONG
struct log_channel *glogs = atomic_load_explicit(&global_logs, memory_order_acquire);
if (!glogs)
{
static struct log_channel initial_stderr_log = {
.rf = &rf_stderr,
.mask = ~0,
.prepare = BIT32_ALL(LBPP_TERMINAL, LBP_CLASS, LBP_MSG),
};
glogs = &initial_stderr_log;
}
for (
struct log_channel *l = atomic_load_explicit(&global_logs, memory_order_acquire);
l;
struct log_channel *l = glogs; l;
l = atomic_load_explicit(&l->next, memory_order_acquire)
)
{

View File

@ -30,6 +30,7 @@
#include "lib/event.h"
#include "lib/locking.h"
#include "lib/timer.h"
#include "lib/tlists.h"
#include "lib/string.h"
#include "nest/route.h"
#include "nest/protocol.h"
@ -186,6 +187,8 @@ cf_read(byte *dest, uint len, int fd)
return l;
}
static void cli_preconfig(struct config *c);
void
sysdep_preconfig(struct config *c)
{
@ -200,14 +203,19 @@ sysdep_preconfig(struct config *c)
read_iproute_table(c, PATH_IPROUTE_DIR "/rt_scopes", "ips_", 255);
read_iproute_table(c, PATH_IPROUTE_DIR "/rt_tables", "ipt_", 0xffffffff);
#endif
cli_preconfig(c);
}
static void cli_commit(struct config *new, struct config *old);
void
sysdep_commit(struct config *new, struct config *old)
{
if (!new->shutdown)
log_switch(0, &new->logfiles, new->syslog_name);
cli_commit(new, old);
bird_thread_commit(new, old);
}
@ -400,9 +408,28 @@ cmd_reconfig_status(void)
* Command-Line Interface
*/
static sock *cli_sk;
static char *path_control_socket = PATH_CONTROL_SOCKET;
static struct cli_config initial_control_socket_config = {
.name = PATH_CONTROL_SOCKET,
.mode = 0660,
};
#define path_control_socket initial_control_socket_config.name
static struct cli_config *main_control_socket_config = NULL;
#define TLIST_PREFIX cli_listener
#define TLIST_TYPE struct cli_listener
#define TLIST_ITEM n
#define TLIST_WANT_ADD_TAIL
#define TLIST_WANT_WALK
static struct cli_listener {
TLIST_DEFAULT_NODE;
sock *s;
struct cli_config *config;
} *main_control_socket = NULL;
#include "lib/tlists.h"
static TLIST_LIST(cli_listener) cli_listeners;
static void
cli_write(cli *c)
@ -523,7 +550,7 @@ cli_connect(sock *s, uint size UNUSED)
s->rx_hook = cli_rx;
s->tx_hook = cli_tx;
s->err_hook = cli_err;
s->data = c = cli_new(s);
s->data = c = cli_new(s, ((struct cli_listener *) s->data)->config);
s->pool = c->pool; /* We need to have all the socket buffers allocated in the cli pool */
s->fast_rx = 1;
c->rx_pos = c->rx_buf;
@ -531,33 +558,116 @@ cli_connect(sock *s, uint size UNUSED)
return 1;
}
static void
cli_init_unix(uid_t use_uid, gid_t use_gid)
static struct cli_listener *
cli_listen(struct cli_config *cf)
{
sock *s;
cli_init();
s = cli_sk = sk_new(cli_pool);
struct cli_listener *l = mb_allocz(cli_pool, sizeof *l);
l->config = cf;
sock *s = l->s = sk_new(cli_pool);
s->type = SK_UNIX_PASSIVE;
s->rx_hook = cli_connect;
s->err_hook = cli_connect_err;
s->data = l;
s->rbsize = 1024;
s->fast_rx = 1;
/* Return value intentionally ignored */
unlink(path_control_socket);
unlink(cf->name);
if (sk_open_unix(s, &main_birdloop, path_control_socket) < 0)
die("Cannot create control socket %s: %m", path_control_socket);
if (sk_open_unix(s, &main_birdloop, cf->name) < 0)
{
log(L_ERR "Cannot create control socket %s: %m", cf->name);
return NULL;
}
if (use_uid || use_gid)
if (chown(path_control_socket, use_uid, use_gid) < 0)
die("chown: %m");
if (cf->uid || cf->gid)
if (chown(cf->name, cf->uid, cf->gid) < 0)
{
log(L_ERR "Cannot chown control socket %s: %m", cf->name);
return NULL;
}
if (chmod(path_control_socket, 0660) < 0)
die("chmod: %m");
if (chmod(cf->name, cf->mode) < 0)
{
log(L_ERR "Cannot chmod control socket %s: %m", cf->name);
return NULL;
}
cli_listener_add_tail(&cli_listeners, l);
return l;
}
static void
cli_deafen(struct cli_listener *l)
{
rfree(l->s);
unlink(l->config->name);
cli_listener_rem_node(&cli_listeners, l);
mb_free(l);
}
static void
cli_init_unix(uid_t use_uid, gid_t use_gid)
{
ASSERT_DIE(main_control_socket_config == NULL);
main_control_socket_config = &initial_control_socket_config;
main_control_socket_config->uid = use_uid;
main_control_socket_config->gid = use_gid;
ASSERT_DIE(main_control_socket == NULL);
main_control_socket = cli_listen(main_control_socket_config);
if (!main_control_socket)
die("Won't run without control socket");
}
static void
cli_preconfig(struct config *c)
{
if (!main_control_socket_config)
return;
struct cli_config *ccf = mb_alloc(cli_pool, sizeof *ccf);
memcpy(ccf, main_control_socket_config, sizeof *ccf);
ccf->n = (struct cli_config_node) {};
ccf->config = c;
cli_config_add_tail(&c->cli, ccf);
}
static void
cli_commit(struct config *new, struct config *old)
{
if (new->shutdown)
{
/* Keep the main CLI throughout the shutdown */
initial_control_socket_config.config = new;
main_control_socket->config = &initial_control_socket_config;
}
WALK_TLIST(cli_config, c, &new->cli)
{
_Bool seen = 0;
WALK_TLIST(cli_listener, l, &cli_listeners)
if (l->config->config != new)
if (!strcmp(l->config->name, c->name))
{
ASSERT_DIE(l->config->config == old);
l->config = c;
seen = 1;
break;
}
if (!seen)
cli_listen(c);
}
WALK_TLIST_DELSAFE(cli_listener, l, &cli_listeners)
if (l->config->config != new)
cli_deafen(l);
}
/*
* PID file
*/
@ -635,7 +745,7 @@ void
sysdep_shutdown_done(void)
{
unlink_pid_file();
unlink(path_control_socket);
cli_deafen(main_control_socket);
log_msg(L_FATAL "Shutdown completed");
exit(0);
}
@ -920,6 +1030,8 @@ main(int argc, char **argv)
uid_t use_uid = get_uid(use_user);
gid_t use_gid = get_gid(use_group);
cli_init();
if (!parse_and_exit)
{
test_old_bird(path_control_socket);

View File

@ -114,7 +114,7 @@ extern volatile sig_atomic_t async_shutdown_flag;
void io_init(void);
void io_loop(void);
void io_log_dump(void);
int sk_open_unix(struct birdsock *s, struct birdloop *, char *name);
int sk_open_unix(struct birdsock *s, struct birdloop *, const char *name);
enum rf_mode {
RF_APPEND = 1,
@ -130,7 +130,7 @@ int rf_fileno(struct rfile *f);
extern struct rfile rf_stderr;
void test_old_bird(char *path);
void test_old_bird(const char *path);
ip_addr resolve_hostname(const char *host, int type, const char **err_msg);
/* krt.c bits */