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 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 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 users mailing list bird-users@network.cz, just send `subscribe' to
bird-request@network.cz. Bug reports, suggestions, feature requests and bird-request@network.cz.
code are welcome! We don't use gitlab issues for reporting, sorry.
Subscribe: http://bird.network.cz/mailman/listinfo/bird-users/ Subscribe: http://bird.network.cz/mailman/listinfo/bird-users/
Archive: http://bird.network.cz/pipermail/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 Licence
======= =======

View File

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

View File

@ -715,7 +715,7 @@ cf_lex_symbol(const char *data)
int val = sym->keyword->value; int val = sym->keyword->value;
if (val > 0) return val; if (val > 0) return val;
cf_lval.i = -val; cf_lval.i = -val;
return ENUM; return ENUM_TOKEN;
} }
case SYM_METHOD: case SYM_METHOD:
return (sym->method->arg_num > 1) ? CF_SYM_METHOD_ARGS : CF_SYM_METHOD_BARE; 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->mpls_domains);
init_list(&c->symbols); init_list(&c->symbols);
obstacle_target_init(&c->obstacles, &c->obstacles_cleared, c->pool, "Config"); 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)); memset(c->def_tables, 0, sizeof(c->def_tables));
c->shutdown = 1; c->shutdown = 1;
c->gr_down = gr; c->gr_down = gr;

View File

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

View File

@ -100,7 +100,8 @@ CF_DECLS
struct f_prefix px; struct f_prefix px;
struct proto_spec ps; struct proto_spec ps;
struct channel_limit cl; struct channel_limit cl;
struct timeformat *tf; struct timeformat tf;
struct timeformat *tfp;
struct settle_config settle; struct settle_config settle;
struct adata *ad; struct adata *ad;
const struct adata *bs; const struct adata *bs;
@ -110,7 +111,7 @@ CF_DECLS
%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT %token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT
%token GEQ LEQ NEQ AND OR IMP %token GEQ LEQ NEQ AND OR IMP
%token PO PC %token PO PC
%token <i> NUM ENUM %token <i> NUM ENUM_TOKEN
%token <ip4> IP4 %token <ip4> IP4
%token <ip6> IP6 %token <ip6> IP6
%token <i64> VPN_RD %token <i64> VPN_RD
@ -124,7 +125,7 @@ CF_DECLS
%type <settle> settle %type <settle> settle
%type <a> ipa net_ip6_slash %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> 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 <ad> label_stack_start label_stack
%type <t> text opttext %type <t> text opttext
@ -325,6 +326,12 @@ net_mpls_: MPLS NUM
net_fill_mpls($$, $2); 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_ip_: net_ip4_ | net_ip6_ ;
net_vpn_: net_vpn4_ | net_vpn6_ ; net_vpn_: net_vpn4_ | net_vpn6_ ;
net_roa_: net_roa4_ | net_roa6_ ; net_roa_: net_roa4_ | net_roa6_ ;
@ -336,6 +343,7 @@ net_:
| net_flow_ | net_flow_
| net_ip6_sadr_ | net_ip6_sadr_
| net_mpls_ | 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) # Enums are translated to C initializers: use CF_ENUM(typename, prefix, values)
# For different prefix: CF_ENUM_PX(typename, external prefix, C 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_define(CF_enum, `m4_divert(1){ "CF_enum_prefix_ext[[]]$1", -((CF_enum_type<<16) | CF_enum_prefix_int[[]]$1) },
m4_divert(-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, `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, `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_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 # After all configuration templates end, we generate the keyword list
m4_m4wrap(` 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_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_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 # 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_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 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, `') m4_define(CF_CLI_HELP, `')
# ENUM declarations are ignored # ENUM declarations are ignored
m4_define(CF_ENUM, `') m4_define(CF_token, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)%token<s> $1]])')
m4_define(CF_ENUM_PX, `') 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. # After all configuration templates end, we finally generate the grammar file.
m4_m4wrap(` m4_m4wrap(`
@ -58,7 +62,11 @@ m4_undivert(1)DNL
m4_undivert(2)DNL m4_undivert(2)DNL
%type <i> enum_type
%% %%
enum_type: CF_enum_type;
m4_undivert(3)DNL m4_undivert(3)DNL
%% %%

View File

@ -114,7 +114,7 @@ AC_SEARCH_LIBS([clock_gettime], [rt posix4],
AC_CANONICAL_HOST AC_CANONICAL_HOST
# Store this value because ac_test_CFLAGS is overwritten by AC_PROG_CC # 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 bird_cflags_default=yes
fi 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: and Perl, installing BIRD should be as easy as:
<code> <code>
./configure ./configure
make make
make install make install
vi /usr/local/etc/bird.conf vi /usr/local/etc/bird.conf
bird bird
</code> </code>
<p>You can use <tt>./configure --help</tt> to get a list of configure <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> <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 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> <tag><label id="proto-pass-gen-to">generate to "<m/time/"</tag>
The last time of the usage of the password for packet signing. 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> <tag><label id="proto-rpki-reload">rpki reload <m/switch/</tag>
Import or export filters may depend on route RPKI status (using Import or export filters may depend on route RPKI status (using
<cf/roa_check()/ operator). In contrast to to other filter operators, <cf/roa_check()/ or <cf/aspa_check()/ operators). In contrast to other
this status for the same route may change as the content of ROA tables filter operators, this status for the same route may change as the
changes. When this option is active, BIRD activates automatic reload of content of ROA and ASPA tables changes. When this option is active, BIRD
the appropriate subset of prefixes imported or exported by the channels activates automatic reload of the appropriate subset of prefixes imported
whenever ROA tables are updated (after a short settle or exported by the channels whenever ROA and ASPA
time). When disabled, route reloads have to be requested manually. The tables are updated (after a short settle time). When disabled, route
option is ignored if <cf/roa_check()/ is not used in channel filters. reloads have to be requested manually. The option is ignored if neither
Note that for BGP channels, automatic reload requires <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-import-table" name="import table"> or
<ref id="bgp-export-table" name="export table"> (for respective <ref id="bgp-export-table" name="export table"> (for respective
direction). Default: on. direction). Default: on.
@ -1264,9 +1265,11 @@ protocol bgp {
<chapt>Remote control <chapt>Remote control
<label id="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 <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 BIRD. Communication is done using the appropriate UNIX domain socket. The
changed with the <tt/-s/ option given to both the server and the client). The
commands can perform simple actions such as enabling/disabling of protocols, commands can perform simple actions such as enabling/disabling of protocols,
telling BIRD to show various information, telling it to show routing table 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 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 This is useful for running BIRD in resource constrained environments, where
Readline library (required for regular BIRD client) is not available. Readline library (required for regular BIRD client) is not available.
<p>Many commands have the <m/name/ of the protocol instance as an argument. <sect>Configuration
This argument can be omitted if there exists only a single instance. <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> <descrip>
<tag><label id="cli-show-status">show status</tag> <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 pipe protocol, both directions are always reloaded together (<cf/in/ or
<cf/out/ options are ignored in that case). <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> <tag><label id="cli-down">down</tag>
Shut BIRD down. Shut BIRD down.
@ -1627,9 +1659,9 @@ in the foot).
a shell pattern (represented also as a string). a shell pattern (represented also as a string).
<tag><label id="type-bytestring">bytestring</tag> <tag><label id="type-bytestring">bytestring</tag>
This is a sequences of arbitrary bytes. There are no ways to modify This is a sequence of arbitrary bytes. There are no ways to modify
bytestrings in filters. You can pass them between function, assign bytestrings in filters. You can pass them between functions, assign
them to variables of type <cf/bytestring/, print such values, them to variables of type <cf/bytestring/, print such values, and
compare bytestings (<cf/=, !=/). compare bytestings (<cf/=, !=/).
Bytestring literals are written as a sequence of hexadecimal digit 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 Route Distinguisher (<rfc id="4364">). They support the same special
operators as IP prefixes, and also <cf/.rd/ which extracts the Route operators as IP prefixes, and also <cf/.rd/ which extracts the Route
Distinguisher. Their literals are written 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 <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 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 <cf/NET_MPLS/ holds a single MPLS label and its handling is currently
not implemented. 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 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> and <cf><m/IPv4 address/:<m/32bit int/</cf>
<tag><label id="type-ec">ec</tag> <tag><label id="type-ec">ec</tag>
@ -1722,9 +1754,9 @@ in the foot).
to extract corresponding components of LCs: to extract corresponding components of LCs:
<cf>(<m/asn/, <m/data1/, <m/data2/)</cf>. <cf>(<m/asn/, <m/data1/, <m/data2/)</cf>.
<tag><label id="type-set">int|pair|quad|ip|prefix|ec|lc|enum set</tag> <tag><label id="type-set">int|pair|quad|ip|prefix|ec|lc|rd|enum set</tag>
Filters recognize four types of sets. Sets are similar to strings: you Filters recognize several types of sets. Sets are similar to strings: you
can pass them around but you can't modify them. Literals of type <cf>int 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 set</cf> look like <cf> [ 1, 2, 5..7 ]</cf>. As you can see, both simple
values and ranges are permitted in sets. 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 is valid, while <cf/(10, *, 20..30)/ or <cf/(10, 20..30, 40)/ is not
valid. valid.
You can also use expressions for int, pair, EC and LC set values. You can also use named constants or compound expressions for non-prefix
However, it must be possible to evaluate these expressions before daemon set values. However, it must be possible to evaluate these expressions
boots. So you can use only constants inside them. E.g. 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> <code>
define one=1; define one=1;
define myas=64500; define myas=64500;
int set odds;
pair set ps;
ec set es;
odds = [ one, 2+1, 6-one, 2*2*2-1, 9, 11 ]; int set 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,*) ]; pair set 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, *) ]; ec set es = [ (rt, myas, *), (rt, myas+2, 0..16*16*16-1) ];
</code> </code>
Sets of prefixes are special: their literals does not allow ranges, but Sets of prefixes are special: their literals does not allow ranges, but
allows prefix patterns that are written allows prefix patterns that are written
@ -1923,32 +1954,93 @@ in the foot).
<label id="operators"> <label id="operators">
<p>The filter language supports common integer operators <cf>(+,-,*,/)</cf>, <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)/. parentheses <cf/(a*(b+c))/, comparison <cf/(a=b, a!=b, a&lt;b, a&gt;=b)/.</p>
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).
<p>There is one operator related to ROA infrastructure - <cf/roa_check()/. It <p>Logical operations include unary not (<cf/!/), and (<cf/&amp;&amp;/), and or
examines a ROA table and does <rfc id="6483"> route origin validation for a (<cf/&verbar;&verbar;/).</p>
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>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 <sect>Control structures
<label id="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. and name is used). In both cases, it must have the same type as elements.
<p>The <cf>case</cf> is similar to case from Pascal. Syntax is <cf>case <p>The <cf>case</cf> is similar to case from Pascal. Syntax is <cf>case
<m/expr/ { else: | <m/num_or_prefix [ .. num_or_prefix]/: <m/statement/ ; [ <m/expr/ { else: | <m/set_body_expr/ /: <m/statement/ ; [... ] }</cf>.
... ] }</cf>. The expression after <cf>case</cf> can be of any type which can be The expression after <cf>case</cf> can be of any type that could be a member of
on the left side of the &tilde; operator and anything that could be a member of a set, while the <m/set_body_expr/ before <cf/:/ can be anything (constants,
a set is allowed before <cf/:/. Multiple commands are allowed without <cf/{}/ intervals, expressions) that could be a part of a set literal. One exception is
grouping. If <cf><m/expr/</cf> matches one of the <cf/:/ clauses, statements prefix type, which can be used in sets bud not in <cf/case/ structure. Multiple
between it and next <cf/:/ statement are executed. If <cf><m/expr/</cf> matches commands must be grouped by <cf/{}/. If <cf><m/expr/</cf> matches one
neither of the <cf/:/ clauses, the statements after <cf/else:/ are executed. 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: <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 { 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"; 3 .. 5: print "three to five";
else: print "something else"; 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). the BFD session went down).
<p>BIRD implements basic BFD behavior as defined in <rfc id="5880"> (some <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 advanced features like the echo mode are not implemented), IP transport for BFD
transport for BFD as defined in <rfc id="5881"> and <rfc id="5883"> and as defined in <rfc id="5881"> and <rfc id="5883"> and interaction with client
interaction with client protocols as defined in <rfc id="5882">. protocols as defined in <rfc id="5882">.
<p>BFD packets are sent with a dynamic source port number. Linux systems use by <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 default a bit different dynamic port range than the IANA approved one
@ -2534,6 +2628,8 @@ milliseconds.
<code> <code>
protocol bfd [&lt;name&gt;] { protocol bfd [&lt;name&gt;] {
accept [ipv4|ipv6] [direct|multihop]; accept [ipv4|ipv6] [direct|multihop];
strict bind &lt;switch&gt;;
zero udp6 checksum rx &lt;switch&gt;;
interface &lt;interface pattern&gt; { interface &lt;interface pattern&gt; {
interval &lt;time&gt;; interval &lt;time&gt;;
min rx 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 in cases like running multiple BIRD instances on a machine, each
handling a different set of interfaces. Default: disabled. 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> <tag><label id="bfd-iface">interface <m/pattern/ [, <m/.../] { <m/options/ }</tag>
Interface definitions allow to specify options for sessions associated Interface definitions allow to specify options for sessions associated
with such interfaces and also may contain interface specific options. 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="9072"> - Extended Optional Parameters Length for BGP OPEN Message
<item> <rfc id="9117"> - Revised Validation Procedure for BGP Flow Specifications <item> <rfc id="9117"> - Revised Validation Procedure for BGP Flow Specifications
<item> <rfc id="9234"> - Route Leak Prevention and Detection Using Roles <item> <rfc id="9234"> - Route Leak Prevention and Detection Using Roles
<item> <rfc id="9687"> - Send Hold Timer
</itemize> </itemize>
<sect1>Route selection rules <sect1>Route selection rules
@ -2915,10 +3020,9 @@ using the following configuration parameters:
restarted. Optionally, it can be configured (by <cf/graceful/ argument) restarted. Optionally, it can be configured (by <cf/graceful/ argument)
to trigger graceful restart instead of regular restart. It is also to trigger graceful restart instead of regular restart. It is also
possible to specify section with per-peer BFD session options instead of possible to specify section with per-peer BFD session options instead of
just switch argument. Most BFD session specific options are allowed here just the switch argument. All BFD session-specific options are allowed
with the exception of authentication options. here Note that BFD here. Note that BFD protocol also has to be configured, see
protocol also has to be configured, see <ref id="bfd" name="BFD"> <ref id="bfd" name="BFD"> section for details. Default: disabled.
section for details. Default: disabled.
<tag><label id="bgp-ttl-security">ttl security <m/switch/</tag> <tag><label id="bgp-ttl-security">ttl security <m/switch/</tag>
Use GTSM (<rfc id="5082"> - the generalized TTL security mechanism). GTSM 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> <tag><label id="bgp-interpret-communities">interpret communities <m/switch/</tag>
<rfc id="1997"> demands that BGP speaker should process well-known <rfc id="1997"> demands that BGP speaker should process well-known
communities like no-export (65535, 65281) or no-advertise (65535, 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 should not be advertised to any of its neighbors. If this option is
enabled (which is by default), BIRD has such behavior automatically (it enabled (which is by default), BIRD has such behavior automatically (it
is evaluated when a route is exported to the BGP protocol just before 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, Send hold timer drops the session if the neighbor is sending keepalives,
but does not receive our messages, causing the TCP connection to stall. but does not receive our messages, causing the TCP connection to stall.
This may happen due to malfunctioning or overwhelmed neighbor. See This may happen due to malfunctioning or overwhelmed neighbor. See
<HTMLURL URL="https://datatracker.ietf.org/doc/draft-ietf-idr-bgp-sendholdtimer/" <rfc id="9687"> for more details.
name="draft-ietf-idr-bgp-sendholdtimer"> for more details.
Like the option <cf/keepalive time/, the effective value depends on the Like the option <cf/keepalive time/, the effective value depends on the
negotiated hold time, as it is scaled to maintain proportion between 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/. <cf/mpls/.
<p><descrip> <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 The route distinguisher that is attached to routes in the export
direction. Mandatory. 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/. A shorthand for the option <cf/route distinguisher/.
<tag><label id="l3vpn-import-target">import target <m/ec/|<m/ec-set/</tag> <tag><label id="l3vpn-import-target">import target <m/ec/|<m/ec-set/</tag>
@ -4846,14 +4949,14 @@ protocol ospf MyOSPF {
authentication cryptographic; authentication cryptographic;
password "abc" { password "abc" {
id 1; id 1;
generate to "22-04-2003 11:00:06"; generate to "2023-04-22 11:00:06";
accept from "17-01-2001 12:01:05"; accept from "2021-01-17 12:01:05";
algorithm hmac sha384; algorithm hmac sha384;
}; };
password "def" { password "def" {
id 2; id 2;
generate to "22-07-2005 17:03:21"; generate to "2025-07-22";
accept from "22-02-2001 11:34:06"; accept from "2021-02-22";
algorithm hmac sha512; 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 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. <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 <sect1>Supported transports
<p> <p>
<itemize> <itemize>
@ -5690,22 +5797,28 @@ define more RPKI protocols generally.
protocol rpki [&lt;name&gt;] { protocol rpki [&lt;name&gt;] {
roa4 { table &lt;tab&gt;; }; roa4 { table &lt;tab&gt;; };
roa6 { 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;]; remote &lt;ip&gt; | "&lt;domain&gt;" [port &lt;num&gt;];
port &lt;num&gt;; port &lt;num&gt;;
local address &lt;ip&gt;; local address &lt;ip&gt;;
refresh [keep] &lt;num&gt;; refresh [keep] &lt;num&gt;;
retry [keep] &lt;num&gt;; retry [keep] &lt;num&gt;;
expire [keep] &lt;num&gt;; expire [keep] &lt;num&gt;;
transport tcp; transport tcp {
authentication none|md5;
password "&lt;text&gt;";
};
transport ssh { transport ssh {
bird private key "&lt;/path/to/id_rsa&gt;"; bird private key "&lt;/path/to/id_rsa&gt;";
remote public key "&lt;/path/to/known_host&gt;"; remote public key "&lt;/path/to/known_host&gt;";
user "&lt;name&gt;"; user "&lt;name&gt;";
}; };
max version 2;
min version 2;
} }
</code> </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 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 prefixes only. If you want to fetch both IPv4 and even IPv6 ROAs you have to
specify both channels. specify both channels.
@ -5752,15 +5865,37 @@ specify both channels.
instead. This may be useful for implementing loose RPKI check for instead. This may be useful for implementing loose RPKI check for
blackholes. Default: disabled. blackholes. Default: disabled.
<tag>transport tcp</tag> Unprotected transport over TCP. It's a default <tag>min version <m/num/</tag>
transport. Should be used only on secure private networks. Minimal allowed version of the RTR protocol. BIRD will refuse to
Default: tcp 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 <tag>transport ssh { <m/SSH transport options.../ }</tag> It enables a
SSHv2 transport encryption. Cannot be combined with a TCP transport. SSHv2 transport encryption. Cannot be combined with a TCP transport.
Default: off Default: off
</descrip> </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 <sect3>SSH transport options
<p> <p>
<descrip> <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. <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 <sect1>Flowspec
<label id="flowspec-network-type"> <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. 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 ## Route attributes
All protocol attributes have been renamed in CLI to align with the filter language tokens. 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. 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 ## Pipe
It's now impossible to check immediately whether the route has entered a 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 9000 Command too long
9001 Parse error 9001 Parse error
9002 Invalid symbol type 9002 Invalid symbol type
9003 Argument too long

View File

@ -362,13 +362,13 @@ CF_DECLS
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
ACCEPT, REJECT, ERROR, 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, SET, STRING, BYTESTRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
IF, THEN, ELSE, CASE, IF, THEN, ELSE, CASE,
FOR, IN, DO, FOR, IN, DO,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC, TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
FROM, GW, NET, PROTO, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS, FROM, GW, NET, PROTO, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
ROA_CHECK, ROA_CHECK, ASPA_CHECK,
DEFINED, DEFINED,
ADD, DELETE, RESET, ADD, DELETE, RESET,
PREPEND, PREPEND,
@ -381,7 +381,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%nonassoc ELSE %nonassoc ELSE
%type <xp> cmds_int cmd_prep %type <xp> cmds_int cmd_prep
%type <x> term 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 <fsa> static_attr
%type <f> filter where_filter %type <f> filter where_filter
%type <fl> filter_body function_body %type <fl> filter_body function_body
@ -393,7 +393,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%type <i32> cnum %type <i32> cnum
%type <e> pair_item ec_item lc_item set_item switch_item ec_items set_items switch_items switch_body %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 <trie> fprefix_set
%type <v> set_atom switch_atom fipa %type <v> set_atom0 set_atom switch_atom fipa
%type <px> fprefix %type <px> fprefix
%type <t> get_cf_position %type <t> get_cf_position
%type <s> for_var %type <s> for_var
@ -498,6 +498,7 @@ type:
case T_INT: case T_INT:
case T_PAIR: case T_PAIR:
case T_QUAD: case T_QUAD:
case T_ENUM:
case T_EC: case T_EC:
case T_LC: case T_LC:
case T_RD: case T_RD:
@ -513,6 +514,13 @@ type:
cf_error( "You can't create sets of this 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: function_argsn:
@ -625,8 +633,6 @@ cmds: /* EMPTY */ { $$ = NULL; }
| cmds_int { $$ = $1.begin; } | 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_var: var | cmd ;
cmd_prep: cmd_var { cmd_prep: cmd_var {
@ -668,27 +674,30 @@ fipa:
* as a function call in switch case cmds. * as a function call in switch case cmds.
*/ */
set_atom: set_atom0:
NUM { $$.type = T_INT; $$.val.i = $1; } NUM { $$.type = T_INT; $$.val.i = $1; }
| fipa { $$ = $1; } | fipa { $$ = $1; }
| VPN_RD { $$.type = T_RD; $$.val.ec = $1; } | VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); } | ENUM_TOKEN { $$.type = pair_a($1); $$.val.i = pair_b($1); }
| '(' term ')' { | '(' term ')' {
$$ = cf_eval_tmp($2, T_VOID); $$ = 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_SYM_KNOWN {
cf_assert_symbol($1, SYM_CONSTANT); 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; $$ = *$1->val;
} }
; ;
switch_atom: switch_atom:
NUM { $$.type = T_INT; $$.val.i = $1; } set_atom0
| '(' term ')' { $$ = cf_eval_tmp($2, T_INT); }
| fipa { $$ = $1; }
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
; ;
cnum: cnum:
@ -781,14 +790,14 @@ fprefix_set:
; ;
switch_body: /* EMPTY */ { $$ = NULL; } switch_body: /* EMPTY */ { $$ = NULL; }
| switch_body switch_items ':' cmds_scoped { | switch_body switch_items ':' cmd {
/* Fill data fields */ /* Fill data fields */
struct f_tree *t; struct f_tree *t;
for (t = $2; t; t = t->left) for (t = $2; t; t = t->left)
t->data = $4; t->data = $4;
$$ = f_merge_items($1, $2); $$ = f_merge_items($1, $2);
} }
| switch_body ELSECOL cmds_scoped { | switch_body ELSECOL cmd {
struct f_tree *t = f_new_tree(); struct f_tree *t = f_new_tree();
t->from.type = t->to.type = T_VOID; t->from.type = t->to.type = T_VOID;
t->right = t; t->right = t;
@ -837,7 +846,7 @@ constant:
DBG( "ook\n" ); DBG( "ook\n" );
} }
| '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PREFIX_SET, .val.ti = $2, }); } | '[' 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: constructor:
@ -956,6 +965,7 @@ term:
| ROA_CHECK '(' rtable ')' { $$ = f_implicit_roa_check($3); } | ROA_CHECK '(' rtable ')' { $$ = f_implicit_roa_check($3); }
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK, $5, $7, $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); } | FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
@ -990,9 +1000,7 @@ for_var:
; ;
cmd: cmd:
'{' cmds_scoped '}' { '{' { cf_push_soft_scope(new_config); } cmds { cf_pop_soft_scope(new_config); } '}' { $$ = $3; }
$$ = $2;
}
| IF term THEN cmd { | IF term THEN cmd {
$$ = f_new_inst(FI_CONDITION, $2, $4, NULL); $$ = 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_SCOPE] = "enum scope",
[T_ENUM_RTD] = "enum rtd", [T_ENUM_RTD] = "enum rtd",
[T_ENUM_ROA] = "enum roa", [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_RA_PREFERENCE] = "enum ra_preference",
[T_ENUM_AF] = "enum af", [T_ENUM_AF] = "enum af",
[T_ENUM_MPLS_POLICY] = "enum mpls_policy",
[T_IP] = "ip", [T_IP] = "ip",
[T_NET] = "prefix", [T_NET] = "prefix",

View File

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

View File

@ -503,7 +503,7 @@
RESULT(T_BOOL, i, (v1.type != T_VOID) && !val_is_undefined(v1)); 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)); METHOD_R(T_IP, is_v4, T_BOOL, i, ipa_is_ip4(v1.val.ip));
/* Add initialized variable */ /* 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) else if ((empty = f_get_empty(da->type)).type != T_VOID)
RESULT_VAL(empty); RESULT_VAL(empty);
else 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 */ INST(FI_FROM_HEX, 1, 1) { /* Convert hex text to bytestring */
ARG(1, T_STRING); 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_max20;
attribute lclist test_ca_lclist_max21; 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 */ /* Uncomment this to get an error */
#attribute int bgp_path; #attribute int bgp_path;
@ -195,8 +206,11 @@ bt_test_suite(t_bool, "Testing boolean expressions");
function aux_t_int(int t; int u) function aux_t_int(int t; int u)
{ {
int v;
case t { case t {
1: {} 1: {}
2: { int u; u = 1; v = 42; }
3: if true then v = t + u + 53; else v = 35 + u + t;
else: {} else: {}
} }
} }
@ -249,7 +263,7 @@ function t_int()
} }
case four { case four {
4: bt_assert(true); (2+2): bt_assert(true);
else: bt_assert(false); else: bt_assert(false);
} }
@ -582,12 +596,31 @@ bt_test_suite(t_ip_set, "Testing sets of ip address");
function t_enum() 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(RTS_STATIC) = "(enum 31)1");
bt_assert(format(NET_IP4) = "(enum 3b)1"); bt_assert(format(NET_IP4) = "(enum 3b)1");
bt_assert(format(NET_VPN6) = "(enum 3b)4"); 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_STATIC ~ [RTS_STATIC, RTS_DEVICE]);
bt_assert(RTS_BGP !~ [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"); bt_test_suite(t_enum, "Testing enums");
@ -2298,9 +2331,9 @@ bool t;
krt_source = 17; krt_source = 17;
krt_metric = 19; krt_metric = 19;
# krt_lock_mtu = false; # if krt_lock_congctl then krt_lock_mtu = false;
# krt_lock_window = true; # 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"; accept "ok I take that";
} }
@ -2381,7 +2414,56 @@ prefix pfx;
bt_test_suite(t_roa_check, "Testing ROA"); 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 filter vpn_filter
{ {

View File

@ -34,7 +34,7 @@
* the buffer to indicate truncation. * the buffer to indicate truncation.
*/ */
int 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; u32 *z = (u32 *) set->data;
byte *end = buf + size - 24; 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) if (i > from2)
*buf++ = ' '; *buf++ = ' ';
if (way) switch (way)
buf += bsprintf(buf, "(%d,%d)", z[i] >> 16, z[i] & 0xffff); {
else case ISF_COMMUNITY_LIST:
buf += bsprintf(buf, "%R", z[i]); 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; *buf = 0;
return 0; return 0;

View File

@ -92,11 +92,11 @@ t_set_int_union(void)
const struct adata *set_union; const struct adata *set_union;
set_union = int_set_union(tmp_linpool, set_sequence, set_sequence_same); set_union = int_set_union(tmp_linpool, set_sequence, set_sequence_same);
bt_assert(int_set_get_size(set_union) == SET_SIZE); bt_assert(int_set_get_size(set_union) == SET_SIZE);
bt_assert(int_set_format(set_union, 0, 2, buf, 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); set_union = int_set_union(tmp_linpool, set_sequence, set_sequence_higher);
bt_assert_msg(int_set_get_size(set_union) == SET_SIZE*2, "int_set_get_size(set_union) %d, SET_SIZE*2 %d", int_set_get_size(set_union), SET_SIZE*2); bt_assert_msg(int_set_get_size(set_union) == SET_SIZE*2, "int_set_get_size(set_union) %d, SET_SIZE*2 %d", int_set_get_size(set_union), SET_SIZE*2);
bt_assert(int_set_format(set_union, 0, 2, buf, T_BUFFER_SIZE) == 0); bt_assert(int_set_format(set_union, ISF_ROUTER_ID, 2, buf, T_BUFFER_SIZE) == 0);
return 1; return 1;
} }
@ -106,17 +106,21 @@ t_set_int_format(void)
{ {
generate_set_sequence(SET_TYPE_INT, SET_SIZE_FOR_FORMAT_OUTPUT); generate_set_sequence(SET_TYPE_INT, SET_SIZE_FOR_FORMAT_OUTPUT);
bt_assert(int_set_format(set_sequence, 0, 0, buf, 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); 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); 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); 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); 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); 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; 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_to_old(struct linpool *pool, const struct adata *path);
struct adata *as_path_cut(struct linpool *pool, const struct adata *path, uint num); 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); 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(const struct adata *path);
int as_path_getlen_int(const struct adata *path, int bs); int as_path_getlen_int(const struct adata *path, int bs);
int as_path_get_first(const struct adata *path, u32 *orig_as); 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) static inline u32 *lc_copy(u32 *dst, const u32 *src)
{ memcpy(dst, src, LCOMM_LENGTH); return dst + 3; } { 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_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_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 int_set_contains(const struct adata *list, u32 val);
int ec_set_contains(const struct adata *list, u64 val); int ec_set_contains(const struct adata *list, u64 val);
int lc_set_contains(const struct adata *list, lcomm 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 USE_RESULT __atribute__((warn_unused_result))
#define UNUSED __attribute__((unused)) #define UNUSED __attribute__((unused))
#define PACKED __attribute__((packed)) #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))) #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(EXP) _Static_assert(EXP, #EXP)
#define STATIC_ASSERT_MSG(EXP,MSG) _Static_assert(EXP, MSG) #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 static inline ip6_addr
flow_read_ip6(const byte *px, uint pxlen, uint pxoffset) 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; ip6_addr ip = IP6_NONE;
memcpy(&ip, px, BYTES(pxlen - pxoffset));
memcpy(((byte *) &ip) + floor_offset, px, ceil_len - floor_offset); return ip6_shift_right(ip6_ntoh(ip), pxoffset);
return ip6_ntoh(ip);
} }
ip6_addr ip6_addr
@ -476,7 +472,7 @@ flow_validate(const byte *nlri, uint len, int ipv6)
uint pxoffset = *pos++; uint pxoffset = *pos++;
if (pxoffset > IP6_MAX_PREFIX_LENGTH || pxoffset > pxlen) if (pxoffset > IP6_MAX_PREFIX_LENGTH || pxoffset > pxlen)
return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET; return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET;
bytes -= pxoffset / 8; bytes = BYTES(pxlen - pxoffset);
} }
pos += bytes; 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)) if (!builder_add_prepare(fb))
return 0; 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) = fb->this_type;
BUFFER_PUSH(fb->data) = n6->pxlen; BUFFER_PUSH(fb->data) = n6->pxlen;
BUFFER_PUSH(fb->data) = pxoffset; 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); builder_add_finish(fb);
return 1; return 1;

View File

@ -9,21 +9,37 @@
#include "test/birdtest.h" #include "test/birdtest.h"
#include "lib/flowspec.h" #include "lib/flowspec.h"
#define NET_ADDR_FLOW4_(what,prefix,pxlen,data_) \ #define NET_ADDR_FLOW4_(prefix,pxlen,nlri) \
do \ ({ \
{ \ uint _len = sizeof(nlri); \
what = alloca(sizeof(net_addr_flow4) + 128); \ net_addr_flow4 *_n = tmp_alloc(sizeof(net_addr_flow4) + _len); \
*what = NET_ADDR_FLOW4(prefix, pxlen, sizeof(data_)); \ *_n = NET_ADDR_FLOW4(prefix, pxlen, _len); \
memcpy(what->data, &(data_), sizeof(data_)); \ memcpy(_n->data, &(nlri), _len); \
} while(0) if (_n->data[0] == 0) _n->data[0] = _len - 1; \
_n; \
})
#define NET_ADDR_FLOW6_(what,prefix,pxlen,data_) \ #define NET_ADDR_FLOW4_NLRI(...) \
do \ ({ \
{ \ const byte _nlri[] = { __VA_ARGS__ }; \
what = alloca(sizeof(net_addr_flow6) + 128); \ NET_ADDR_FLOW4_(flow_read_ip4_part(_nlri + 1), _nlri[2], _nlri); \
*what = NET_ADDR_FLOW6(prefix, pxlen, sizeof(data_)); \ })
memcpy(what->data, &(data_), sizeof(data_)); \
} while(0) #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 static int
t_read_length(void) t_read_length(void)
@ -67,13 +83,13 @@ t_write_length(void)
static int static int
t_first_part(void) t_first_part(void)
{ {
net_addr_flow4 *f; net_addr_flow4 *f = NET_ADDR_FLOW4_(IP4_NONE, 0, ((byte[]) { 0x00, 0x00, 0xab }));
NET_ADDR_FLOW4_(f, ip4_build(10,0,0,1), 24, ((byte[]) { 0x00, 0x00, 0xab }));
const byte *under240 = &f->data[1]; const byte *under240 = &f->data[1];
const byte *above240 = &f->data[2]; const byte *above240 = &f->data[2];
/* Case 0x00 0x00 */ /* Case 0x00 0x00 */
f->data[0] = 0x00;
bt_assert(flow4_first_part(f) == NULL); bt_assert(flow4_first_part(f) == NULL);
/* Case 0x01 0x00 */ /* Case 0x01 0x00 */
@ -103,15 +119,14 @@ t_first_part(void)
static int static int
t_iterators4(void) t_iterators4(void)
{ {
net_addr_flow4 *f; const net_addr_flow4 *f = NET_ADDR_FLOW4_NLRI(
NET_ADDR_FLOW4_(f, ip4_build(5,6,7,0), 24, ((byte[]) {
25, /* Length */ 25, /* Length */
FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7, FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13, FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06, FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90, FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55, FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
})); );
const byte *start = f->data; const byte *start = f->data;
const byte *p1_dst_pfx = &f->data[1]; const byte *p1_dst_pfx = &f->data[1];
@ -136,15 +151,14 @@ t_iterators4(void)
static int static int
t_iterators6(void) t_iterators6(void)
{ {
net_addr_flow6 *f; const net_addr_flow6 *f = NET_ADDR_FLOW6_NLRI(
NET_ADDR_FLOW6_(f, ip6_build(0,0,0x12345678,0x9a000000), 0x68, ((byte[]) {
26, /* Length */ 26, /* Length */
FLOW_TYPE_DST_PREFIX, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a, FLOW_TYPE_DST_PREFIX, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a,
FLOW_TYPE_SRC_PREFIX, 0x08, 0x0, 0xc0, FLOW_TYPE_SRC_PREFIX, 0x08, 0x0, 0xc0,
FLOW_TYPE_NEXT_HEADER, 0x81, 0x06, FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90, FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
FLOW_TYPE_LABEL, 0x80, 0x55, FLOW_TYPE_LABEL, 0x80, 0x55,
})); );
const byte *start = f->data; const byte *start = f->data;
const byte *p1_dst_pfx = &f->data[1]; const byte *p1_dst_pfx = &f->data[1];
@ -169,15 +183,14 @@ t_iterators6(void)
static int static int
t_accessors4(void) t_accessors4(void)
{ {
net_addr_flow4 *f; const net_addr_flow4 *f = NET_ADDR_FLOW4_NLRI(
NET_ADDR_FLOW4_(f, ip4_build(5,6,7,0), 24, ((byte[]) {
25, /* Length */ 25, /* Length */
FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7, FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13, FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06, FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90, FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55, FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
})); );
const byte *p1_dst_px = &f->data[1]; const byte *p1_dst_px = &f->data[1];
const ip4_addr p1_dst_addr = ip4_build(5,6,7,0); const ip4_addr p1_dst_addr = ip4_build(5,6,7,0);
@ -194,15 +207,14 @@ t_accessors4(void)
static int static int
t_accessors6(void) t_accessors6(void)
{ {
net_addr_flow6 *f; const net_addr_flow6 *f = NET_ADDR_FLOW6_NLRI(
NET_ADDR_FLOW6_(f, ip6_build(0,0,0x12345678,0x9a000000), 0x68, ((byte[]) {
26, /* Length */ 26, /* Length */
FLOW_TYPE_DST_PREFIX, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a, FLOW_TYPE_DST_PREFIX, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a,
FLOW_TYPE_SRC_PREFIX, 0x08, 0x0, 0xc0, FLOW_TYPE_SRC_PREFIX, 0x08, 0x0, 0xc0,
FLOW_TYPE_NEXT_HEADER, 0x81, 0x06, FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90, FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
FLOW_TYPE_LABEL, 0x80, 0x55, FLOW_TYPE_LABEL, 0x80, 0x55,
})); );
const byte *p1_dst_px = &f->data[1]; const byte *p1_dst_px = &f->data[1];
const ip6_addr p1_dst_addr = ip6_build(0,0,0x12345678,0x9a000000); const ip6_addr p1_dst_addr = ip6_build(0,0,0x12345678,0x9a000000);
@ -450,17 +462,14 @@ t_builder4(void)
/* Expectation */ /* Expectation */
static byte nlri[] = { const net_addr_flow4 *expect = NET_ADDR_FLOW4_NLRI(
25, 0,
FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7, FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13, FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
FLOW_TYPE_IP_PROTOCOL, 0x80, 0x06, FLOW_TYPE_IP_PROTOCOL, 0x80, 0x06,
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90, FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55 FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
}; );
net_addr_flow4 *expect;
NET_ADDR_FLOW4_(expect, ip4_build(5, 6, 7, 0), 24, nlri);
/* Normal order */ /* Normal order */
@ -531,17 +540,14 @@ t_builder6(void)
/* Expectation */ /* Expectation */
byte nlri[] = { const net_addr_flow6 *expect = NET_ADDR_FLOW6_NLRI(
27, 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_SRC_PREFIX, 8, 0, 0xc0,
FLOW_TYPE_NEXT_HEADER, 0x80, 0x06, FLOW_TYPE_NEXT_HEADER, 0x80, 0x06,
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90, FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
FLOW_TYPE_LABEL, 0x80, 0x55, FLOW_TYPE_LABEL, 0x80, 0x55,
}; );
net_addr_flow6 *expect;
NET_ADDR_FLOW6_(expect, ip6_build(0, 1, 0x12345678, 0x98000000), 103, nlri);
/* Normal order */ /* Normal order */
@ -605,9 +611,11 @@ t_builder6(void)
static int static int
t_formatting4(void) 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, 0,
FLOW_TYPE_DST_PREFIX, 0x08, 10, FLOW_TYPE_DST_PREFIX, 0x08, 10,
FLOW_TYPE_IP_PROTOCOL, 0x81, 23, FLOW_TYPE_IP_PROTOCOL, 0x81, 23,
@ -618,18 +626,44 @@ t_formatting4(void)
FLOW_TYPE_TCP_FLAGS, 0x01, 0x03, 0xc2, 0x0c, FLOW_TYPE_TCP_FLAGS, 0x01, 0x03, 0xc2, 0x0c,
FLOW_TYPE_PACKET_LENGTH, 0x03, 0, 0xd5, 0xff, 0xff, FLOW_TYPE_PACKET_LENGTH, 0x03, 0, 0xd5, 0xff, 0xff,
FLOW_TYPE_DSCP, 0x81, 63, FLOW_TYPE_DSCP, 0x81, 63,
FLOW_TYPE_FRAGMENT, 0x01, 0x01, 0x82, 0x02 FLOW_TYPE_FRAGMENT, 0x01, 0x01, 0x82, 0x02,
}; );
*nlri = (u8) sizeof(nlri);
net_addr_flow4 *input; /* RFC 8955 4.3.1 Example 1 */
NET_ADDR_FLOW4_(input, ip4_build(5, 6, 7, 0), 24, nlri); 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)); /* RFC 8955 4.3.3 Example 3 */
bt_debug(" expect: '%s',\n output: '%s'\n", expect, b); expect[3] = "flow4 { dst 192.0.2.1/32; fragment !0x0/0x5; }";
bt_assert(strcmp(b, expect) == 0); 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; return 1;
} }
@ -637,26 +671,46 @@ t_formatting4(void)
static int static int
t_formatting6(void) 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, 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_SRC_PREFIX, 8, 0, 0xc0,
FLOW_TYPE_NEXT_HEADER, 0x81, 0x06, FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
FLOW_TYPE_PORT, 0x03, 20, 0x45, 40, 0x91, 0x01, 0x11, FLOW_TYPE_PORT, 0x03, 20, 0x45, 40, 0x91, 0x01, 0x11,
FLOW_TYPE_LABEL, 0xa4, 0x00, 0x07, 0xa1, 0x20, FLOW_TYPE_LABEL, 0xa4, 0x00, 0x07, 0xa1, 0x20,
}; );
*nlri = (u8) sizeof(nlri);
net_addr_flow6 *input; /* RFC 8956 3.8.1 Example 1 */
NET_ADDR_FLOW6_(input, ip6_build(0, 1, 0x12345678, 0x98000000), 103, nlri); 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)); /* Run the tests */
bt_debug(" expect: '%s',\n output: '%s'\n", expect, b); for (uint i = 0; i < ARRAY_SIZE(input); i++)
bt_assert(strcmp(b, expect) == 0); {
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; 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. * 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. * 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 * char *
ip6_ntop(ip6_addr a, char *b) 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) static inline ip6_addr ip6_setbits(ip6_addr a, uint pos, uint val)
{ a.addr[pos / 32] |= val << (31 - pos % 32); return a; } { 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) static inline ip4_addr ip4_opposite_m1(ip4_addr a)
{ return _MI4(_I(a) ^ 1); } { return _MI4(_I(a) ^ 1); }
@ -398,7 +401,7 @@ typedef struct mpls_label_stack {
u32 stack[MPLS_MAX_LABEL_STACK]; u32 stack[MPLS_MAX_LABEL_STACK];
} mpls_label_stack; } mpls_label_stack;
static inline int static inline int ACCESS_READ(1, 2)
mpls_get(const char *buf, int buflen, u32 *stack) mpls_get(const char *buf, int buflen, u32 *stack)
{ {
for (int i=0; (i<MPLS_MAX_LABEL_STACK) && (i*4+3 < buflen); i++) 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 * 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 *ip4_ntop(ip4_addr a, char *b);
char *ip6_ntop(ip6_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) static inline char * ip4_ntox(ip4_addr a, char *b)
{ return b + bsprintf(b, "%08x", _I(a)); } { return b + bsprintf(b, "%08x", _I(a)); }

View File

@ -231,6 +231,66 @@ t_ip6_prefix_equal(void)
return 1; 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 int
main(int argc, char *argv[]) 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_ip6_ntop, "Converting ip6_addr struct to IPv6 string");
bt_test_suite(t_ip4_prefix_equal, "Testing ip4_prefix_equal()"); 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_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(); return bt_exit_value();
} }

View File

@ -16,6 +16,7 @@ const char * const net_label[] = {
[NET_FLOW6] = "flow6", [NET_FLOW6] = "flow6",
[NET_IP6_SADR]= "ipv6-sadr", [NET_IP6_SADR]= "ipv6-sadr",
[NET_MPLS] = "mpls", [NET_MPLS] = "mpls",
[NET_ASPA] = "aspa",
}; };
const u16 net_addr_length[] = { const u16 net_addr_length[] = {
@ -29,6 +30,7 @@ const u16 net_addr_length[] = {
[NET_FLOW6] = 0, [NET_FLOW6] = 0,
[NET_IP6_SADR]= sizeof(net_addr_ip6_sadr), [NET_IP6_SADR]= sizeof(net_addr_ip6_sadr),
[NET_MPLS] = sizeof(net_addr_mpls), [NET_MPLS] = sizeof(net_addr_mpls),
[NET_ASPA] = sizeof(net_addr_aspa),
}; };
const u8 net_max_prefix_length[] = { const u8 net_max_prefix_length[] = {
@ -42,6 +44,7 @@ const u8 net_max_prefix_length[] = {
[NET_FLOW6] = IP6_MAX_PREFIX_LENGTH, [NET_FLOW6] = IP6_MAX_PREFIX_LENGTH,
[NET_IP6_SADR]= IP6_MAX_PREFIX_LENGTH, [NET_IP6_SADR]= IP6_MAX_PREFIX_LENGTH,
[NET_MPLS] = 0, [NET_MPLS] = 0,
[NET_ASPA] = 0,
}; };
const u16 net_max_text_length[] = { const u16 net_max_text_length[] = {
@ -55,6 +58,7 @@ const u16 net_max_text_length[] = {
[NET_FLOW6] = 0, /* "flow6 { ... }" */ [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_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_MPLS] = 7, /* "1048575" */
[NET_ASPA] = 10, /* "4294967295" */
}; };
/* There should be no implicit padding in net_addr structures */ /* 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_flow6) == 20);
STATIC_ASSERT(sizeof(net_addr_ip6_sadr) == 40); STATIC_ASSERT(sizeof(net_addr_ip6_sadr) == 40);
STATIC_ASSERT(sizeof(net_addr_mpls) == 8); STATIC_ASSERT(sizeof(net_addr_mpls) == 8);
STATIC_ASSERT(sizeof(net_addr_aspa) == 8);
int int
@ -95,7 +100,7 @@ net_format(const net_addr *N, char *buf, int buflen)
switch (n->n.type) switch (n->n.type)
{ {
case NET_IP4: 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: case NET_IP6:
return bsnprintf(buf, buflen, "%I6/%d", n->ip6.prefix, n->ip6.pxlen); return bsnprintf(buf, buflen, "%I6/%d", n->ip6.prefix, n->ip6.pxlen);
case NET_VPN4: 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); 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: case NET_MPLS:
return bsnprintf(buf, buflen, "%u", n->mpls.label); return bsnprintf(buf, buflen, "%u", n->mpls.label);
case NET_ASPA:
return bsnprintf(buf, buflen, "%u", n->aspa.asn);
} }
bug("unknown network type"); bug("unknown network type");
@ -147,6 +154,7 @@ net_pxmask(const net_addr *a)
return ipa_from_ip6(ip6_mkmask(net6_pxlen(a))); return ipa_from_ip6(ip6_mkmask(net6_pxlen(a)));
case NET_MPLS: case NET_MPLS:
case NET_ASPA:
default: default:
return IPA_NONE; 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); return net_compare_ip6_sadr((const net_addr_ip6_sadr *) a, (const net_addr_ip6_sadr *) b);
case NET_MPLS: case NET_MPLS:
return net_compare_mpls((const net_addr_mpls *) a, (const net_addr_mpls *) b); 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; return 0;
} }
@ -201,6 +211,7 @@ net_hash(const net_addr *n)
case NET_FLOW6: return NET_HASH(n, flow6); case NET_FLOW6: return NET_HASH(n, flow6);
case NET_IP6_SADR: return NET_HASH(n, ip6_sadr); case NET_IP6_SADR: return NET_HASH(n, ip6_sadr);
case NET_MPLS: return NET_HASH(n, mpls); case NET_MPLS: return NET_HASH(n, mpls);
case NET_ASPA: return NET_HASH(n, aspa);
default: bug("invalid type"); default: bug("invalid type");
} }
} }
@ -223,6 +234,7 @@ net_validate(const net_addr *n)
case NET_FLOW6: return NET_VALIDATE(n, flow6); case NET_FLOW6: return NET_VALIDATE(n, flow6);
case NET_IP6_SADR: return NET_VALIDATE(n, ip6_sadr); case NET_IP6_SADR: return NET_VALIDATE(n, ip6_sadr);
case NET_MPLS: return NET_VALIDATE(n, mpls); case NET_MPLS: return NET_VALIDATE(n, mpls);
case NET_ASPA: return NET_VALIDATE(n, aspa);
default: return 0; default: return 0;
} }
} }
@ -250,6 +262,7 @@ net_normalize(net_addr *N)
return net_normalize_ip6_sadr(&n->ip6_sadr); return net_normalize_ip6_sadr(&n->ip6_sadr);
case NET_MPLS: case NET_MPLS:
case NET_ASPA:
return; 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); return ip6_zero(n->ip6_sadr.dst_prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->ip6_sadr.dst_prefix);
case NET_MPLS: case NET_MPLS:
case NET_ASPA:
return IADDR_HOST | SCOPE_UNIVERSE; 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)))); ip6_mkmask(net6_pxlen(n))));
case NET_MPLS: case NET_MPLS:
case NET_ASPA:
default: default:
return 0; return 0;
} }

View File

@ -23,7 +23,8 @@
#define NET_FLOW6 8 #define NET_FLOW6 8
#define NET_IP6_SADR 9 #define NET_IP6_SADR 9
#define NET_MPLS 10 #define NET_MPLS 10
#define NET_MAX 11 #define NET_ASPA 11
#define NET_MAX 12
#define NB_IP4 (1 << NET_IP4) #define NB_IP4 (1 << NET_IP4)
#define NB_IP6 (1 << NET_IP6) #define NB_IP6 (1 << NET_IP6)
@ -35,6 +36,7 @@
#define NB_FLOW6 (1 << NET_FLOW6) #define NB_FLOW6 (1 << NET_FLOW6)
#define NB_IP6_SADR (1 << NET_IP6_SADR) #define NB_IP6_SADR (1 << NET_IP6_SADR)
#define NB_MPLS (1 << NET_MPLS) #define NB_MPLS (1 << NET_MPLS)
#define NB_ASPA (1 << NET_ASPA)
#define NB_IP (NB_IP4 | NB_IP6) #define NB_IP (NB_IP4 | NB_IP6)
#define NB_VPN (NB_VPN4 | NB_VPN6) #define NB_VPN (NB_VPN4 | NB_VPN6)
@ -124,6 +126,13 @@ typedef struct net_addr_mpls {
u32 label; u32 label;
} net_addr_mpls; } 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 { typedef struct net_addr_ip6_sadr {
u8 type; u8 type;
u8 dst_pxlen; u8 dst_pxlen;
@ -145,6 +154,7 @@ typedef union net_addr_union {
net_addr_flow6 flow6; net_addr_flow6 flow6;
net_addr_ip6_sadr ip6_sadr; net_addr_ip6_sadr ip6_sadr;
net_addr_mpls mpls; net_addr_mpls mpls;
net_addr_aspa aspa;
} net_addr_union; } 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) \ #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 }) ((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) \ #define NET_ADDR_MPLS(label) \
((net_addr_mpls) { NET_MPLS, 20, sizeof(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) static inline void net_fill_mpls(net_addr *a, u32 label)
{ *(net_addr_mpls *)a = NET_ADDR_MPLS(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) static inline void net_fill_ipa(net_addr *a, ip_addr prefix, uint pxlen)
{ {
if (ipa_is_ip4(prefix)) 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) static inline int net_is_flow(const net_addr *a)
{ return (a->type == NET_FLOW4) || (a->type == NET_FLOW6); } { 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) static inline int net_is_sadr(const net_addr *a)
{ return (a->type == NET_IP6_SADR); } { 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)); return ipa_from_ip6(net6_prefix(a));
case NET_MPLS: case NET_MPLS:
case NET_ASPA:
default: default:
return IPA_NONE; 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) static inline int net_equal_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
{ return !memcmp(a, b, sizeof(net_addr_mpls)); } { 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) 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); } { 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) static inline int net_zero_mpls(const net_addr_mpls *a)
{ return !a->label; } { 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) 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); } { 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) static inline int net_compare_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
{ return uint_cmp(a->label, b->label); } { 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); 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) static inline void net_copy_mpls(net_addr_mpls *dst, const net_addr_mpls *src)
{ memcpy(dst, src, sizeof(net_addr_mpls)); } { 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) static inline u32 px4_hash(ip4_addr prefix, u32 pxlen)
{ return ip4_hash(prefix) ^ (pxlen << 26); } { 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) static inline u32 net_hash_mpls(const net_addr_mpls *n)
{ return u32_hash(n->label); } { 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); 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) static inline int net_validate_mpls(const net_addr_mpls *n)
{ return n->label < (1 << 20); } { 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) 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); } { 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_classify(const net_addr *N);
int net_format(const net_addr *N, 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); 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) 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))); } { 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; i = 0;
if (num == 0) if (num == 0)
tmp[i++]='0'; tmp[i++]='0';
else while (num != 0) { else if (base == 10) {
uint res = num % base; /* Separate cases to have fixed divisors */
num = num / base; while (num != 0) {
tmp[i++] = digits[res]; 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) if (i > precision)
precision = i; precision = i;

View File

@ -81,9 +81,9 @@ static inline pool *resource_parent(resource *r)
/* Normal memory blocks */ /* Normal memory blocks */
void *mb_alloc(pool *, unsigned size); void *mb_alloc(pool *, unsigned size) ALLOC_SIZE(2);
void *mb_allocz(pool *, unsigned size); void *mb_allocz(pool *, unsigned size) ALLOC_SIZE(2);
void *mb_realloc(void *m, unsigned size); void *mb_realloc(void *m, unsigned size) ALLOC_SIZE(2);
void mb_free(void *); void mb_free(void *);
/* Memory pools with linear allocation */ /* Memory pools with linear allocation */
@ -97,9 +97,9 @@ typedef struct lp_state {
} lp_state; } lp_state;
linpool *lp_new(pool *); linpool *lp_new(pool *);
void *lp_alloc(linpool *, unsigned size); /* Aligned */ void *lp_alloc(linpool *, unsigned size) ALLOC_SIZE(2); /* Aligned */
void *lp_allocu(linpool *, unsigned size); /* Unaligned */ void *lp_allocu(linpool *, unsigned size) ALLOC_SIZE(2); /* Unaligned */
void *lp_allocz(linpool *, unsigned size); /* With clear */ void *lp_allocz(linpool *, unsigned size) ALLOC_SIZE(2); /* With clear */
void lp_flush(linpool *); /* Free everything, but leave linpool */ void lp_flush(linpool *); /* Free everything, but leave linpool */
lp_state *lp_save(linpool *m); /* Save state */ lp_state *lp_save(linpool *m); /* Save state */
void lp_restore(linpool *m, lp_state *p); /* Restore 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 (*format)(const eattr *ea, byte *buf, uint size); \
void (*stored)(const eattr *ea); /* When stored into global hash */ \ void (*stored)(const eattr *ea); /* When stored into global hash */ \
void (*freed)(const eattr *ea); /* When released from 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; 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)); 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); void rta_init(void);
ea_list *ea_lookup_slow(ea_list *r, u32 squash_upto, enum ea_stored oid); 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_HDRINCL 0x400 /* Used internally */
#define SKF_PKTINFO 0x800 /* 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) * 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 bsprintf(char *str, const char *fmt, ...);
int bvsprintf(char *str, const char *fmt, va_list args); int bvsprintf(char *str, const char *fmt, va_list args);
int bsnprintf(char *str, int size, const char *fmt, ...); int bsnprintf(char *str, int size, const char *fmt, ...) ACCESS_WRITE(1, 2);
int bvsnprintf(char *str, int size, const char *fmt, va_list args); 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_sprintf(pool *p, const char *fmt, ...);
char *mb_vsprintf(pool *p, const char *fmt, va_list args); 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); byte bstrtobyte16(const char *str);
int bstrhextobin(const char *s, byte *b); 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); int patmatch(const byte *pat, const byte *str);

View File

@ -68,9 +68,9 @@ tm_dump(resource *r, unsigned indent UNUSED)
if (t->randomize) if (t->randomize)
debug("rand %d, ", t->randomize); debug("rand %d, ", t->randomize);
if (t->recurrent) if (t->recurrent)
debug("recur %d, ", t->recurrent); debug("recur %ld, ", t->recurrent);
if (t->expires) 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 else
debug("inactive)\n"); debug("inactive)\n");
} }

View File

@ -28,8 +28,8 @@ typedef struct timer
void *data; void *data;
btime expires; /* 0=inactive */ btime expires; /* 0=inactive */
btime recurrent; /* Timer recurrence */
uint randomize; /* Amount of randomization */ uint randomize; /* Amount of randomization */
uint recurrent; /* Timer recurrence */
struct timeloop *loop; /* Loop where the timer is active */ struct timeloop *loop; /* Loop where the timer is active */
@ -84,7 +84,7 @@ tm_remains(timer *t)
} }
static inline timer * 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); timer *t = tm_new(p);
t->hook = hook; 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_HEAD
#undef TLIST_WANT_ADD_TAIL #undef TLIST_WANT_ADD_TAIL
#undef TLIST_WANT_UPDATE_NODE #undef TLIST_WANT_UPDATE_NODE
#undef TLIST_DEFINED_BEFORE
# endif # endif
#else #else

View File

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

View File

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

View File

@ -272,7 +272,7 @@ cli_event(void *data)
} }
cli * 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"); pool *p = rp_new(cli_pool, the_bird_domain.the_bird, "CLI");
cli *c = mb_alloc(p, sizeof(cli)); cli *c = mb_alloc(p, sizeof(cli));
@ -286,6 +286,10 @@ cli_new(struct birdsock *sock)
c->cont = cli_hello; c->cont = cli_hello;
c->parser_pool = lp_new_default(c->pool); c->parser_pool = lp_new_default(c->pool);
c->rx_buf = mb_alloc(c->pool, CLI_RX_BUF_SIZE); c->rx_buf = mb_alloc(c->pool, CLI_RX_BUF_SIZE);
if (cf->restricted)
c->restricted = 1;
ev_schedule(c->event); ev_schedule(c->event);
return c; return c;
} }
@ -300,6 +304,36 @@ cli_kick(cli *c)
static list cli_log_hooks; static list cli_log_hooks;
static int cli_log_inited; 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 */ /* Hack for scheduled undo notification */
extern cli *cmd_reconfig_stored_cli; extern cli *cmd_reconfig_stored_cli;

View File

@ -12,6 +12,9 @@
#include "lib/resource.h" #include "lib/resource.h"
#include "lib/lists.h" #include "lib/lists.h"
#include "lib/event.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_RX_BUF_SIZE 4096
#define CLI_TX_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 */ struct config *main_config; /* Main config currently in use */
int last_reply; int last_reply;
int restricted; /* CLI is restricted to read-only commands */ int restricted; /* CLI is restricted to read-only commands */
struct timeformat *tf; /* Time format override */
struct linpool *parser_pool; /* Pool used during parsing */ struct linpool *parser_pool; /* Pool used during parsing */
uint log_mask; /* Mask of allowed message levels */ uint log_mask; /* Mask of allowed message levels */
uint log_threshold; /* When free < log_threshold, store only important messages */ uint log_threshold; /* When free < log_threshold, store only important messages */
uint async_msg_size; /* Total size of async messages queued in tx_buf */ uint async_msg_size; /* Total size of async messages queued in tx_buf */
} cli; } 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 pool *cli_pool;
extern struct cli *this_cli; /* Used during parsing */ 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 *, ...); void cli_printf(cli *, int, char *, ...);
#define cli_msg(x...) cli_printf(this_cli, x) #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) static inline void cli_separator(cli *c)
{ if (c->last_reply) cli_printf(c, -c->last_reply, ""); }; { if (c->last_reply) cli_printf(c, -c->last_reply, ""); };
/* Functions provided to sysdep layer */ /* Functions provided to sysdep layer */
cli *cli_new(struct birdsock *); cli *cli_new(struct birdsock *, struct cli_config *);
void cli_init(void); void cli_init(void);
void cli_free(cli *); void cli_free(cli *);
void cli_kick(cli *); void cli_kick(cli *);

View File

@ -22,11 +22,10 @@ extern int configuring;
void void
cmd_show_status(void) cmd_show_status(void)
{ {
byte tim[TM_DATETIME_BUFFER_SIZE];
rcu_read_lock(); rcu_read_lock();
struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_acquire); 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); cli_msg(-1000, "BIRD " BIRD_VERSION);
tm_format_time(tim, tf, current_time()); 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(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(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(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(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) 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(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(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, AS) CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, AS)
CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
CF_KEYWORDS(CHECK, LINK) CF_KEYWORDS(CHECK, LINK)
CF_KEYWORDS(CORK, SORTED, TRIE, MIN, MAX, ROA, DIGEST, ROUTE, REFRESH, SETTLE, TIME, GC, THRESHOLD, PERIOD) 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(MPLS_LABEL, MPLS_POLICY, MPLS_CLASS)
CF_KEYWORDS(ASPA_PROVIDERS)
/* For r_args_channel */ /* For r_args_channel */
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC) CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC, 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, 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, RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL, RPKI, L3VPN,
AGGREGATED) AGGREGATED)
CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED) CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT) CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID) 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_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE, VRF) 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 <ps> proto_patt proto_patt2
%type <cc> channel_start proto_channel %type <cc> channel_start proto_channel
%type <cl> limit_spec %type <cl> limit_spec
%type <tf> timeformat_spec
%type <tfp> timeformat_which
%type <net> r_args_for_val %type <net> r_args_for_val
%type <net_ptr> r_args_for %type <net_ptr> r_args_for
%type <t> channel_sym %type <t> channel_sym
@ -241,6 +245,7 @@ net_type_base:
| ROA6 { $$ = NET_ROA6; } | ROA6 { $$ = NET_ROA6; }
| FLOW4{ $$ = NET_FLOW4; } | FLOW4{ $$ = NET_FLOW4; }
| FLOW6{ $$ = NET_FLOW6; } | FLOW6{ $$ = NET_FLOW6; }
| ASPA { $$ = NET_ASPA; }
; ;
net_type: net_type:
@ -248,8 +253,6 @@ net_type:
| MPLS { $$ = NET_MPLS; } | MPLS { $$ = NET_MPLS; }
; ;
CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP6_SADR, MPLS)
/* Creation of routing tables */ /* Creation of routing tables */
@ -452,6 +455,10 @@ debug_default:
conf: timeformat_base ; conf: timeformat_base ;
timeformat_base:
TIMEFORMAT timeformat_which timeformat_spec ';' { *$2 = $3; }
;
timeformat_which: timeformat_which:
ROUTE { $$ = &new_config->tf_route; } ROUTE { $$ = &new_config->tf_route; }
| PROTOCOL { $$ = &new_config->tf_proto; } | PROTOCOL { $$ = &new_config->tf_proto; }
@ -460,18 +467,14 @@ timeformat_which:
; ;
timeformat_spec: timeformat_spec:
timeformat_which TEXT { *$1 = (struct timeformat){$2, NULL, 0}; } TEXT { $$ = (struct timeformat){$1, NULL, 0}; }
| timeformat_which TEXT expr TEXT { *$1 = (struct timeformat){$2, $4, (s64) $3 S_}; } | TEXT expr TEXT { $$ = (struct timeformat){$1, $3, (s64) $2 S_}; }
| timeformat_which ISO SHORT { *$1 = TM_ISO_SHORT_S; } | ISO SHORT { $$ = TM_ISO_SHORT_S; }
| timeformat_which ISO SHORT MS { *$1 = TM_ISO_SHORT_MS; } | ISO SHORT MS { $$ = TM_ISO_SHORT_MS; }
| timeformat_which ISO SHORT US { *$1 = TM_ISO_SHORT_US; } | ISO SHORT US { $$ = TM_ISO_SHORT_US; }
| timeformat_which ISO LONG { *$1 = TM_ISO_LONG_S; } | ISO LONG { $$ = TM_ISO_LONG_S; }
| timeformat_which ISO LONG MS { *$1 = TM_ISO_LONG_MS; } | ISO LONG MS { $$ = TM_ISO_LONG_MS; }
| timeformat_which ISO LONG US { *$1 = TM_ISO_LONG_US; } | ISO LONG US { $$ = TM_ISO_LONG_US; }
;
timeformat_base:
TIMEFORMAT timeformat_spec ';'
; ;
/* Interface patterns */ /* 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 */ /* Core commands */
CF_CLI_HELP(SHOW, ..., [[Show status information]]) CF_CLI_HELP(SHOW, ..., [[Show status information]])
@ -908,6 +894,7 @@ channel_sym:
| FLOW4 { $$ = "flow4"; } | FLOW4 { $$ = "flow4"; }
| FLOW6 { $$ = "flow6"; } | FLOW6 { $$ = "flow6"; }
| MPLS { $$ = "mpls"; } | MPLS { $$ = "mpls"; }
| ASPA { $$ = "aspa"; }
| PRI { $$ = "pri"; } | PRI { $$ = "pri"; }
| SEC { $$ = "sec"; } | 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]]) CF_CLI(RESTRICT,,,[[Restrict current CLI session to safe commands]])
{ this_cli->restricted = 1; cli_msg(16, "Access restricted"); } ; { 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: proto_patt:
CF_SYM_KNOWN { cf_assert_symbol($1, SYM_PROTO); $$.ptr = $1; $$.patt = 0; } CF_SYM_KNOWN { cf_assert_symbol($1, SYM_PROTO); $$.ptr = $1; $$.patt = 0; }
| ALL { $$.ptr = NULL; $$.patt = 1; } | 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; struct ifa *addr = NULL;
WALK_LIST(n, neigh_hash_table[h]) /* Search the cache */ 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; IFACE_UNLOCK;
return n; return n;

View File

@ -613,6 +613,11 @@ channel_roa_subscribe_filter(struct channel *c, int dir)
found = 1; found = 1;
break; 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: default:
break; break;
} }
@ -2610,7 +2615,7 @@ proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt)
p->proto->get_status(p, buf); p->proto->get_status(p, buf);
rcu_read_lock(); 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(); rcu_read_unlock();
cli_msg(-1002, "%-10s %-10s %-10s %-6s %-12s %s", cli_msg(-1002, "%-10s %-10s %-10s %-6s %-12s %s",
p->name, p->name,

View File

@ -33,6 +33,7 @@
#include <stdatomic.h> #include <stdatomic.h>
struct ea_list; struct ea_list;
struct adata;
struct protocol; struct protocol;
struct proto; struct proto;
struct channel; struct channel;
@ -928,6 +929,13 @@ void ea_show_nexthop_list(struct cli *c, struct nexthop_adata *nhad);
#define ROA_VALID 1 #define ROA_VALID 1
#define ROA_INVALID 2 #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); 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 #endif

View File

@ -188,6 +188,18 @@ const char * flowspec_valid_names[FLOWSPEC__MAX] = {
[FLOWSPEC_INVALID] = "invalid", [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; DOMAIN(attrs) attrs_domain;
pool *rta_pool; pool *rta_pool;
@ -1454,7 +1466,7 @@ ea_show(struct cli *c, const eattr *e)
as_path_format(ad, pos, end - pos); as_path_format(ad, pos, end - pos);
break; break;
case T_CLIST: 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; return;
case T_ECLIST: case T_ECLIST:
ea_show_ec_set(c, cls->name, ad, buf); 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_policy);
ea_register_init(&ea_gen_mpls_class); ea_register_init(&ea_gen_mpls_class);
ea_register_init(&ea_gen_mpls_label); 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_FLOW6: return FIB_FIND(f, a, flow6);
case NET_IP6_SADR: return FIB_FIND(f, a, ip6_sadr); case NET_IP6_SADR: return FIB_FIND(f, a, ip6_sadr);
case NET_MPLS: return FIB_FIND(f, a, mpls); case NET_MPLS: return FIB_FIND(f, a, mpls);
case NET_ASPA: return FIB_FIND(f, a, aspa);
default: bug("invalid type"); 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_FLOW6: FIB_INSERT(f, a, e, flow6); return;
case NET_IP6_SADR: FIB_INSERT(f, a, e, ip6_sadr); 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_MPLS: FIB_INSERT(f, a, e, mpls); return;
case NET_ASPA: FIB_INSERT(f, a, e, aspa); return;
default: bug("invalid type"); 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 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; 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); 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))) if (ipa_nonzero(a_from) && (!nhad || !ipa_equal(a_from, nhad->nh.gw)))
bsprintf(from, " from %I", a_from); 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 #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 * struct rte_storage *
rte_store(const rte *r, struct netindex *i, struct rtable_private *tab) 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, .idle_tx_int = opts->idle_tx_int ?: cf->idle_tx_int,
.multiplier = opts->multiplier ?: cf->multiplier, .multiplier = opts->multiplier ?: cf->multiplier,
.passive = opts->passive_set ? opts->passive : cf->passive, .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_ipv6 != old->accept_ipv6) ||
(new->accept_direct != old->accept_direct) || (new->accept_direct != old->accept_direct) ||
(new->accept_multihop != old->accept_multihop) || (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; return 0;
birdloop_mask_wakeups(p->p.loop); 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 : "---"; 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 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; 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; loc_state = (loc_state < 4) ? loc_state : 0;
rem_state = (rem_state < 4) ? rem_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(); rcu_read_lock();
struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_relaxed); 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(); rcu_read_unlock();
if (!details) if (!details)

View File

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

View File

@ -24,7 +24,7 @@ CF_DECLS
CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE, CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE,
INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL, AUTHENTICATION, INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL, AUTHENTICATION,
NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1, IPV4, IPV6, DIRECT, NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1, IPV4, IPV6, DIRECT,
STRICT, BIND) STRICT, BIND, ZERO, UDP6, CHECKSUM, RX)
%type <iface> bfd_neigh_iface %type <iface> bfd_neigh_iface
%type <a> bfd_neigh_local %type <a> bfd_neigh_local
@ -53,6 +53,7 @@ bfd_proto_item:
| MULTIHOP bfd_multihop | MULTIHOP bfd_multihop
| NEIGHBOR bfd_neighbor | NEIGHBOR bfd_neighbor
| STRICT BIND bool { BFD_CFG->strict_bind = $3; } | STRICT BIND bool { BFD_CFG->strict_bind = $3; }
| ZERO UDP6 CHECKSUM RX bool { BFD_CFG->zero_udp6_checksum_rx = $5; }
; ;
bfd_proto_opts: 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, ..., [[Show information about BFD protocol]]);
CF_CLI_HELP(SHOW BFD SESSIONS, ..., [[Show information about BFD sessions]]); 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 static void
bfd_fill_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt) 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); struct password_item *pass = password_find(cf->passwords, 0);
uint meticulous = 0; uint meticulous = 0;
@ -179,7 +179,7 @@ bfd_fill_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_c
static int static int
bfd_check_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt) 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; const char *err_dsc = NULL;
uint err_val = 0; uint err_val = 0;
uint auth_type = 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) else if (s->poll_active)
pkt->flags |= BFD_FLAG_POLL; pkt->flags |= BFD_FLAG_POLL;
if (s->ifa->cf->auth_type) if (s->cf.auth_type)
bfd_fill_authentication(p, s, pkt); bfd_fill_authentication(p, s, pkt);
if (sk->tbuf != sk->tpos) if (sk->tbuf != sk->tpos)
@ -416,6 +416,8 @@ bfd_err_hook(sock *sk, int err)
sock * sock *
bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af) 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); sock *sk = sk_new(p->p.pool);
sk->type = SK_UDP; sk->type = SK_UDP;
sk->subtype = af; 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->priority = sk_priority_control;
sk->flags = SKF_LADDR_RX | (!multihop ? SKF_TTL_RX : 0); 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) if (sk_open(sk, p->p.loop) < 0)
goto err; goto err;
@ -446,6 +451,8 @@ err:
sock * sock *
bfd_open_rx_sk_bound(struct bfd_proto *p, ip_addr local, struct iface *ifa) 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); sock *sk = sk_new(p->tpool);
sk->type = SK_UDP; sk->type = SK_UDP;
sk->saddr = local; 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->priority = sk_priority_control;
sk->flags = SKF_BIND | (ifa ? SKF_TTL_RX : 0); 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) if (sk_open(sk, p->p.loop) < 0)
goto err; goto err;

View File

@ -729,7 +729,7 @@ static void
bgp_format_cluster_list(const eattr *a, byte *buf, uint size) bgp_format_cluster_list(const eattr *a, byte *buf, uint size)
{ {
/* Truncates cluster lists larger than buflen, probably not a problem */ /* 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 9072 - Extended Optional Parameters Length for BGP OPEN Message
* RFC 9117 - Revised Validation Procedure for BGP Flow Specifications * RFC 9117 - Revised Validation Procedure for BGP Flow Specifications
* RFC 9234 - Route Leak Prevention and Detection Using Roles * RFC 9234 - Route Leak Prevention and Detection Using Roles
* RFC 9687 - Send Hold Timer
* draft-uttaro-idr-bgp-persistence-04 * draft-uttaro-idr-bgp-persistence-04
* draft-walton-bgp-hostname-capability-02 * 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, FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER,
RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND) RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND)
CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
%type <i> bgp_nh %type <i> bgp_nh
%type <i32> bgp_afi %type <i32> bgp_afi
CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER, CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION, CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION,
OUT, OF, RESOURCES) OUT, OF, RESOURCES, ASPA_CHECK_UPSTREAM, ASPA_CHECK_DOWNSTREAM)
%type<i> bgp_cease_mask bgp_cease_list bgp_cease_flag bgp_role_name %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_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]]) CF_CLI(RELOAD BGP, proto_patt, [<name>], [[Send and request route refresh to/from neighbor]])
{ {
proto_apply_cmd($3, bgp_reload_in, 1, 0); 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); 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_CODE
CF_END CF_END

View File

@ -3342,7 +3342,7 @@ static struct {
{ 6, 8, "Out of Resources" }, { 6, 8, "Out of Resources" },
{ 7, 0, "Invalid ROUTE-REFRESH message" }, /* [RFC7313] */ { 7, 0, "Invalid ROUTE-REFRESH message" }, /* [RFC7313] */
{ 7, 1, "Invalid ROUTE-REFRESH message length" }, /* [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) */ u32 last_vlink_id; /* Interface IDs for vlinks (starts at 0x80000000) */
struct tbf log_pkt_tbf; /* TBF for packet messages */ struct tbf log_pkt_tbf; /* TBF for packet messages */
struct tbf log_lsa_tbf; /* TBF for LSA 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 struct ospf_area
@ -331,6 +332,7 @@ struct ospf_iface
struct top_hash_entry **flood_queue; /* LSAs queued for LSUPD */ struct top_hash_entry **flood_queue; /* LSAs queued for LSUPD */
u8 update_link_lsa; u8 update_link_lsa;
u8 update_net_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_used; /* The current number of LSAs in flood_queue */
u16 flood_queue_size; /* The maximum 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 */ int fadj; /* Number of fully adjacent neighbors */

View File

@ -2029,6 +2029,14 @@ again1:
{ {
neighbor *nbr = neigh_find(&p->p, nh->gw, nh->iface, neighbor *nbr = neigh_find(&p->p, nh->gw, nh->iface,
(nh->flags & RNF_ONLINK) ? NEF_ONLINK : 0); (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)) if (!nbr || (nbr->scope == SCOPE_HOST))
{ reset_ri(nf); break; } { 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); 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 static void
prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa) 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++; i++;
} }
if (ospf_is_ip4(p) && ipa_zero(nh))
{
nh = p->loopback_addr;
ifa->loopback_addr_used = 1;
}
/* Filling the preallocated header */ /* Filling the preallocated header */
struct ospf_lsa_link *ll = p->lsab; struct ospf_lsa_link *ll = p->lsab;
ll->options = ifa->oa->options | (ifa->priority << 24); 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) WALK_LIST(ifa, p->iface_list)
{ {
if (ifa->type == OSPF_IT_VLINK) if (ifa->type == OSPF_IT_VLINK)
@ -1839,6 +1888,8 @@ ospf_update_topology(struct ospf_proto *p)
if (ifa->update_link_lsa) if (ifa->update_link_lsa)
{ {
ifa->loopback_addr_used = 0;
if ((ifa->state > OSPF_IS_LOOP) && !ifa->link_lsa_suppression) if ((ifa->state > OSPF_IS_LOOP) && !ifa->link_lsa_suppression)
ospf_originate_link_lsa(p, ifa); ospf_originate_link_lsa(p, ifa);
else else

View File

@ -13,6 +13,7 @@ CF_HDR
CF_DEFINES CF_DEFINES
#define RPKI_CFG ((struct rpki_config *) this_proto) #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) #define RPKI_TR_SSH_CFG ((struct rpki_tr_ssh_config *) RPKI_CFG->tr_config.spec)
static void static void
@ -32,7 +33,8 @@ rpki_check_unused_transport(void)
CF_DECLS CF_DECLS
CF_KEYWORDS(RPKI, REMOTE, BIRD, PRIVATE, PUBLIC, KEY, TCP, SSH, TRANSPORT, USER, 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 %type <i> rpki_keep_interval
@ -46,6 +48,8 @@ rpki_proto_start: proto_start RPKI {
RPKI_CFG->retry_interval = RPKI_RETRY_INTERVAL; RPKI_CFG->retry_interval = RPKI_RETRY_INTERVAL;
RPKI_CFG->refresh_interval = RPKI_REFRESH_INTERVAL; RPKI_CFG->refresh_interval = RPKI_REFRESH_INTERVAL;
RPKI_CFG->expire_interval = RPKI_EXPIRE_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); }; 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; RPKI_CFG->keep_expire_interval = $2;
} }
| IGNORE MAX LENGTH bool { RPKI_CFG->ignore_max_length = $4; } | 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: rpki_keep_interval:
@ -109,7 +121,7 @@ rpki_cache_addr: text_or_ipa
}; };
rpki_transport: 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 | 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_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: rpki_transport_ssh_init:
{ {
#if HAVE_LIBSSH #if HAVE_LIBSSH

View File

@ -62,6 +62,7 @@ enum pdu_type {
CACHE_RESET = 8, CACHE_RESET = 8,
ROUTER_KEY = 9, ROUTER_KEY = 9,
ERROR = 10, ERROR = 10,
ASPA = 11,
PDU_TYPE_MAX PDU_TYPE_MAX
}; };
@ -76,7 +77,8 @@ static const char *str_pdu_type_[] = {
[END_OF_DATA] = "End of Data", [END_OF_DATA] = "End of Data",
[CACHE_RESET] = "Cache Reset", [CACHE_RESET] = "Cache Reset",
[ROUTER_KEY] = "Router Key", [ROUTER_KEY] = "Router Key",
[ERROR] = "Error" [ERROR] = "Error",
[ASPA] = "ASPA",
}; };
static const char *str_pdu_type(uint type) { static const char *str_pdu_type(uint type) {
@ -193,6 +195,35 @@ struct pdu_error {
* Error Diagnostic Message */ * Error Diagnostic Message */
} PACKED; } 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 { struct pdu_reset_query {
u8 ver; u8 ver;
u8 type; u8 type;
@ -230,9 +261,13 @@ static const size_t min_pdu_size[] = {
[END_OF_DATA] = sizeof(struct pdu_end_of_data_v0), [END_OF_DATA] = sizeof(struct pdu_end_of_data_v0),
[CACHE_RESET] = sizeof(struct pdu_cache_response), [CACHE_RESET] = sizeof(struct pdu_cache_response),
[ROUTER_KEY] = sizeof(struct pdu_header), /* FIXME */ [ROUTER_KEY] = sizeof(struct pdu_header), /* FIXME */
[ASPA] = sizeof(struct pdu_aspa),
[ERROR] = 16, [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, ...); 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...) ({ \ #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 static void
rpki_pdu_to_host_byte_order(struct pdu_header *pdu) 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); pdu->len = ntohl(pdu->len);
switch (pdu->type) switch (pdu->type)
{ {
case SERIAL_NOTIFY: case SERIAL_NOTIFY:
{ {
/* Note that a session_id is converted using converting header->reserved */
struct pdu_serial_notify *sn_pdu = (void *) pdu; 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); sn_pdu->serial_num = ntohl(sn_pdu->serial_num);
break; break;
} }
case END_OF_DATA: case END_OF_DATA:
{ {
/* Note that a session_id is converted using converting header->reserved */
struct pdu_end_of_data_v0 *eod0 = (void *) pdu; 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 */ 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; struct pdu_end_of_data_v1 *eod1 = (void *) pdu;
eod1->expire_interval = ntohl(eod1->expire_interval); eod1->expire_interval = ntohl(eod1->expire_interval);
@ -326,14 +357,29 @@ rpki_pdu_to_host_byte_order(struct pdu_header *pdu)
case ERROR: case ERROR:
{ {
/* Note that a error_code is converted using converting header->reserved */
struct pdu_error *err = (void *) pdu; struct pdu_error *err = (void *) pdu;
err->error_code = ntohs(err->error_code);
err->len_enc_pdu = ntohl(err->len_enc_pdu); err->len_enc_pdu = ntohl(err->len_enc_pdu);
u32 *err_text_len = (u32 *)(err->rest + err->len_enc_pdu); u32 *err_text_len = (u32 *)(err->rest + err->len_enc_pdu);
*err_text_len = htonl(*err_text_len); *err_text_len = htonl(*err_text_len);
break; 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: case ROUTER_KEY:
/* Router Key PDU is not supported yet */ /* 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. */ * We don't care here. */
case CACHE_RESPONSE: case CACHE_RESPONSE:
{
struct pdu_cache_response *cr = (void *) pdu;
cr->session_id = ntohs(cr->session_id);
break;
}
case CACHE_RESET: case CACHE_RESET:
/* Converted with pdu->reserved */ /* Nothing to convert */
break; break;
} }
} }
@ -359,6 +411,9 @@ rpki_pdu_to_host_byte_order(struct pdu_header *pdu)
static struct pdu_header * static struct pdu_header *
rpki_pdu_back_to_network_byte_order(struct pdu_header *out, const struct pdu_header *in) 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); memcpy(out, in, in->len);
rpki_pdu_to_host_byte_order(out); rpki_pdu_to_host_byte_order(out);
return 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: case END_OF_DATA:
{ {
const struct pdu_end_of_data_v1 *eod = (void *) pdu; 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)); 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 else
SAVE(bsnprintf(detail, sizeof(detail), "(session id: %u, serial number: %u)", eod->session_id, eod->serial_num)); 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; 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: default:
*detail = '\0'; *detail = '\0';
} }
@ -561,7 +635,9 @@ rpki_check_receive_packet(struct rpki_cache *cache, const struct pdu_header *pdu
} }
else if (!cache->last_update && else if (!cache->last_update &&
(pdu->ver <= RPKI_MAX_VERSION) && (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_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; 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); rpki_send_error_pdu(cache, UNSUPPORTED_PDU_TYPE, pdu_len, pdu, "Unsupported PDU type %u received", pdu->type);
return RPKI_ERROR; 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; return RPKI_ERROR;
} }
@ -611,7 +693,8 @@ rpki_handle_error_pdu(struct rpki_cache *cache, const struct pdu_error *pdu)
case UNSUPPORTED_PROTOCOL_VER: case UNSUPPORTED_PROTOCOL_VER:
CACHE_TRACE(D_PACKETS, cache, "Client uses unsupported protocol version"); CACHE_TRACE(D_PACKETS, cache, "Client uses unsupported protocol version");
if (pdu->ver <= RPKI_MAX_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_TRACE(D_EVENTS, cache, "Downgrading from protocol version %d to version %d", cache->version, pdu->ver);
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; 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 static uint
rpki_check_interval(struct rpki_cache *cache, const char *(check_fn)(uint), uint interval) 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; 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)) if (!cf->keep_refresh_interval && rpki_check_interval(cache, rpki_check_refresh_interval, pdu->refresh_interval))
cache->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 */ /* TODO: Implement Router Key PDU handling */
break; break;
case ASPA:
rpki_handle_aspa_pdu(cache, (void *) pdu);
break;
default: default:
CACHE_TRACE(D_PACKETS, cache, "Received unsupported type (%u)", pdu->type); CACHE_TRACE(D_PACKETS, cache, "Received unsupported type (%u)", pdu->type);
}; };

View File

@ -23,8 +23,10 @@
* +4 bytes (Length of inserted text) * +4 bytes (Length of inserted text)
* +800 bytes (UTF-8 text 400*2 bytes) * +800 bytes (UTF-8 text 400*2 bytes)
* ------------ * ------------
* = 848 bytes (Maximal expected PDU size) */ * = 848 bytes (Maximal expected PDU size)
#define RPKI_PDU_MAX_LEN 848 *
* 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 */ /* RX buffer size has a great impact to scheduler granularity */
#define RPKI_RX_BUFFER_SIZE 4096 #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); 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 void
rpki_start_refresh(struct rpki_proto *p) 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); rt_refresh_begin(&p->roa4_channel->in_req);
if (p->roa6_channel) if (p->roa6_channel)
rt_refresh_begin(&p->roa6_channel->in_req); rt_refresh_begin(&p->roa6_channel->in_req);
if (p->aspa_channel)
rt_refresh_begin(&p->aspa_channel->in_req);
p->refresh_channels = 1; p->refresh_channels = 1;
} }
@ -160,6 +189,8 @@ rpki_stop_refresh(struct rpki_proto *p)
rt_refresh_end(&p->roa4_channel->in_req); rt_refresh_end(&p->roa4_channel->in_req);
if (p->roa6_channel) if (p->roa6_channel)
rt_refresh_end(&p->roa6_channel->in_req); 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->state = RPKI_CS_SHUTDOWN;
cache->request_session_id = 1; 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->refresh_interval = cf->refresh_interval;
cache->retry_interval = cf->retry_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; 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) if (old->tr_config.type != new->tr_config.type)
{ {
CACHE_TRACE(D_EVENTS, cache, "Transport type changed"); 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; 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 #if HAVE_LIBSSH
else if (new->tr_config.type == RPKI_TR_SSH) 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; struct rpki_cache *cache = p->cache;
if (!proto_configure_channel(&p->p, &p->roa4_channel, proto_cf_find_channel(CF, NET_ROA4)) || 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; return NEED_RESTART;
if (rpki_reconfigure_cache(p, cache, new, old) != SUCCESSFUL_RECONF) 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->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 P; return P;
} }
@ -883,8 +952,12 @@ rpki_show_proto_info(struct proto *P)
default_port = RPKI_SSH_PORT; default_port = RPKI_SSH_PORT;
break; break;
#endif #endif
case RPKI_TR_TCP: case RPKI_TR_TCP:;
transport_name = "Unprotected over 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; default_port = RPKI_TCP_PORT;
break; break;
}; };
@ -927,6 +1000,11 @@ rpki_show_proto_info(struct proto *P)
channel_show_info(p->roa6_channel); channel_show_info(p->roa6_channel);
else else
cli_msg(-1006, " No roa6 channel"); 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 void
rpki_check_config(struct rpki_config *cf) 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 */ /* Do not check templates at all */
if (cf->c.class == SYM_TEMPLATE) if (cf->c.class == SYM_TEMPLATE)
return; return;
@ -998,7 +1079,7 @@ struct protocol proto_rpki = {
.init = rpki_init, .init = rpki_init,
.start = rpki_start, .start = rpki_start,
.postconfig = rpki_postconfig, .postconfig = rpki_postconfig,
.channel_mask = (NB_ROA4 | NB_ROA6), .channel_mask = (NB_ROA4 | NB_ROA6 | NB_ASPA),
.show_proto_info = rpki_show_proto_info, .show_proto_info = rpki_show_proto_info,
.shutdown = rpki_shutdown, .shutdown = rpki_shutdown,
.copy_config = rpki_copy_config, .copy_config = rpki_copy_config,

View File

@ -29,7 +29,8 @@
#define RPKI_VERSION_0 0 #define RPKI_VERSION_0 0
#define RPKI_VERSION_1 1 #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 */ 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 */ u32 serial_num; /* Serial number denotes the logical version of data from cache server */
u8 version; /* Protocol version */ u8 version; /* Protocol version */
u8 min_version; /* Minimum allowed protocol version */
btime last_update; /* Last successful synchronization with cache server */ btime last_update; /* Last successful synchronization with cache server */
btime last_rx_prefix; /* Last received prefix PDU */ 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_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_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_start_refresh(struct rpki_proto *p);
void rpki_stop_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 *roa4_channel;
struct channel *roa6_channel; struct channel *roa6_channel;
struct channel *aspa_channel;
u8 refresh_channels; /* For non-incremental updates using rt_refresh_begin(), rt_refresh_end() */ 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_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 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 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); void rpki_check_config(struct rpki_config *cf);

View File

@ -24,10 +24,16 @@
static int static int
rpki_tr_tcp_open(struct rpki_tr_sock *tr) 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; sock *sk = tr->sk;
sk->type = SK_TCP_ACTIVE; 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) if (sk_open(sk, tr->cache->p->p.loop) != 0)
return RPKI_TR_ERROR; return RPKI_TR_ERROR;

View File

@ -56,6 +56,12 @@ enum rpki_tr_type {
#endif #endif
}; };
/* TCP authentication types */
enum rpki_tcp_auth {
RPKI_TCP_AUTH_NONE,
RPKI_TCP_AUTH_MD5
};
/* Common configure structure for transports */ /* Common configure structure for transports */
struct rpki_tr_config { struct rpki_tr_config {
enum rpki_tr_type type; /* RPKI_TR_TCP or RPKI_TR_SSH */ 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 { 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 { struct rpki_tr_ssh_config {

View File

@ -15,6 +15,7 @@ CF_DEFINES
#define STATIC_CFG ((struct static_config *) this_proto) #define STATIC_CFG ((struct static_config *) this_proto)
static struct static_route *this_srt, *this_snh; static struct static_route *this_srt, *this_snh;
static struct f_inst *this_srt_cmds, *this_srt_last_cmd; static struct f_inst *this_srt_cmds, *this_srt_last_cmd;
static uint this_srt_aspa_max;
static struct static_route * static struct static_route *
static_nexthop_new(void) static_nexthop_new(void)
@ -47,6 +48,7 @@ CF_DECLS
CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK, DEV) 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(ONLINK, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD, MPLS)
CF_KEYWORDS(TRANSIT, PROVIDERS)
CF_GRAMMAR CF_GRAMMAR
@ -148,8 +150,35 @@ stat_route:
| stat_route0 BLACKHOLE { this_srt->dest = RTD_BLACKHOLE; } | stat_route0 BLACKHOLE { this_srt->dest = RTD_BLACKHOLE; }
| stat_route0 UNREACHABLE { this_srt->dest = RTD_UNREACHABLE; } | stat_route0 UNREACHABLE { this_srt->dest = RTD_UNREACHABLE; }
| stat_route0 PROHIBIT { this_srt->dest = RTD_PROHIBIT; } | 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: stat_route_item:
cmd { cmd {
if (this_srt_last_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) else if (r->dest)
ea_set_dest(&ea, 0, 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) if (p->p.mpls_channel)
{ {
struct mpls_channel *mc = (void *) 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 */ byte use_bfd; /* Configured to use BFD */
uint mpls_label; /* Local MPLS label, -1 if unused */ uint mpls_label; /* Local MPLS label, -1 if unused */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */ 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"); 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 #define KRT_FEATURES_MAX 4
static void krt_bitfield_format(const eattr *e, byte *buf, uint buflen); 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 static struct ea_class
ea_krt_prefsrc = { ea_krt_prefsrc = {
@ -71,11 +73,13 @@ static struct ea_class ea_krt_metrics[] = {
.name = "krt_lock", .name = "krt_lock",
.type = T_INT, .type = T_INT,
.format = krt_bitfield_format, .format = krt_bitfield_format,
.empty = krt_bitfield_empty,
}, },
[RTAX_FEATURES] = { [RTAX_FEATURES] = {
.name = "krt_features", .name = "krt_features",
.type = T_INT, .type = T_INT,
.format = krt_bitfield_format, .format = krt_bitfield_format,
.empty = krt_bitfield_empty,
}, },
[RTAX_CC_ALGO] = { [RTAX_CC_ALGO] = {
.name = "krt_congctl", .name = "krt_congctl",
@ -1589,8 +1593,24 @@ done:
} }
static inline int 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 * 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. * 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; 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); err = nl_send_route(p, new, NL_OP_REPLACE);
} }

View File

@ -285,3 +285,14 @@ sk_set_freebind(sock *s)
return 0; 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 CF_DEFINES
static struct log_config *this_log; static struct log_config *this_log;
static struct cli_config *this_cli_config;
CF_DECLS 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(NAME, CONFIRM, UNDO, CHECK, TIMEOUT, DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING, STATUS)
CF_KEYWORDS(PING, WAKEUP, SOCKETS, SCHEDULING, EVENTS, TIMERS, ALLOCATOR) CF_KEYWORDS(PING, WAKEUP, SOCKETS, SCHEDULING, EVENTS, TIMERS, ALLOCATOR)
CF_KEYWORDS(GRACEFUL, RESTART, FIXED) 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 <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 <t> cfg_name
%type <tf> timeformat_which
%type <t> syslog_name %type <t> syslog_name
CF_GRAMMAR 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 { conf: THREADS expr {
if ($2 < 1) cf_error("Number of threads must be at least one."); 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; 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 * static inline byte *
sk_skip_ip_header(byte *pkt, int *len) sk_skip_ip_header(byte *pkt, int *len)
{ {
@ -999,6 +1033,9 @@ sk_set_rbsize(sock *s, uint val)
xfree(s->rbuf_alloc); xfree(s->rbuf_alloc);
s->rbuf_alloc = xmalloc(val); s->rbuf_alloc = xmalloc(val);
s->rpos = s->rbuf = s->rbuf_alloc; s->rpos = s->rbuf = s->rbuf_alloc;
if ((s->type == SK_UDP) || (s->type == SK_IP))
sk_set_min_rcvbuf(s, s->rbsize);
} }
void void
@ -1108,10 +1145,11 @@ sk_setup(sock *s)
} }
#endif #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. /* 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 #ifdef SO_BINDTODEVICE
struct ifreq ifr = {}; struct ifreq ifr = {};
strcpy(ifr.ifr_name, s->vrf->name); strcpy(ifr.ifr_name, s->vrf->name);
@ -1183,6 +1221,10 @@ sk_setup(sock *s)
if (s->tos >= 0) if (s->tos >= 0)
if (sk_set_tos6(s, s->tos) < 0) if (sk_set_tos6(s, s->tos) < 0)
return -1; 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 */ /* 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) if (sk_set_priority(s, s->priority) < 0)
return -1; return -1;
if ((s->type == SK_UDP) || (s->type == SK_IP))
sk_set_min_rcvbuf(s, s->rbsize);
return 0; return 0;
} }
@ -1680,7 +1725,7 @@ err:
} }
int 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; struct sockaddr_un sa;
int fd; int fd;
@ -2572,7 +2617,7 @@ io_loop(void)
} }
void void
test_old_bird(char *path) test_old_bird(const char *path)
{ {
int fd; int fd;
struct sockaddr_un sa; struct sockaddr_un sa;

View File

@ -51,7 +51,7 @@ static struct log_channel * _Atomic global_logs;
/* Logging flags to validly prepare logging messages */ /* Logging flags to validly prepare logging messages */
static _Atomic uint logging_flags; static _Atomic uint logging_flags;
static _Atomic uint logging_mask; static _Atomic uint logging_mask = ~0U;
#ifdef HAVE_SYSLOG_H #ifdef HAVE_SYSLOG_H
#include <sys/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); memcpy(buf->buf.end - sizeof TOO_LONG, TOO_LONG, sizeof TOO_LONG);
#undef 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 ( for (
struct log_channel *l = atomic_load_explicit(&global_logs, memory_order_acquire); struct log_channel *l = glogs; l;
l;
l = atomic_load_explicit(&l->next, memory_order_acquire) l = atomic_load_explicit(&l->next, memory_order_acquire)
) )
{ {

View File

@ -30,6 +30,7 @@
#include "lib/event.h" #include "lib/event.h"
#include "lib/locking.h" #include "lib/locking.h"
#include "lib/timer.h" #include "lib/timer.h"
#include "lib/tlists.h"
#include "lib/string.h" #include "lib/string.h"
#include "nest/route.h" #include "nest/route.h"
#include "nest/protocol.h" #include "nest/protocol.h"
@ -186,6 +187,8 @@ cf_read(byte *dest, uint len, int fd)
return l; return l;
} }
static void cli_preconfig(struct config *c);
void void
sysdep_preconfig(struct config *c) 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_scopes", "ips_", 255);
read_iproute_table(c, PATH_IPROUTE_DIR "/rt_tables", "ipt_", 0xffffffff); read_iproute_table(c, PATH_IPROUTE_DIR "/rt_tables", "ipt_", 0xffffffff);
#endif #endif
cli_preconfig(c);
} }
static void cli_commit(struct config *new, struct config *old);
void void
sysdep_commit(struct config *new, struct config *old) sysdep_commit(struct config *new, struct config *old)
{ {
if (!new->shutdown) if (!new->shutdown)
log_switch(0, &new->logfiles, new->syslog_name); log_switch(0, &new->logfiles, new->syslog_name);
cli_commit(new, old);
bird_thread_commit(new, old); bird_thread_commit(new, old);
} }
@ -400,9 +408,28 @@ cmd_reconfig_status(void)
* Command-Line Interface * Command-Line Interface
*/ */
static sock *cli_sk; static struct cli_config initial_control_socket_config = {
static char *path_control_socket = PATH_CONTROL_SOCKET; .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 static void
cli_write(cli *c) cli_write(cli *c)
@ -523,7 +550,7 @@ cli_connect(sock *s, uint size UNUSED)
s->rx_hook = cli_rx; s->rx_hook = cli_rx;
s->tx_hook = cli_tx; s->tx_hook = cli_tx;
s->err_hook = cli_err; 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->pool = c->pool; /* We need to have all the socket buffers allocated in the cli pool */
s->fast_rx = 1; s->fast_rx = 1;
c->rx_pos = c->rx_buf; c->rx_pos = c->rx_buf;
@ -531,33 +558,116 @@ cli_connect(sock *s, uint size UNUSED)
return 1; return 1;
} }
static void static struct cli_listener *
cli_init_unix(uid_t use_uid, gid_t use_gid) cli_listen(struct cli_config *cf)
{ {
sock *s; struct cli_listener *l = mb_allocz(cli_pool, sizeof *l);
l->config = cf;
cli_init(); sock *s = l->s = sk_new(cli_pool);
s = cli_sk = sk_new(cli_pool);
s->type = SK_UNIX_PASSIVE; s->type = SK_UNIX_PASSIVE;
s->rx_hook = cli_connect; s->rx_hook = cli_connect;
s->err_hook = cli_connect_err; s->err_hook = cli_connect_err;
s->data = l;
s->rbsize = 1024; s->rbsize = 1024;
s->fast_rx = 1; s->fast_rx = 1;
/* Return value intentionally ignored */ /* Return value intentionally ignored */
unlink(path_control_socket); unlink(cf->name);
if (sk_open_unix(s, &main_birdloop, path_control_socket) < 0) if (sk_open_unix(s, &main_birdloop, cf->name) < 0)
die("Cannot create control socket %s: %m", path_control_socket); {
log(L_ERR "Cannot create control socket %s: %m", cf->name);
return NULL;
}
if (use_uid || use_gid) if (cf->uid || cf->gid)
if (chown(path_control_socket, use_uid, use_gid) < 0) if (chown(cf->name, cf->uid, cf->gid) < 0)
die("chown: %m"); {
log(L_ERR "Cannot chown control socket %s: %m", cf->name);
return NULL;
}
if (chmod(path_control_socket, 0660) < 0) if (chmod(cf->name, cf->mode) < 0)
die("chmod: %m"); {
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 * PID file
*/ */
@ -635,7 +745,7 @@ void
sysdep_shutdown_done(void) sysdep_shutdown_done(void)
{ {
unlink_pid_file(); unlink_pid_file();
unlink(path_control_socket); cli_deafen(main_control_socket);
log_msg(L_FATAL "Shutdown completed"); log_msg(L_FATAL "Shutdown completed");
exit(0); exit(0);
} }
@ -920,6 +1030,8 @@ main(int argc, char **argv)
uid_t use_uid = get_uid(use_user); uid_t use_uid = get_uid(use_user);
gid_t use_gid = get_gid(use_group); gid_t use_gid = get_gid(use_group);
cli_init();
if (!parse_and_exit) if (!parse_and_exit)
{ {
test_old_bird(path_control_socket); 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_init(void);
void io_loop(void); void io_loop(void);
void io_log_dump(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 { enum rf_mode {
RF_APPEND = 1, RF_APPEND = 1,
@ -130,7 +130,7 @@ int rf_fileno(struct rfile *f);
extern struct rfile rf_stderr; 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); ip_addr resolve_hostname(const char *host, int type, const char **err_msg);
/* krt.c bits */ /* krt.c bits */