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:
commit
b8d3f41d5d
109
CONTRIBUTING.md
Normal file
109
CONTRIBUTING.md
Normal 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
15
README
@ -68,12 +68,23 @@ User support
|
||||
If you want to help us debugging, enhancing and porting BIRD or just lurk
|
||||
around to see what's going to develop, feel free to subscribe to the BIRD
|
||||
users mailing list bird-users@network.cz, just send `subscribe' to
|
||||
bird-request@network.cz. Bug reports, suggestions, feature requests and
|
||||
code are welcome! We don't use gitlab issues for reporting, sorry.
|
||||
bird-request@network.cz.
|
||||
|
||||
Subscribe: http://bird.network.cz/mailman/listinfo/bird-users/
|
||||
Archive: http://bird.network.cz/pipermail/bird-users/
|
||||
|
||||
Please don't send security issues to the mailing-list, contact us instead at
|
||||
bird-support@network.cz which is a private e-mail address where you also can
|
||||
get commercial support for your BIRD deployment.
|
||||
|
||||
We don't use our gitlab issues for reporting but we're partially tracking
|
||||
the core developent team's work there publicly.
|
||||
|
||||
Contributing
|
||||
============
|
||||
|
||||
Please see the CONTRIBUTING.md file to find how to contribute to BIRD.
|
||||
|
||||
Licence
|
||||
=======
|
||||
|
||||
|
@ -28,7 +28,7 @@ class BIRDFValPrinter(BIRDPrinter):
|
||||
"T_ENUM_SCOPE": "i",
|
||||
"T_ENUM_RTD": "i",
|
||||
"T_ENUM_ROA": "i",
|
||||
"T_ENUM_NETTYPE": "i",
|
||||
"T_ENUM_NET_TYPE": "i",
|
||||
"T_ENUM_RA_PREFERENCE": "i",
|
||||
"T_ENUM_AF": "i",
|
||||
"T_IP": "ip",
|
||||
|
@ -715,7 +715,7 @@ cf_lex_symbol(const char *data)
|
||||
int val = sym->keyword->value;
|
||||
if (val > 0) return val;
|
||||
cf_lval.i = -val;
|
||||
return ENUM;
|
||||
return ENUM_TOKEN;
|
||||
}
|
||||
case SYM_METHOD:
|
||||
return (sym->method->arg_num > 1) ? CF_SYM_METHOD_ARGS : CF_SYM_METHOD_BARE;
|
||||
|
@ -589,6 +589,7 @@ order_shutdown(int gr)
|
||||
init_list(&c->mpls_domains);
|
||||
init_list(&c->symbols);
|
||||
obstacle_target_init(&c->obstacles, &c->obstacles_cleared, c->pool, "Config");
|
||||
c->cli = (struct cli_config_list) {};
|
||||
memset(c->def_tables, 0, sizeof(c->def_tables));
|
||||
c->shutdown = 1;
|
||||
c->gr_down = gr;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "lib/resource.h"
|
||||
#include "lib/obstacle.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/tlists.h"
|
||||
|
||||
/* Configuration structure */
|
||||
struct config {
|
||||
@ -26,6 +27,7 @@ struct config {
|
||||
list logfiles; /* Configured log files (sysdep) */
|
||||
list tests; /* Configured unit tests (f_bt_test_suite) */
|
||||
list symbols; /* Configured symbols in config order */
|
||||
TLIST_STRUCT_DEF(cli_config, struct cli_config) cli; /* Configured CLI sockets */
|
||||
|
||||
struct rfile *mrtdump_file; /* Configured MRTDump file */
|
||||
const char *syslog_name; /* Name used for syslog (NULL -> no syslog) */
|
||||
|
@ -100,7 +100,8 @@ CF_DECLS
|
||||
struct f_prefix px;
|
||||
struct proto_spec ps;
|
||||
struct channel_limit cl;
|
||||
struct timeformat *tf;
|
||||
struct timeformat tf;
|
||||
struct timeformat *tfp;
|
||||
struct settle_config settle;
|
||||
struct adata *ad;
|
||||
const struct adata *bs;
|
||||
@ -110,7 +111,7 @@ CF_DECLS
|
||||
%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT
|
||||
%token GEQ LEQ NEQ AND OR IMP
|
||||
%token PO PC
|
||||
%token <i> NUM ENUM
|
||||
%token <i> NUM ENUM_TOKEN
|
||||
%token <ip4> IP4
|
||||
%token <ip6> IP6
|
||||
%token <i64> VPN_RD
|
||||
@ -124,7 +125,7 @@ CF_DECLS
|
||||
%type <settle> settle
|
||||
%type <a> ipa net_ip6_slash
|
||||
%type <net> net_ip4_ net_ip4 net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
|
||||
%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_
|
||||
%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_ net_aspa_
|
||||
%type <ad> label_stack_start label_stack
|
||||
|
||||
%type <t> text opttext
|
||||
@ -325,6 +326,12 @@ net_mpls_: MPLS NUM
|
||||
net_fill_mpls($$, $2);
|
||||
}
|
||||
|
||||
net_aspa_: ASPA NUM
|
||||
{
|
||||
$$ = cfg_alloc(sizeof(net_addr_aspa));
|
||||
net_fill_aspa($$, $2);
|
||||
}
|
||||
|
||||
net_ip_: net_ip4_ | net_ip6_ ;
|
||||
net_vpn_: net_vpn4_ | net_vpn6_ ;
|
||||
net_roa_: net_roa4_ | net_roa6_ ;
|
||||
@ -336,6 +343,7 @@ net_:
|
||||
| net_flow_
|
||||
| net_ip6_sadr_
|
||||
| net_mpls_
|
||||
| net_aspa_
|
||||
;
|
||||
|
||||
|
||||
|
@ -34,10 +34,11 @@ m4_define(CF_CLI, `CF_KEYWORDS(m4_translit($1, [[ ]], [[,]]))
|
||||
|
||||
# Enums are translated to C initializers: use CF_ENUM(typename, prefix, values)
|
||||
# For different prefix: CF_ENUM_PX(typename, external prefix, C prefix, values)
|
||||
# The typename is converted to a keyword by removing T_ENUM_ prefix
|
||||
m4_define(CF_enum, `m4_divert(1){ "CF_enum_prefix_ext[[]]$1", -((CF_enum_type<<16) | CF_enum_prefix_int[[]]$1) },
|
||||
m4_divert(-1)')
|
||||
m4_define(CF_ENUM, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$2)CF_iterate([[CF_enum]], [[m4_shift(m4_shift($@))]])DNL')
|
||||
m4_define(CF_ENUM_PX, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$3)CF_iterate([[CF_enum]], [[m4_shift(m4_shift(m4_shift($@)))]])DNL')
|
||||
m4_define(CF_ENUM, `CF_keywd(m4_substr($1, 7))m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$2)CF_iterate([[CF_enum]], [[m4_shift(m4_shift($@))]])DNL')
|
||||
m4_define(CF_ENUM_PX, `CF_keywd(m4_substr($1, 7))m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$3)CF_iterate([[CF_enum]], [[m4_shift(m4_shift(m4_shift($@)))]])DNL')
|
||||
|
||||
# After all configuration templates end, we generate the keyword list
|
||||
m4_m4wrap(`
|
||||
|
@ -29,6 +29,8 @@ m4_define(CF_END, `m4_divert(-1)')
|
||||
m4_define(CF_itera, `m4_ifelse($#, 1, [[CF_iter($1)]], [[CF_iter($1)[[]]CF_itera(m4_shift($@))]])')
|
||||
m4_define(CF_iterate, `m4_define([[CF_iter]], m4_defn([[$1]]))CF_itera($2)')
|
||||
|
||||
m4_define(CF_append, `m4_define([[$1]], m4_ifdef([[$1]], m4_defn([[$1]])[[$3]])[[$2]])')
|
||||
|
||||
# Keywords act as untyped %token
|
||||
m4_define(CF_keywd, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)m4_define([[CF_toks]],CF_toks $1)]])')
|
||||
m4_define(CF_KEYWORDS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token<s>[[]]CF_toks
|
||||
@ -46,8 +48,10 @@ m4_define(CF_CLI_OPT, `')
|
||||
m4_define(CF_CLI_HELP, `')
|
||||
|
||||
# ENUM declarations are ignored
|
||||
m4_define(CF_ENUM, `')
|
||||
m4_define(CF_ENUM_PX, `')
|
||||
m4_define(CF_token, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)%token<s> $1]])')
|
||||
m4_define(CF_enum, `CF_append([[CF_enum_type]],[[$1 { $$ = $2; }]],[[ | ]])CF_token($1)')
|
||||
m4_define(CF_ENUM, `CF_enum(m4_substr($1, 7), $1)')
|
||||
m4_define(CF_ENUM_PX, `CF_enum(m4_substr($1, 7), $1)')
|
||||
|
||||
# After all configuration templates end, we finally generate the grammar file.
|
||||
m4_m4wrap(`
|
||||
@ -58,7 +62,11 @@ m4_undivert(1)DNL
|
||||
|
||||
m4_undivert(2)DNL
|
||||
|
||||
%type <i> enum_type
|
||||
|
||||
%%
|
||||
enum_type: CF_enum_type;
|
||||
|
||||
m4_undivert(3)DNL
|
||||
|
||||
%%
|
||||
|
@ -114,7 +114,7 @@ AC_SEARCH_LIBS([clock_gettime], [rt posix4],
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
# Store this value because ac_test_CFLAGS is overwritten by AC_PROG_CC
|
||||
if test "$ac_test_CFLAGS" != set ; then
|
||||
if ! test "$ac_test_CFLAGS" ; then
|
||||
bird_cflags_default=yes
|
||||
fi
|
||||
|
||||
|
330
doc/bird.sgml
330
doc/bird.sgml
@ -125,11 +125,11 @@ Anyway, it will probably work well also on older systems.
|
||||
and Perl, installing BIRD should be as easy as:
|
||||
|
||||
<code>
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
vi /usr/local/etc/bird.conf
|
||||
bird
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
vi /usr/local/etc/bird.conf
|
||||
bird
|
||||
</code>
|
||||
|
||||
<p>You can use <tt>./configure --help</tt> to get a list of configure
|
||||
@ -992,7 +992,7 @@ agreement").
|
||||
|
||||
<tag><label id="proto-pass-gen-from">generate from "<m/time/"</tag>
|
||||
The start time of the usage of the password for packet signing.
|
||||
The format of <cf><m/time/</cf> is <tt>dd-mm-yyyy HH:MM:SS</tt>.
|
||||
The format of <cf><m/time/</cf> is <tt>YYYY-MM-DD [hh:mm:ss[.sss]]</tt>.
|
||||
|
||||
<tag><label id="proto-pass-gen-to">generate to "<m/time/"</tag>
|
||||
The last time of the usage of the password for packet signing.
|
||||
@ -1068,14 +1068,15 @@ inherited from templates can be updated by new definitions.
|
||||
|
||||
<tag><label id="proto-rpki-reload">rpki reload <m/switch/</tag>
|
||||
Import or export filters may depend on route RPKI status (using
|
||||
<cf/roa_check()/ operator). In contrast to to other filter operators,
|
||||
this status for the same route may change as the content of ROA tables
|
||||
changes. When this option is active, BIRD activates automatic reload of
|
||||
the appropriate subset of prefixes imported or exported by the channels
|
||||
whenever ROA tables are updated (after a short settle
|
||||
time). When disabled, route reloads have to be requested manually. The
|
||||
option is ignored if <cf/roa_check()/ is not used in channel filters.
|
||||
Note that for BGP channels, automatic reload requires
|
||||
<cf/roa_check()/ or <cf/aspa_check()/ operators). In contrast to other
|
||||
filter operators, this status for the same route may change as the
|
||||
content of ROA and ASPA tables changes. When this option is active, BIRD
|
||||
activates automatic reload of the appropriate subset of prefixes imported
|
||||
or exported by the channels whenever ROA and ASPA
|
||||
tables are updated (after a short settle time). When disabled, route
|
||||
reloads have to be requested manually. The option is ignored if neither
|
||||
<cf/roa_check()/ nor <cf/aspa_check()/ is used in channel filters. Note
|
||||
that for BGP channels, automatic reload requires
|
||||
<ref id="bgp-import-table" name="import table"> or
|
||||
<ref id="bgp-export-table" name="export table"> (for respective
|
||||
direction). Default: on.
|
||||
@ -1264,9 +1265,11 @@ protocol bgp {
|
||||
<chapt>Remote control
|
||||
<label id="remote-control">
|
||||
|
||||
<sect>Overview
|
||||
<label id="remote-control-overview">
|
||||
|
||||
<p>You can use the command-line client <file>birdc</file> to talk with a running
|
||||
BIRD. Communication is done using a <file/bird.ctl/ UNIX domain socket (unless
|
||||
changed with the <tt/-s/ option given to both the server and the client). The
|
||||
BIRD. Communication is done using the appropriate UNIX domain socket. The
|
||||
commands can perform simple actions such as enabling/disabling of protocols,
|
||||
telling BIRD to show various information, telling it to show routing table
|
||||
filtered by filter, or asking BIRD to reconfigure. Press <tt/?/ at any time to
|
||||
@ -1282,10 +1285,32 @@ does not support command line editing and history and has minimal dependencies.
|
||||
This is useful for running BIRD in resource constrained environments, where
|
||||
Readline library (required for regular BIRD client) is not available.
|
||||
|
||||
<p>Many commands have the <m/name/ of the protocol instance as an argument.
|
||||
This argument can be omitted if there exists only a single instance.
|
||||
<sect>Configuration
|
||||
<label id="remote-control-configuration">
|
||||
|
||||
<p>Here is a brief list of supported functions:
|
||||
<p>By default, BIRD opens <file/bird.ctl/ UNIX domain socket and the CLI tool
|
||||
connects to it. If changed on the command line by the <tt/-s/ option,
|
||||
BIRD or the CLI tool connects there instead.
|
||||
|
||||
<p>It's also possible to configure additional remote control sockets in the
|
||||
configuration file by <cf/cli "name";/ and you can open how many
|
||||
sockets you wish. There are no checks whether the user configured the same
|
||||
socket multiple times and BIRD may behave weirdly if this happens. On shutdown,
|
||||
the additional sockets get removed immediately and only the main socket stays
|
||||
until the very end.
|
||||
|
||||
<p>The remote control socket can be also set as restricted by
|
||||
<cf/cli "name" { restrict; };/ instead of sending the <cf/restrict/ command
|
||||
after connecting. The user may still overload the daemon by requesting insanely
|
||||
complex filters so you shouldn't expose this socket to public anyway.
|
||||
|
||||
<sect>Usage
|
||||
<label id="remote-control-usage">
|
||||
|
||||
<p>Here is a brief list of supported functions.
|
||||
|
||||
<p>Note: Many commands have the <m/name/ of the protocol instance as an argument.
|
||||
This argument can be omitted if there exists only a single instance.
|
||||
|
||||
<descrip>
|
||||
<tag><label id="cli-show-status">show status</tag>
|
||||
@ -1465,6 +1490,13 @@ This argument can be omitted if there exists only a single instance.
|
||||
pipe protocol, both directions are always reloaded together (<cf/in/ or
|
||||
<cf/out/ options are ignored in that case).
|
||||
|
||||
<tag><label id="cli-timeformat">timeformat "<m/format1/" [<m/limit/ "<m/format2/"]</tag>
|
||||
Override format of date/time used by BIRD in this CLI session.
|
||||
|
||||
Meaning of "<m/format1/", <m/limit/, and "<m/format2/" is the same as in the
|
||||
<ref id="opt-timeformat" name="timeformat"> configuration option. Also, the
|
||||
same <cf/iso .../ shorthands may be used.
|
||||
|
||||
<tag><label id="cli-down">down</tag>
|
||||
Shut BIRD down.
|
||||
|
||||
@ -1627,9 +1659,9 @@ in the foot).
|
||||
a shell pattern (represented also as a string).
|
||||
|
||||
<tag><label id="type-bytestring">bytestring</tag>
|
||||
This is a sequences of arbitrary bytes. There are no ways to modify
|
||||
bytestrings in filters. You can pass them between function, assign
|
||||
them to variables of type <cf/bytestring/, print such values,
|
||||
This is a sequence of arbitrary bytes. There are no ways to modify
|
||||
bytestrings in filters. You can pass them between functions, assign
|
||||
them to variables of type <cf/bytestring/, print such values, and
|
||||
compare bytestings (<cf/=, !=/).
|
||||
|
||||
Bytestring literals are written as a sequence of hexadecimal digit
|
||||
@ -1678,7 +1710,7 @@ in the foot).
|
||||
Route Distinguisher (<rfc id="4364">). They support the same special
|
||||
operators as IP prefixes, and also <cf/.rd/ which extracts the Route
|
||||
Distinguisher. Their literals are written
|
||||
as <cf><m/vpnrd/ <m/ipprefix/</cf>
|
||||
as <cf><m/rd/ <m/ipprefix/</cf>
|
||||
|
||||
<cf/NET_ROA4/ and <cf/NET_ROA6/ prefixes hold an IP prefix range
|
||||
together with an ASN. They support the same special operators as IP
|
||||
@ -1693,9 +1725,9 @@ in the foot).
|
||||
<cf/NET_MPLS/ holds a single MPLS label and its handling is currently
|
||||
not implemented.
|
||||
|
||||
<tag><label id="type-vpnrd">vpnrd</tag>
|
||||
<tag><label id="type-rd"><label id="type-vpnrd">rd</tag>
|
||||
This is a route distinguisher according to <rfc id="4364">. There are
|
||||
three kinds of RD's: <cf><m/asn/:<m/32bit int/</cf>, <cf><m/asn4/:<m/16bit int/</cf>
|
||||
three kinds of RDs: <cf><m/asn/:<m/32bit int/</cf>, <cf><m/asn4/:<m/16bit int/</cf>
|
||||
and <cf><m/IPv4 address/:<m/32bit int/</cf>
|
||||
|
||||
<tag><label id="type-ec">ec</tag>
|
||||
@ -1722,9 +1754,9 @@ in the foot).
|
||||
to extract corresponding components of LCs:
|
||||
<cf>(<m/asn/, <m/data1/, <m/data2/)</cf>.
|
||||
|
||||
<tag><label id="type-set">int|pair|quad|ip|prefix|ec|lc|enum set</tag>
|
||||
Filters recognize four types of sets. Sets are similar to strings: you
|
||||
can pass them around but you can't modify them. Literals of type <cf>int
|
||||
<tag><label id="type-set">int|pair|quad|ip|prefix|ec|lc|rd|enum set</tag>
|
||||
Filters recognize several types of sets. Sets are similar to strings: you
|
||||
can pass them around but you cannot modify them. Literals of type <cf>int
|
||||
set</cf> look like <cf> [ 1, 2, 5..7 ]</cf>. As you can see, both simple
|
||||
values and ranges are permitted in sets.
|
||||
|
||||
@ -1747,21 +1779,20 @@ in the foot).
|
||||
is valid, while <cf/(10, *, 20..30)/ or <cf/(10, 20..30, 40)/ is not
|
||||
valid.
|
||||
|
||||
You can also use expressions for int, pair, EC and LC set values.
|
||||
However, it must be possible to evaluate these expressions before daemon
|
||||
boots. So you can use only constants inside them. E.g.
|
||||
You can also use named constants or compound expressions for non-prefix
|
||||
set values. However, it must be possible to evaluate these expressions
|
||||
before daemon boots. So you can use only constants inside them. Also,
|
||||
in case of compound expressions, they require parentheses around them.
|
||||
E.g.
|
||||
|
||||
<code>
|
||||
define one=1;
|
||||
define myas=64500;
|
||||
int set odds;
|
||||
pair set ps;
|
||||
ec set es;
|
||||
<code>
|
||||
define one=1;
|
||||
define myas=64500;
|
||||
|
||||
odds = [ one, 2+1, 6-one, 2*2*2-1, 9, 11 ];
|
||||
ps = [ (1,one+one), (3,4)..(4,8), (5,*), (6,3..6), (7..9,*) ];
|
||||
es = [ (rt, myas, 3*10), (rt, myas+one, 0..16*16*16-1), (ro, myas+2, *) ];
|
||||
</code>
|
||||
int set odds = [ one, (2+1), (6-one), (2*2*2-1), 9, 11 ];
|
||||
pair set ps = [ (1,one+one), (3,4)..(4,8), (5,*), (6,3..6), (7..9,*) ];
|
||||
ec set es = [ (rt, myas, *), (rt, myas+2, 0..16*16*16-1) ];
|
||||
</code>
|
||||
|
||||
Sets of prefixes are special: their literals does not allow ranges, but
|
||||
allows prefix patterns that are written
|
||||
@ -1923,32 +1954,93 @@ in the foot).
|
||||
<label id="operators">
|
||||
|
||||
<p>The filter language supports common integer operators <cf>(+,-,*,/)</cf>,
|
||||
parentheses <cf/(a*(b+c))/, comparison <cf/(a=b, a!=b, a<b, a>=b)/.
|
||||
Logical operations include unary not (<cf/!/), and (<cf/&&/), and or
|
||||
(<cf/||/). Special operators include (<cf/˜/,
|
||||
<cf/!˜/) for "is (not) element of a set" operation - it can be used on
|
||||
element and set of elements of the same type (returning true if element is
|
||||
contained in the given set), or on two strings (returning true if first string
|
||||
matches a shell-like pattern stored in second string) or on IP and prefix
|
||||
(returning true if IP is within the range defined by that prefix), or on prefix
|
||||
and prefix (returning true if first prefix is more specific than second one) or
|
||||
on bgppath and bgpmask (returning true if the path matches the mask) or on
|
||||
number and bgppath (returning true if the number is in the path) or on bgppath
|
||||
and int (number) set (returning true if any ASN from the path is in the set) or
|
||||
on pair/quad and clist (returning true if the pair/quad is element of the
|
||||
clist) or on clist and pair/quad set (returning true if there is an element of
|
||||
the clist that is also a member of the pair/quad set).
|
||||
parentheses <cf/(a*(b+c))/, comparison <cf/(a=b, a!=b, a<b, a>=b)/.</p>
|
||||
|
||||
<p>There is one operator related to ROA infrastructure - <cf/roa_check()/. It
|
||||
examines a ROA table and does <rfc id="6483"> route origin validation for a
|
||||
given network prefix. The basic usage is <cf>roa_check(<m/table/)</cf>, which
|
||||
checks the current route (which should be from BGP to have AS_PATH argument) in
|
||||
the specified ROA table and returns ROA_UNKNOWN if there is no relevant ROA,
|
||||
ROA_VALID if there is a matching ROA, or ROA_INVALID if there are some relevant
|
||||
ROAs but none of them match. There is also an extended variant
|
||||
<cf>roa_check(<m/table/, <m/prefix/, <m/asn/)</cf>, which allows to specify a
|
||||
prefix and an ASN as arguments.
|
||||
<p>Logical operations include unary not (<cf/!/), and (<cf/&&/), and or
|
||||
(<cf/||/).</p>
|
||||
|
||||
<p>Special operators include (<cf/˜/, <cf/!˜/) for "is (not) element
|
||||
of a set" operation - it can be used on:
|
||||
<itemize>
|
||||
<item>element and set of elements of the same type (returning true if
|
||||
element is contained in the given set)
|
||||
<item>two strings (returning true if the first string matches a shell-like
|
||||
pattern stored in the second string)
|
||||
<item>IP and prefix (returning true if IP is within the range defined by
|
||||
that prefix)
|
||||
<item>prefix and prefix (returning true if the first prefix is more specific
|
||||
than the second one)
|
||||
<item>bgppath and bgpmask (returning true if the path matches the mask)
|
||||
<item>number and bgppath (returning true if the number is in the path)
|
||||
<item>bgppath and int (number) set (returning true if any ASN from the
|
||||
path is in the set)
|
||||
<item>pair/quad and clist (returning true if the pair/quad is element of
|
||||
the clist)
|
||||
<item>clist and pair/quad set (returning true if there is an element of the
|
||||
clist that is also a member of the pair/quad set).
|
||||
</itemize>
|
||||
|
||||
<p>There are also operators related to RPKI infrastructure used to run
|
||||
<rfc id="6483"> route origin validation and (draft) AS path validation.
|
||||
|
||||
<itemize>
|
||||
<item><cf>roa_check(<m/table/)</cf> checks the current route in the specified
|
||||
ROA table and returns <cf>ROA_UNKNOWN</cf>, <cf>ROA_INVALID</cf> or <cf>ROA_VALID</cf>,
|
||||
if the validation result is unknown, invalid, or valid, respectively. The result is
|
||||
valid if there is a matching ROA, it is invalid if there is either matching ROA
|
||||
with a different ASN, or any covering ROA with shorter maximal prefix length.
|
||||
|
||||
<item><cf>roa_check(<m/table/, <m/prefix/, <m/asn/)</cf> is an explicit version
|
||||
of the ROA check if the user for whatever reason needs to check a different prefix
|
||||
or different ASN than the default one. The equivalent call of the short variant
|
||||
is <cf>roa_check(<m/table/, net, bgp_path.last)</cf> and it is faster
|
||||
to call the short variant.
|
||||
|
||||
<item><cf>aspa_check_downstream(<m/table/)</cf> checks the current route
|
||||
in the specified ASPA table and returns <cf>ASPA_UNKNOWN</cf>, <cf>ASPA_INVALID</cf>,
|
||||
or <cf>ASPA_VALID</cf> if the validation result is unknown, invalid, or valid,
|
||||
respectively. The result is valid if there is a full coverage of matching
|
||||
ASPA records according to the Algorithm for Downstream Paths by the (draft).
|
||||
This operator is not present if BGP is not compiled in.
|
||||
|
||||
<item><cf>aspa_check_upstream(<m/table/)</cf> checks the current route
|
||||
in the specified ASPA table as the former operator, but it applies the
|
||||
(stricter) Algorithm for Upstream Paths by the (draft).
|
||||
This operator is not present if BGP is not compiled in.
|
||||
|
||||
<item><cf>aspa_check(<m/table/, <m/path/, <m/is_upstream/)</cf> is
|
||||
an explicit version of the former two ASPA check operators. The equivalent
|
||||
of <cf>aspa_check_downstream</cf> is <cf>aspa_check(<m/table/, bgp_path, false)</cf>
|
||||
and for <cf>aspa_check_upstream</cf> it is
|
||||
<cf>aspa_check(<m/table/, bgp_path, true)</cf>.
|
||||
Note: the ASPA check does not include the local ASN in the AS path.
|
||||
Also, <cf>ASPA_INVALID</cf> is returned for an empty AS path
|
||||
or for AS path containing <cf>CONFED_SET</cf> or <cf>CONFED_SEQUENCE</cf> blocks,
|
||||
as the (draft) stipulates.
|
||||
</itemize>
|
||||
|
||||
<p>The following example checks for ROA and ASPA on routes from a customer:
|
||||
|
||||
<code>
|
||||
roa6 table r6;
|
||||
aspa table at;
|
||||
attribute int valid_roa;
|
||||
attribute int valid_aspa;
|
||||
|
||||
filter customer_check {
|
||||
case roa_check(r6) {
|
||||
ROA_INVALID: reject "Invalid ROA";
|
||||
ROA_VALID: valid_roa = 1;
|
||||
}
|
||||
|
||||
case aspa_check_upstream(at) {
|
||||
ASPA_INVALID: reject "Invalid ASPA";
|
||||
ASPA_VALID: valid_aspa = 1;
|
||||
}
|
||||
|
||||
accept;
|
||||
}
|
||||
</code>
|
||||
|
||||
<sect>Control structures
|
||||
<label id="control-structures">
|
||||
@ -1971,13 +2063,15 @@ may be an existing one (when just name is used) or a locally defined (when type
|
||||
and name is used). In both cases, it must have the same type as elements.
|
||||
|
||||
<p>The <cf>case</cf> is similar to case from Pascal. Syntax is <cf>case
|
||||
<m/expr/ { else: | <m/num_or_prefix [ .. num_or_prefix]/: <m/statement/ ; [
|
||||
... ] }</cf>. The expression after <cf>case</cf> can be of any type which can be
|
||||
on the left side of the ˜ operator and anything that could be a member of
|
||||
a set is allowed before <cf/:/. Multiple commands are allowed without <cf/{}/
|
||||
grouping. If <cf><m/expr/</cf> matches one of the <cf/:/ clauses, statements
|
||||
between it and next <cf/:/ statement are executed. If <cf><m/expr/</cf> matches
|
||||
neither of the <cf/:/ clauses, the statements after <cf/else:/ are executed.
|
||||
<m/expr/ { else: | <m/set_body_expr/ /: <m/statement/ ; [... ] }</cf>.
|
||||
The expression after <cf>case</cf> can be of any type that could be a member of
|
||||
a set, while the <m/set_body_expr/ before <cf/:/ can be anything (constants,
|
||||
intervals, expressions) that could be a part of a set literal. One exception is
|
||||
prefix type, which can be used in sets bud not in <cf/case/ structure. Multiple
|
||||
commands must be grouped by <cf/{}/. If <cf><m/expr/</cf> matches one
|
||||
of the <cf/:/ clauses, the statement or block after it is
|
||||
executed. If <cf><m/expr/</cf> matches neither of the <cf/:/ clauses, the
|
||||
statement or block after <cf/else:/ is executed.
|
||||
|
||||
<p>Here is example that uses <cf/if/ and <cf/case/ structures:
|
||||
|
||||
@ -1993,7 +2087,7 @@ for int asn in bgp_path do {
|
||||
}
|
||||
|
||||
case arg1 {
|
||||
2: print "two"; print "I can do more commands without {}";
|
||||
2: { print "two"; print "Multiple commands must brace themselves."; }
|
||||
3 .. 5: print "three to five";
|
||||
else: print "something else";
|
||||
}
|
||||
@ -2492,9 +2586,9 @@ protocols are notified and act accordingly (e.g. break an OSPF adjacency when
|
||||
the BFD session went down).
|
||||
|
||||
<p>BIRD implements basic BFD behavior as defined in <rfc id="5880"> (some
|
||||
advanced features like the echo mode or authentication are not implemented), IP
|
||||
transport for BFD as defined in <rfc id="5881"> and <rfc id="5883"> and
|
||||
interaction with client protocols as defined in <rfc id="5882">.
|
||||
advanced features like the echo mode are not implemented), IP transport for BFD
|
||||
as defined in <rfc id="5881"> and <rfc id="5883"> and interaction with client
|
||||
protocols as defined in <rfc id="5882">.
|
||||
|
||||
<p>BFD packets are sent with a dynamic source port number. Linux systems use by
|
||||
default a bit different dynamic port range than the IANA approved one
|
||||
@ -2534,6 +2628,8 @@ milliseconds.
|
||||
<code>
|
||||
protocol bfd [<name>] {
|
||||
accept [ipv4|ipv6] [direct|multihop];
|
||||
strict bind <switch>;
|
||||
zero udp6 checksum rx <switch>;
|
||||
interface <interface pattern> {
|
||||
interval <time>;
|
||||
min rx interval <time>;
|
||||
@ -2583,6 +2679,14 @@ protocol bfd [<name>] {
|
||||
in cases like running multiple BIRD instances on a machine, each
|
||||
handling a different set of interfaces. Default: disabled.
|
||||
|
||||
<tag><label id="bfd-zero-udp6-checksum-rx">zero udp6 checksum rx <m/switch/</tag>
|
||||
UDP checksum computation is optional in IPv4 while it is mandatory in
|
||||
IPv6. Some BFD implementations send UDP datagrams with zero (blank)
|
||||
checksum even in IPv6 case. This option configures BFD listening sockets
|
||||
to accept such datagrams. It is available only on platforms that support
|
||||
the relevant socket option (e.g. <cf/UDP_NO_CHECK6_RX/ on Linux).
|
||||
Default: disabled.
|
||||
|
||||
<tag><label id="bfd-iface">interface <m/pattern/ [, <m/.../] { <m/options/ }</tag>
|
||||
Interface definitions allow to specify options for sessions associated
|
||||
with such interfaces and also may contain interface specific options.
|
||||
@ -2771,6 +2875,7 @@ avoid routing loops.
|
||||
<item> <rfc id="9072"> - Extended Optional Parameters Length for BGP OPEN Message
|
||||
<item> <rfc id="9117"> - Revised Validation Procedure for BGP Flow Specifications
|
||||
<item> <rfc id="9234"> - Route Leak Prevention and Detection Using Roles
|
||||
<item> <rfc id="9687"> - Send Hold Timer
|
||||
</itemize>
|
||||
|
||||
<sect1>Route selection rules
|
||||
@ -2915,10 +3020,9 @@ using the following configuration parameters:
|
||||
restarted. Optionally, it can be configured (by <cf/graceful/ argument)
|
||||
to trigger graceful restart instead of regular restart. It is also
|
||||
possible to specify section with per-peer BFD session options instead of
|
||||
just switch argument. Most BFD session specific options are allowed here
|
||||
with the exception of authentication options. here Note that BFD
|
||||
protocol also has to be configured, see <ref id="bfd" name="BFD">
|
||||
section for details. Default: disabled.
|
||||
just the switch argument. All BFD session-specific options are allowed
|
||||
here. Note that BFD protocol also has to be configured, see
|
||||
<ref id="bfd" name="BFD"> section for details. Default: disabled.
|
||||
|
||||
<tag><label id="bgp-ttl-security">ttl security <m/switch/</tag>
|
||||
Use GTSM (<rfc id="5082"> - the generalized TTL security mechanism). GTSM
|
||||
@ -3113,7 +3217,7 @@ using the following configuration parameters:
|
||||
<tag><label id="bgp-interpret-communities">interpret communities <m/switch/</tag>
|
||||
<rfc id="1997"> demands that BGP speaker should process well-known
|
||||
communities like no-export (65535, 65281) or no-advertise (65535,
|
||||
65282). For example, received route carrying a no-adverise community
|
||||
65282). For example, received route carrying a no-advertise community
|
||||
should not be advertised to any of its neighbors. If this option is
|
||||
enabled (which is by default), BIRD has such behavior automatically (it
|
||||
is evaluated when a route is exported to the BGP protocol just before
|
||||
@ -3210,8 +3314,7 @@ using the following configuration parameters:
|
||||
Send hold timer drops the session if the neighbor is sending keepalives,
|
||||
but does not receive our messages, causing the TCP connection to stall.
|
||||
This may happen due to malfunctioning or overwhelmed neighbor. See
|
||||
<HTMLURL URL="https://datatracker.ietf.org/doc/draft-ietf-idr-bgp-sendholdtimer/"
|
||||
name="draft-ietf-idr-bgp-sendholdtimer"> for more details.
|
||||
<rfc id="9687"> for more details.
|
||||
|
||||
Like the option <cf/keepalive time/, the effective value depends on the
|
||||
negotiated hold time, as it is scaled to maintain proportion between the
|
||||
@ -4087,11 +4190,11 @@ could have up to 5 channels: <cf/ipv4/, <cf/ipv6/, <cf/vpn4/, <cf/vpn6/, and
|
||||
<cf/mpls/.
|
||||
|
||||
<p><descrip>
|
||||
<tag><label id="l3vpn-route-distinguisher">route distinguisher <m/vpnrd/</tag>
|
||||
<tag><label id="l3vpn-route-distinguisher">route distinguisher <m/rd/</tag>
|
||||
The route distinguisher that is attached to routes in the export
|
||||
direction. Mandatory.
|
||||
|
||||
<tag><label id="l3vpn-rd">rd <m/vpnrd/</tag>
|
||||
<tag><label id="l3vpn-rd">rd <m/rd/</tag>
|
||||
A shorthand for the option <cf/route distinguisher/.
|
||||
|
||||
<tag><label id="l3vpn-import-target">import target <m/ec/|<m/ec-set/</tag>
|
||||
@ -4846,14 +4949,14 @@ protocol ospf MyOSPF {
|
||||
authentication cryptographic;
|
||||
password "abc" {
|
||||
id 1;
|
||||
generate to "22-04-2003 11:00:06";
|
||||
accept from "17-01-2001 12:01:05";
|
||||
generate to "2023-04-22 11:00:06";
|
||||
accept from "2021-01-17 12:01:05";
|
||||
algorithm hmac sha384;
|
||||
};
|
||||
password "def" {
|
||||
id 2;
|
||||
generate to "22-07-2005 17:03:21";
|
||||
accept from "22-02-2001 11:34:06";
|
||||
generate to "2025-07-22";
|
||||
accept from "2021-02-22";
|
||||
algorithm hmac sha512;
|
||||
};
|
||||
};
|
||||
@ -5671,6 +5774,10 @@ affected routes after RPKI update, see option <ref id="proto-rpki-reload"
|
||||
name="rpki reload">. Or you can use a BIRD client command <cf>reload in
|
||||
<m/bgp_protocol_name/</cf> for manual call of revalidation of all routes.
|
||||
|
||||
<p>The same protocol, since version 2, also receives and maintains a set
|
||||
of ASPAs. You can then validate AS paths using function <cf/aspa_check()/
|
||||
in (import) filters.
|
||||
|
||||
<sect1>Supported transports
|
||||
<p>
|
||||
<itemize>
|
||||
@ -5690,22 +5797,28 @@ define more RPKI protocols generally.
|
||||
protocol rpki [<name>] {
|
||||
roa4 { table <tab>; };
|
||||
roa6 { table <tab>; };
|
||||
aspa { table <tab>; };
|
||||
remote <ip> | "<domain>" [port <num>];
|
||||
port <num>;
|
||||
local address <ip>;
|
||||
refresh [keep] <num>;
|
||||
retry [keep] <num>;
|
||||
expire [keep] <num>;
|
||||
transport tcp;
|
||||
transport tcp {
|
||||
authentication none|md5;
|
||||
password "<text>";
|
||||
};
|
||||
transport ssh {
|
||||
bird private key "</path/to/id_rsa>";
|
||||
remote public key "</path/to/known_host>";
|
||||
user "<name>";
|
||||
};
|
||||
max version 2;
|
||||
min version 2;
|
||||
}
|
||||
</code>
|
||||
|
||||
<p>Alse note that you have to specify the ROA channel. If you want to import
|
||||
<p>Alse note that you have to specify the ROA and ASPA channels. If you want to import
|
||||
only IPv4 prefixes you have to specify only roa4 channel. Similarly with IPv6
|
||||
prefixes only. If you want to fetch both IPv4 and even IPv6 ROAs you have to
|
||||
specify both channels.
|
||||
@ -5752,15 +5865,37 @@ specify both channels.
|
||||
instead. This may be useful for implementing loose RPKI check for
|
||||
blackholes. Default: disabled.
|
||||
|
||||
<tag>transport tcp</tag> Unprotected transport over TCP. It's a default
|
||||
transport. Should be used only on secure private networks.
|
||||
Default: tcp
|
||||
<tag>min version <m/num/</tag>
|
||||
Minimal allowed version of the RTR protocol. BIRD will refuse to
|
||||
downgrade a connection below this version and drop the session instead.
|
||||
Default: 0
|
||||
|
||||
<tag>max version <m/num/</tag>
|
||||
Maximal allowed version of the RTR protocol. BIRD will start with this
|
||||
version. Use this option if sending version 2 to your cache causes
|
||||
problems. Default: 2
|
||||
|
||||
<tag>transport tcp { <m/TCP transport options.../ }</tag> Transport over
|
||||
TCP, it's the default transport. Cannot be combined with a SSH transport.
|
||||
Default: TCP, no authentication.
|
||||
|
||||
<tag>transport ssh { <m/SSH transport options.../ }</tag> It enables a
|
||||
SSHv2 transport encryption. Cannot be combined with a TCP transport.
|
||||
Default: off
|
||||
</descrip>
|
||||
|
||||
<sect3>TCP transport options
|
||||
<p>
|
||||
<descrip>
|
||||
<tag>authentication none|md5</tag>
|
||||
Select authentication method to be used. <cf/none/ means no
|
||||
authentication, <cf/md5/ is TCP-MD5 authentication (<rfc id="2385">).
|
||||
Default: no authentication.
|
||||
|
||||
<tag>password "<m>text</m>"</tag>
|
||||
Use this password for TCP-MD5 authentication of the RPKI-To-Router session.
|
||||
</descrip>
|
||||
|
||||
<sect3>SSH transport options
|
||||
<p>
|
||||
<descrip>
|
||||
@ -5966,6 +6101,13 @@ options (<cf/bfd/ and <cf/weight 1/), the second nexthop has just <cf/weight 2/.
|
||||
|
||||
<p>The ROA config is just <cf>route <m/prefix/ max <m/int/ as <m/int/</cf> with no nexthop.
|
||||
|
||||
<sect1>Autonomous System Provider Authorization
|
||||
|
||||
<p>The ASPA config is <cf>route aspa <m/int/ providers <m/int/ [, <m/int/ ...]</cf> with no nexthop.
|
||||
The first ASN is client and the following are a list of providers.
|
||||
For a transit, you can also write <cf>route aspa <m/int/ transit</cf> to get
|
||||
the no-provider ASPA.
|
||||
|
||||
<sect1>Flowspec
|
||||
<label id="flowspec-network-type">
|
||||
|
||||
|
@ -60,6 +60,11 @@ the given prefix. Experimental.
|
||||
|
||||
Reload of filters is now done by `reload filters` command, contrary to just `reload` in BIRD 2.
|
||||
|
||||
## Filters
|
||||
|
||||
We have removed the exception for `case` where multiple commands could be written
|
||||
after the case label without braces. This caused unneeded complexity in the parser.
|
||||
|
||||
## Route attributes
|
||||
|
||||
All protocol attributes have been renamed in CLI to align with the filter language tokens.
|
||||
@ -72,6 +77,11 @@ how to implement it properly.
|
||||
|
||||
The `scope` route attribute has been removed. Use custom route attributes instead.
|
||||
|
||||
## Protocols common
|
||||
|
||||
There is now a guard against too frequent restarts due to limits, called
|
||||
`restart time`, set by default to 5 seconds. To disable, set this to 1 us.
|
||||
|
||||
## Pipe
|
||||
|
||||
It's now impossible to check immediately whether the route has entered a pipe
|
||||
|
@ -77,3 +77,4 @@ Reply codes of BIRD command-line interface
|
||||
9000 Command too long
|
||||
9001 Parse error
|
||||
9002 Invalid symbol type
|
||||
9003 Argument too long
|
||||
|
@ -362,13 +362,13 @@ CF_DECLS
|
||||
|
||||
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||
ACCEPT, REJECT, ERROR,
|
||||
INT, BOOL, IP, PREFIX, RD, PAIR, QUAD, EC, LC,
|
||||
INT, BOOL, IP, PREFIX, RD, PAIR, QUAD, EC, LC, ENUM,
|
||||
SET, STRING, BYTESTRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
|
||||
IF, THEN, ELSE, CASE,
|
||||
FOR, IN, DO,
|
||||
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
|
||||
FROM, GW, NET, PROTO, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
|
||||
ROA_CHECK,
|
||||
ROA_CHECK, ASPA_CHECK,
|
||||
DEFINED,
|
||||
ADD, DELETE, RESET,
|
||||
PREPEND,
|
||||
@ -381,7 +381,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||
%nonassoc ELSE
|
||||
|
||||
%type <xp> cmds_int cmd_prep
|
||||
%type <x> term term_bs cmd cmd_var cmds cmds_scoped constant constructor var var_list var_list_r function_call bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont
|
||||
%type <x> term term_bs cmd cmd_var cmds constant constructor var var_list var_list_r function_call bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont
|
||||
%type <fsa> static_attr
|
||||
%type <f> filter where_filter
|
||||
%type <fl> filter_body function_body
|
||||
@ -393,7 +393,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||
%type <i32> cnum
|
||||
%type <e> pair_item ec_item lc_item set_item switch_item ec_items set_items switch_items switch_body
|
||||
%type <trie> fprefix_set
|
||||
%type <v> set_atom switch_atom fipa
|
||||
%type <v> set_atom0 set_atom switch_atom fipa
|
||||
%type <px> fprefix
|
||||
%type <t> get_cf_position
|
||||
%type <s> for_var
|
||||
@ -498,6 +498,7 @@ type:
|
||||
case T_INT:
|
||||
case T_PAIR:
|
||||
case T_QUAD:
|
||||
case T_ENUM:
|
||||
case T_EC:
|
||||
case T_LC:
|
||||
case T_RD:
|
||||
@ -513,6 +514,13 @@ type:
|
||||
cf_error( "You can't create sets of this type." );
|
||||
}
|
||||
}
|
||||
| ENUM
|
||||
{ $<i>$ = cf_maybe_exit_filters(); }
|
||||
enum_type
|
||||
{
|
||||
if ($<i>2) cf_enter_filters();
|
||||
$$ = $3;
|
||||
}
|
||||
;
|
||||
|
||||
function_argsn:
|
||||
@ -625,8 +633,6 @@ cmds: /* EMPTY */ { $$ = NULL; }
|
||||
| cmds_int { $$ = $1.begin; }
|
||||
;
|
||||
|
||||
cmds_scoped: { cf_push_soft_scope(new_config); } cmds { cf_pop_soft_scope(new_config); $$ = $2; } ;
|
||||
|
||||
cmd_var: var | cmd ;
|
||||
|
||||
cmd_prep: cmd_var {
|
||||
@ -668,27 +674,30 @@ fipa:
|
||||
* as a function call in switch case cmds.
|
||||
*/
|
||||
|
||||
set_atom:
|
||||
set_atom0:
|
||||
NUM { $$.type = T_INT; $$.val.i = $1; }
|
||||
| fipa { $$ = $1; }
|
||||
| VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
|
||||
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
|
||||
| ENUM_TOKEN { $$.type = pair_a($1); $$.val.i = pair_b($1); }
|
||||
| '(' term ')' {
|
||||
$$ = cf_eval_tmp($2, T_VOID);
|
||||
if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
|
||||
if (!f_valid_set_type($$.type))
|
||||
cf_error("Set-incompatible type (%s)", f_type_name($$.type));
|
||||
}
|
||||
;
|
||||
|
||||
set_atom:
|
||||
set_atom0
|
||||
| CF_SYM_KNOWN {
|
||||
cf_assert_symbol($1, SYM_CONSTANT);
|
||||
if (!f_valid_set_type(SYM_TYPE($1))) cf_error("%s: set-incompatible type", $1->name);
|
||||
if (!f_valid_set_type(SYM_TYPE($1)))
|
||||
cf_error("%s: Set-incompatible type (%s)", $1->name, f_type_name(SYM_TYPE($1)));
|
||||
$$ = *$1->val;
|
||||
}
|
||||
;
|
||||
|
||||
switch_atom:
|
||||
NUM { $$.type = T_INT; $$.val.i = $1; }
|
||||
| '(' term ')' { $$ = cf_eval_tmp($2, T_INT); }
|
||||
| fipa { $$ = $1; }
|
||||
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
|
||||
set_atom0
|
||||
;
|
||||
|
||||
cnum:
|
||||
@ -781,14 +790,14 @@ fprefix_set:
|
||||
;
|
||||
|
||||
switch_body: /* EMPTY */ { $$ = NULL; }
|
||||
| switch_body switch_items ':' cmds_scoped {
|
||||
| switch_body switch_items ':' cmd {
|
||||
/* Fill data fields */
|
||||
struct f_tree *t;
|
||||
for (t = $2; t; t = t->left)
|
||||
t->data = $4;
|
||||
$$ = f_merge_items($1, $2);
|
||||
}
|
||||
| switch_body ELSECOL cmds_scoped {
|
||||
| switch_body ELSECOL cmd {
|
||||
struct f_tree *t = f_new_tree();
|
||||
t->from.type = t->to.type = T_VOID;
|
||||
t->right = t;
|
||||
@ -837,7 +846,7 @@ constant:
|
||||
DBG( "ook\n" );
|
||||
}
|
||||
| '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PREFIX_SET, .val.ti = $2, }); }
|
||||
| ENUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = $1 >> 16, .val.i = $1 & 0xffff, }); }
|
||||
| ENUM_TOKEN { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = $1 >> 16, .val.i = $1 & 0xffff, }); }
|
||||
;
|
||||
|
||||
constructor:
|
||||
@ -956,6 +965,7 @@ term:
|
||||
|
||||
| ROA_CHECK '(' rtable ')' { $$ = f_implicit_roa_check($3); }
|
||||
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK, $5, $7, $3); }
|
||||
| ASPA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ASPA_CHECK_EXPLICIT, $5, $7, $3); }
|
||||
|
||||
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
|
||||
|
||||
@ -990,9 +1000,7 @@ for_var:
|
||||
;
|
||||
|
||||
cmd:
|
||||
'{' cmds_scoped '}' {
|
||||
$$ = $2;
|
||||
}
|
||||
'{' { cf_push_soft_scope(new_config); } cmds { cf_pop_soft_scope(new_config); } '}' { $$ = $3; }
|
||||
| IF term THEN cmd {
|
||||
$$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
|
||||
}
|
||||
|
@ -43,9 +43,11 @@ static const char * const f_type_str[] = {
|
||||
[T_ENUM_SCOPE] = "enum scope",
|
||||
[T_ENUM_RTD] = "enum rtd",
|
||||
[T_ENUM_ROA] = "enum roa",
|
||||
[T_ENUM_NETTYPE] = "enum nettype",
|
||||
[T_ENUM_ASPA] = "enum aspa",
|
||||
[T_ENUM_NET_TYPE] = "enum net_type",
|
||||
[T_ENUM_RA_PREFERENCE] = "enum ra_preference",
|
||||
[T_ENUM_AF] = "enum af",
|
||||
[T_ENUM_MPLS_POLICY] = "enum mpls_policy",
|
||||
|
||||
[T_IP] = "ip",
|
||||
[T_NET] = "prefix",
|
||||
|
@ -622,12 +622,19 @@ FID_WR_PUT(11)
|
||||
#pragma GCC diagnostic ignored "-Woverride-init"
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Winitializer-overrides"
|
||||
#endif
|
||||
|
||||
static struct sym_scope f_type_method_scopes[] = {
|
||||
FID_WR_PUT(12)
|
||||
};
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 6
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
@ -503,7 +503,7 @@
|
||||
RESULT(T_BOOL, i, (v1.type != T_VOID) && !val_is_undefined(v1));
|
||||
}
|
||||
|
||||
METHOD_R(T_NET, type, T_ENUM_NETTYPE, i, v1.val.net->type);
|
||||
METHOD_R(T_NET, type, T_ENUM_NET_TYPE, i, v1.val.net->type);
|
||||
METHOD_R(T_IP, is_v4, T_BOOL, i, ipa_is_ip4(v1.val.ip));
|
||||
|
||||
/* Add initialized variable */
|
||||
@ -886,6 +886,8 @@
|
||||
}]]);
|
||||
}
|
||||
}
|
||||
else if (da->empty)
|
||||
RESULT_VAL(da->empty(da));
|
||||
else if ((empty = f_get_empty(da->type)).type != T_VOID)
|
||||
RESULT_VAL(empty);
|
||||
else
|
||||
@ -1520,6 +1522,22 @@
|
||||
|
||||
}
|
||||
|
||||
INST(FI_ASPA_CHECK_EXPLICIT, 2, 1) { /* ASPA Check */
|
||||
NEVER_CONSTANT;
|
||||
ARG(1, T_PATH);
|
||||
ARG(2, T_BOOL);
|
||||
RTC(3);
|
||||
rtable *table = rtc->table;
|
||||
|
||||
if (!table)
|
||||
runtime("Missing ASPA table");
|
||||
|
||||
if (table->addr_type != NET_ASPA)
|
||||
runtime("Table type must be ASPA");
|
||||
|
||||
RESULT(T_ENUM_ASPA, i, [[ aspa_check(table, v1.val.ad, v2.val.i) ]]);
|
||||
}
|
||||
|
||||
INST(FI_FROM_HEX, 1, 1) { /* Convert hex text to bytestring */
|
||||
ARG(1, T_STRING);
|
||||
|
||||
|
@ -112,6 +112,17 @@ attribute lclist test_ca_lclist_max19;
|
||||
attribute lclist test_ca_lclist_max20;
|
||||
attribute lclist test_ca_lclist_max21;
|
||||
|
||||
attribute enum rts test_ca_rts;
|
||||
attribute enum rtd test_ca_rtd;
|
||||
attribute enum scope test_ca_scope;
|
||||
attribute enum roa test_ca_roa;
|
||||
attribute enum aspa test_ca_aspa;
|
||||
attribute enum af test_ca_af;
|
||||
attribute enum net_type test_ca_nettype;
|
||||
attribute enum bgp_origin test_ca_bgp_origin;
|
||||
attribute enum ra_preference test_ca_ra_preference;
|
||||
attribute enum mpls_policy test_ca_mpls_policy;
|
||||
attribute enum net_type set test_ca_nettype_set;
|
||||
|
||||
/* Uncomment this to get an error */
|
||||
#attribute int bgp_path;
|
||||
@ -195,8 +206,11 @@ bt_test_suite(t_bool, "Testing boolean expressions");
|
||||
|
||||
function aux_t_int(int t; int u)
|
||||
{
|
||||
int v;
|
||||
case t {
|
||||
1: {}
|
||||
2: { int u; u = 1; v = 42; }
|
||||
3: if true then v = t + u + 53; else v = 35 + u + t;
|
||||
else: {}
|
||||
}
|
||||
}
|
||||
@ -249,7 +263,7 @@ function t_int()
|
||||
}
|
||||
|
||||
case four {
|
||||
4: bt_assert(true);
|
||||
(2+2): bt_assert(true);
|
||||
else: bt_assert(false);
|
||||
}
|
||||
|
||||
@ -582,12 +596,31 @@ bt_test_suite(t_ip_set, "Testing sets of ip address");
|
||||
|
||||
function t_enum()
|
||||
{
|
||||
enum rts ev0 = RTS_STATIC;
|
||||
enum rtd ev1 = RTD_UNICAST;
|
||||
enum scope ev2 = SCOPE_UNIVERSE;
|
||||
enum roa ev3 = ROA_VALID;
|
||||
enum aspa ev4 = ASPA_VALID;
|
||||
enum af ev5 = AF_IPV6;
|
||||
enum net_type ev6 = NET_IP6;
|
||||
enum bgp_origin ev7 = ORIGIN_IGP;
|
||||
enum ra_preference ev8 = RA_PREF_LOW;
|
||||
enum mpls_policy ev9 = MPLS_POLICY_STATIC;
|
||||
|
||||
enum net_type set es = [NET_IP6, NET_VPN6];
|
||||
|
||||
bt_assert(format(RTS_STATIC) = "(enum 31)1");
|
||||
bt_assert(format(NET_IP4) = "(enum 3b)1");
|
||||
bt_assert(format(NET_VPN6) = "(enum 3b)4");
|
||||
bt_assert(format(ev6) = "(enum 3b)2");
|
||||
|
||||
bt_assert(ev0 = RTS_STATIC);
|
||||
bt_assert(ev6 = NET_IP6);
|
||||
|
||||
bt_assert(RTS_STATIC ~ [RTS_STATIC, RTS_DEVICE]);
|
||||
bt_assert(RTS_BGP !~ [RTS_STATIC, RTS_DEVICE]);
|
||||
bt_assert(NET_IP6 ~ es);
|
||||
bt_assert(NET_IP4 !~ es);
|
||||
}
|
||||
|
||||
bt_test_suite(t_enum, "Testing enums");
|
||||
@ -2298,9 +2331,9 @@ bool t;
|
||||
krt_source = 17;
|
||||
krt_metric = 19;
|
||||
|
||||
# krt_lock_mtu = false;
|
||||
# if krt_lock_congctl then krt_lock_mtu = false;
|
||||
# krt_lock_window = true;
|
||||
# krt_lock_rtt = krt_lock_rttvar && krt_lock_sstresh || krt_lock_cwnd;
|
||||
# krt_lock_rtt = krt_lock_rttvar && krt_lock_ssthresh || krt_lock_cwnd;
|
||||
|
||||
accept "ok I take that";
|
||||
}
|
||||
@ -2381,7 +2414,56 @@ prefix pfx;
|
||||
bt_test_suite(t_roa_check, "Testing ROA");
|
||||
|
||||
|
||||
aspa table at;
|
||||
|
||||
protocol static
|
||||
{
|
||||
aspa { table at; };
|
||||
route aspa 65540 provider 65544;
|
||||
route aspa 65541 provider 65545;
|
||||
route aspa 65542 provider 65544, 65545;
|
||||
route aspa 65543 provider 65544, 65545;
|
||||
route aspa 65544 transit;
|
||||
route aspa 65545 transit;
|
||||
route aspa 65550 provider 65540;
|
||||
route aspa 65551 provider 65543;
|
||||
}
|
||||
|
||||
function t_aspa_check()
|
||||
{
|
||||
bgppath p1 = +empty+;
|
||||
p1.prepend(65540);
|
||||
p1.prepend(65544);
|
||||
bt_assert(aspa_check(at, p1, false) = ASPA_VALID);
|
||||
bt_assert(aspa_check(at, p1, true) = ASPA_VALID);
|
||||
|
||||
p1.prepend(65542);
|
||||
bt_assert(aspa_check(at, p1, false) = ASPA_VALID);
|
||||
bt_assert(aspa_check(at, p1, true) = ASPA_INVALID);
|
||||
|
||||
p1.prepend(65555);
|
||||
bt_assert(aspa_check(at, p1, false) = ASPA_UNKNOWN);
|
||||
bt_assert(aspa_check(at, p1, true) = ASPA_INVALID);
|
||||
|
||||
bgppath p2 = +empty+;
|
||||
p2.prepend(65554);
|
||||
p2.prepend(65541);
|
||||
p2.prepend(65545);
|
||||
bt_assert(aspa_check(at, p2, false) = ASPA_UNKNOWN);
|
||||
bt_assert(aspa_check(at, p2, true) = ASPA_UNKNOWN);
|
||||
|
||||
p2.prepend(65543);
|
||||
bt_assert(aspa_check(at, p2, false) = ASPA_UNKNOWN);
|
||||
bt_assert(aspa_check(at, p2, true) = ASPA_INVALID);
|
||||
|
||||
bgppath p3 = +empty+;
|
||||
p3.prepend(65541);
|
||||
p3.prepend(65544);
|
||||
bt_assert(aspa_check(at, p3, false) = ASPA_INVALID);
|
||||
bt_assert(aspa_check(at, p3, true) = ASPA_INVALID);
|
||||
}
|
||||
|
||||
bt_test_suite(t_aspa_check, "Testing ASPA");
|
||||
|
||||
filter vpn_filter
|
||||
{
|
||||
|
18
lib/a-set.c
18
lib/a-set.c
@ -34,7 +34,7 @@
|
||||
* the buffer to indicate truncation.
|
||||
*/
|
||||
int
|
||||
int_set_format(const struct adata *set, int way, int from, byte *buf, uint size)
|
||||
int_set_format(const struct adata *set, enum isf_way way, int from, byte *buf, uint size)
|
||||
{
|
||||
u32 *z = (u32 *) set->data;
|
||||
byte *end = buf + size - 24;
|
||||
@ -56,10 +56,18 @@ int_set_format(const struct adata *set, int way, int from, byte *buf, uint size)
|
||||
if (i > from2)
|
||||
*buf++ = ' ';
|
||||
|
||||
if (way)
|
||||
buf += bsprintf(buf, "(%d,%d)", z[i] >> 16, z[i] & 0xffff);
|
||||
else
|
||||
buf += bsprintf(buf, "%R", z[i]);
|
||||
switch (way)
|
||||
{
|
||||
case ISF_COMMUNITY_LIST:
|
||||
buf += bsprintf(buf, "(%d,%d)", z[i] >> 16, z[i] & 0xffff);
|
||||
break;
|
||||
case ISF_ROUTER_ID:
|
||||
buf += bsprintf(buf, "%R", z[i]);
|
||||
break;
|
||||
case ISF_NUMBERS:
|
||||
buf += bsprintf(buf, "%u", z[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
*buf = 0;
|
||||
return 0;
|
||||
|
@ -92,11 +92,11 @@ t_set_int_union(void)
|
||||
const struct adata *set_union;
|
||||
set_union = int_set_union(tmp_linpool, set_sequence, set_sequence_same);
|
||||
bt_assert(int_set_get_size(set_union) == SET_SIZE);
|
||||
bt_assert(int_set_format(set_union, 0, 2, buf, T_BUFFER_SIZE) == 0);
|
||||
bt_assert(int_set_format(set_union, ISF_ROUTER_ID, 2, buf, T_BUFFER_SIZE) == 0);
|
||||
|
||||
set_union = int_set_union(tmp_linpool, set_sequence, set_sequence_higher);
|
||||
bt_assert_msg(int_set_get_size(set_union) == SET_SIZE*2, "int_set_get_size(set_union) %d, SET_SIZE*2 %d", int_set_get_size(set_union), SET_SIZE*2);
|
||||
bt_assert(int_set_format(set_union, 0, 2, buf, T_BUFFER_SIZE) == 0);
|
||||
bt_assert(int_set_format(set_union, ISF_ROUTER_ID, 2, buf, T_BUFFER_SIZE) == 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -106,17 +106,21 @@ t_set_int_format(void)
|
||||
{
|
||||
generate_set_sequence(SET_TYPE_INT, SET_SIZE_FOR_FORMAT_OUTPUT);
|
||||
|
||||
bt_assert(int_set_format(set_sequence, 0, 0, buf, T_BUFFER_SIZE) == 0);
|
||||
bt_assert(int_set_format(set_sequence, ISF_ROUTER_ID, 0, buf, T_BUFFER_SIZE) == 0);
|
||||
bt_assert(strcmp(buf, "0.0.0.0 0.0.0.1 0.0.0.2 0.0.0.3 0.0.0.4 0.0.0.5 0.0.0.6 0.0.0.7 0.0.0.8 0.0.0.9") == 0);
|
||||
|
||||
bzero(buf, T_BUFFER_SIZE);
|
||||
bt_assert(int_set_format(set_sequence, 0, 2, buf, T_BUFFER_SIZE) == 0);
|
||||
bt_assert(int_set_format(set_sequence, ISF_ROUTER_ID, 2, buf, T_BUFFER_SIZE) == 0);
|
||||
bt_assert(strcmp(buf, "0.0.0.2 0.0.0.3 0.0.0.4 0.0.0.5 0.0.0.6 0.0.0.7 0.0.0.8 0.0.0.9") == 0);
|
||||
|
||||
bzero(buf, T_BUFFER_SIZE);
|
||||
bt_assert(int_set_format(set_sequence, 1, 0, buf, T_BUFFER_SIZE) == 0);
|
||||
bt_assert(int_set_format(set_sequence, ISF_COMMUNITY_LIST, 0, buf, T_BUFFER_SIZE) == 0);
|
||||
bt_assert(strcmp(buf, "(0,0) (0,1) (0,2) (0,3) (0,4) (0,5) (0,6) (0,7) (0,8) (0,9)") == 0);
|
||||
|
||||
bzero(buf, T_BUFFER_SIZE);
|
||||
bt_assert(int_set_format(set_sequence, ISF_NUMBERS, 0, buf, T_BUFFER_SIZE) == 0);
|
||||
bt_assert(strcmp(buf, "0 1 2 3 4 5 6 7 8 9") == 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
13
lib/attrs.h
13
lib/attrs.h
@ -73,7 +73,7 @@ struct adata *as_path_prepend2(struct linpool *pool, const struct adata *op, int
|
||||
struct adata *as_path_to_old(struct linpool *pool, const struct adata *path);
|
||||
struct adata *as_path_cut(struct linpool *pool, const struct adata *path, uint num);
|
||||
const struct adata *as_path_merge(struct linpool *pool, const struct adata *p1, const struct adata *p2);
|
||||
void as_path_format(const struct adata *path, byte *buf, uint size);
|
||||
void as_path_format(const struct adata *path, byte *buf, uint size) ACCESS_WRITE(2, 3);
|
||||
int as_path_getlen(const struct adata *path);
|
||||
int as_path_getlen_int(const struct adata *path, int bs);
|
||||
int as_path_get_first(const struct adata *path, u32 *orig_as);
|
||||
@ -238,12 +238,17 @@ static inline int lc_match(const u32 *l, int i, lcomm v)
|
||||
static inline u32 *lc_copy(u32 *dst, const u32 *src)
|
||||
{ memcpy(dst, src, LCOMM_LENGTH); return dst + 3; }
|
||||
|
||||
enum isf_way {
|
||||
ISF_NUMBERS,
|
||||
ISF_COMMUNITY_LIST,
|
||||
ISF_ROUTER_ID,
|
||||
};
|
||||
|
||||
int int_set_format(const struct adata *set, int way, int from, byte *buf, uint size);
|
||||
int int_set_format(const struct adata *set, enum isf_way way, int from, byte *buf, uint size) ACCESS_WRITE(4, 5);
|
||||
int ec_format(byte *buf, u64 ec);
|
||||
int ec_set_format(const struct adata *set, int from, byte *buf, uint size);
|
||||
int ec_set_format(const struct adata *set, int from, byte *buf, uint size) ACCESS_WRITE(3, 4);
|
||||
int lc_format(byte *buf, lcomm lc);
|
||||
int lc_set_format(const struct adata *set, int from, byte *buf, uint size);
|
||||
int lc_set_format(const struct adata *set, int from, byte *buf, uint size) ACCESS_WRITE(3, 4);
|
||||
int int_set_contains(const struct adata *list, u32 val);
|
||||
int ec_set_contains(const struct adata *list, u64 val);
|
||||
int lc_set_contains(const struct adata *list, lcomm val);
|
||||
|
@ -114,9 +114,20 @@ static inline int u64_cmp(u64 i1, u64 i2)
|
||||
#define USE_RESULT __atribute__((warn_unused_result))
|
||||
#define UNUSED __attribute__((unused))
|
||||
#define PACKED __attribute__((packed))
|
||||
#define NONNULL(...) __attribute__((nonnull((__VA_ARGS__))))
|
||||
#define NONNULL(...) __attribute__((nonnull(__VA_ARGS__)))
|
||||
#define ALLOC_SIZE(...) __attribute__((alloc_size(__VA_ARGS__)))
|
||||
#define CLEANUP(fun) __attribute__((cleanup(fun)))
|
||||
|
||||
#if __GNUC__ >= 10
|
||||
#define ACCESS_READ(...) __attribute__((access(read_only, __VA_ARGS__)))
|
||||
#define ACCESS_WRITE(...) __attribute__((access(write_only, __VA_ARGS__)))
|
||||
#define ACCESS_RW(...) __attribute__((access(read_write, __VA_ARGS__)))
|
||||
#else
|
||||
#define ACCESS_READ(...)
|
||||
#define ACCESS_WRITE(...)
|
||||
#define ACCESS_RW(...)
|
||||
#endif
|
||||
|
||||
#define STATIC_ASSERT(EXP) _Static_assert(EXP, #EXP)
|
||||
#define STATIC_ASSERT_MSG(EXP,MSG) _Static_assert(EXP, MSG)
|
||||
|
||||
|
@ -293,13 +293,9 @@ flow_read_ip4_part(const byte *part)
|
||||
static inline ip6_addr
|
||||
flow_read_ip6(const byte *px, uint pxlen, uint pxoffset)
|
||||
{
|
||||
uint floor_offset = BYTES(pxoffset - (pxoffset % 8));
|
||||
uint ceil_len = BYTES(pxlen);
|
||||
ip6_addr ip = IP6_NONE;
|
||||
|
||||
memcpy(((byte *) &ip) + floor_offset, px, ceil_len - floor_offset);
|
||||
|
||||
return ip6_ntoh(ip);
|
||||
memcpy(&ip, px, BYTES(pxlen - pxoffset));
|
||||
return ip6_shift_right(ip6_ntoh(ip), pxoffset);
|
||||
}
|
||||
|
||||
ip6_addr
|
||||
@ -476,7 +472,7 @@ flow_validate(const byte *nlri, uint len, int ipv6)
|
||||
uint pxoffset = *pos++;
|
||||
if (pxoffset > IP6_MAX_PREFIX_LENGTH || pxoffset > pxlen)
|
||||
return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET;
|
||||
bytes -= pxoffset / 8;
|
||||
bytes = BYTES(pxlen - pxoffset);
|
||||
}
|
||||
pos += bytes;
|
||||
|
||||
@ -749,12 +745,12 @@ flow_builder6_add_pfx(struct flow_builder *fb, const net_addr_ip6 *n6, u32 pxoff
|
||||
if (!builder_add_prepare(fb))
|
||||
return 0;
|
||||
|
||||
ip6_addr ip6 = ip6_hton(n6->prefix);
|
||||
ip6_addr ip6 = ip6_hton(ip6_shift_left(n6->prefix, pxoffset));
|
||||
|
||||
BUFFER_PUSH(fb->data) = fb->this_type;
|
||||
BUFFER_PUSH(fb->data) = n6->pxlen;
|
||||
BUFFER_PUSH(fb->data) = pxoffset;
|
||||
push_pfx_to_buffer(fb, BYTES(n6->pxlen) - (pxoffset / 8), ((byte *) &ip6) + (pxoffset / 8));
|
||||
push_pfx_to_buffer(fb, BYTES(n6->pxlen - pxoffset), ((byte *) &ip6));
|
||||
|
||||
builder_add_finish(fb);
|
||||
return 1;
|
||||
|
@ -9,21 +9,37 @@
|
||||
#include "test/birdtest.h"
|
||||
#include "lib/flowspec.h"
|
||||
|
||||
#define NET_ADDR_FLOW4_(what,prefix,pxlen,data_) \
|
||||
do \
|
||||
{ \
|
||||
what = alloca(sizeof(net_addr_flow4) + 128); \
|
||||
*what = NET_ADDR_FLOW4(prefix, pxlen, sizeof(data_)); \
|
||||
memcpy(what->data, &(data_), sizeof(data_)); \
|
||||
} while(0)
|
||||
#define NET_ADDR_FLOW4_(prefix,pxlen,nlri) \
|
||||
({ \
|
||||
uint _len = sizeof(nlri); \
|
||||
net_addr_flow4 *_n = tmp_alloc(sizeof(net_addr_flow4) + _len); \
|
||||
*_n = NET_ADDR_FLOW4(prefix, pxlen, _len); \
|
||||
memcpy(_n->data, &(nlri), _len); \
|
||||
if (_n->data[0] == 0) _n->data[0] = _len - 1; \
|
||||
_n; \
|
||||
})
|
||||
|
||||
#define NET_ADDR_FLOW6_(what,prefix,pxlen,data_) \
|
||||
do \
|
||||
{ \
|
||||
what = alloca(sizeof(net_addr_flow6) + 128); \
|
||||
*what = NET_ADDR_FLOW6(prefix, pxlen, sizeof(data_)); \
|
||||
memcpy(what->data, &(data_), sizeof(data_)); \
|
||||
} while(0)
|
||||
#define NET_ADDR_FLOW4_NLRI(...) \
|
||||
({ \
|
||||
const byte _nlri[] = { __VA_ARGS__ }; \
|
||||
NET_ADDR_FLOW4_(flow_read_ip4_part(_nlri + 1), _nlri[2], _nlri); \
|
||||
})
|
||||
|
||||
#define NET_ADDR_FLOW6_(prefix,pxlen,nlri) \
|
||||
({ \
|
||||
uint _len = sizeof(nlri); \
|
||||
net_addr_flow6 *_n = tmp_alloc(sizeof(net_addr_flow6) + _len); \
|
||||
*_n = NET_ADDR_FLOW6(prefix, pxlen, _len); \
|
||||
memcpy(_n->data, &(nlri), _len); \
|
||||
if (_n->data[0] == 0) _n->data[0] = _len - 1; \
|
||||
_n; \
|
||||
})
|
||||
|
||||
#define NET_ADDR_FLOW6_NLRI(...) \
|
||||
({ \
|
||||
const byte _nlri[] = { __VA_ARGS__ }; \
|
||||
NET_ADDR_FLOW6_(flow_read_ip6_part(_nlri + 1), _nlri[2], _nlri); \
|
||||
})
|
||||
|
||||
static int
|
||||
t_read_length(void)
|
||||
@ -67,13 +83,13 @@ t_write_length(void)
|
||||
static int
|
||||
t_first_part(void)
|
||||
{
|
||||
net_addr_flow4 *f;
|
||||
NET_ADDR_FLOW4_(f, ip4_build(10,0,0,1), 24, ((byte[]) { 0x00, 0x00, 0xab }));
|
||||
net_addr_flow4 *f = NET_ADDR_FLOW4_(IP4_NONE, 0, ((byte[]) { 0x00, 0x00, 0xab }));
|
||||
|
||||
const byte *under240 = &f->data[1];
|
||||
const byte *above240 = &f->data[2];
|
||||
|
||||
/* Case 0x00 0x00 */
|
||||
f->data[0] = 0x00;
|
||||
bt_assert(flow4_first_part(f) == NULL);
|
||||
|
||||
/* Case 0x01 0x00 */
|
||||
@ -103,15 +119,14 @@ t_first_part(void)
|
||||
static int
|
||||
t_iterators4(void)
|
||||
{
|
||||
net_addr_flow4 *f;
|
||||
NET_ADDR_FLOW4_(f, ip4_build(5,6,7,0), 24, ((byte[]) {
|
||||
const net_addr_flow4 *f = NET_ADDR_FLOW4_NLRI(
|
||||
25, /* Length */
|
||||
FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
|
||||
FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
|
||||
FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
|
||||
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
|
||||
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
|
||||
}));
|
||||
);
|
||||
|
||||
const byte *start = f->data;
|
||||
const byte *p1_dst_pfx = &f->data[1];
|
||||
@ -136,15 +151,14 @@ t_iterators4(void)
|
||||
static int
|
||||
t_iterators6(void)
|
||||
{
|
||||
net_addr_flow6 *f;
|
||||
NET_ADDR_FLOW6_(f, ip6_build(0,0,0x12345678,0x9a000000), 0x68, ((byte[]) {
|
||||
const net_addr_flow6 *f = NET_ADDR_FLOW6_NLRI(
|
||||
26, /* Length */
|
||||
FLOW_TYPE_DST_PREFIX, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a,
|
||||
FLOW_TYPE_SRC_PREFIX, 0x08, 0x0, 0xc0,
|
||||
FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
|
||||
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
|
||||
FLOW_TYPE_LABEL, 0x80, 0x55,
|
||||
}));
|
||||
);
|
||||
|
||||
const byte *start = f->data;
|
||||
const byte *p1_dst_pfx = &f->data[1];
|
||||
@ -169,15 +183,14 @@ t_iterators6(void)
|
||||
static int
|
||||
t_accessors4(void)
|
||||
{
|
||||
net_addr_flow4 *f;
|
||||
NET_ADDR_FLOW4_(f, ip4_build(5,6,7,0), 24, ((byte[]) {
|
||||
const net_addr_flow4 *f = NET_ADDR_FLOW4_NLRI(
|
||||
25, /* Length */
|
||||
FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
|
||||
FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
|
||||
FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
|
||||
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
|
||||
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
|
||||
}));
|
||||
);
|
||||
|
||||
const byte *p1_dst_px = &f->data[1];
|
||||
const ip4_addr p1_dst_addr = ip4_build(5,6,7,0);
|
||||
@ -194,15 +207,14 @@ t_accessors4(void)
|
||||
static int
|
||||
t_accessors6(void)
|
||||
{
|
||||
net_addr_flow6 *f;
|
||||
NET_ADDR_FLOW6_(f, ip6_build(0,0,0x12345678,0x9a000000), 0x68, ((byte[]) {
|
||||
const net_addr_flow6 *f = NET_ADDR_FLOW6_NLRI(
|
||||
26, /* Length */
|
||||
FLOW_TYPE_DST_PREFIX, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a,
|
||||
FLOW_TYPE_SRC_PREFIX, 0x08, 0x0, 0xc0,
|
||||
FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
|
||||
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
|
||||
FLOW_TYPE_LABEL, 0x80, 0x55,
|
||||
}));
|
||||
);
|
||||
|
||||
const byte *p1_dst_px = &f->data[1];
|
||||
const ip6_addr p1_dst_addr = ip6_build(0,0,0x12345678,0x9a000000);
|
||||
@ -450,17 +462,14 @@ t_builder4(void)
|
||||
|
||||
/* Expectation */
|
||||
|
||||
static byte nlri[] = {
|
||||
25,
|
||||
const net_addr_flow4 *expect = NET_ADDR_FLOW4_NLRI(
|
||||
0,
|
||||
FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
|
||||
FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
|
||||
FLOW_TYPE_IP_PROTOCOL, 0x80, 0x06,
|
||||
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
|
||||
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55
|
||||
};
|
||||
|
||||
net_addr_flow4 *expect;
|
||||
NET_ADDR_FLOW4_(expect, ip4_build(5, 6, 7, 0), 24, nlri);
|
||||
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
|
||||
);
|
||||
|
||||
/* Normal order */
|
||||
|
||||
@ -531,17 +540,14 @@ t_builder6(void)
|
||||
|
||||
/* Expectation */
|
||||
|
||||
byte nlri[] = {
|
||||
27,
|
||||
FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
|
||||
const net_addr_flow6 *expect = NET_ADDR_FLOW6_NLRI(
|
||||
0,
|
||||
FLOW_TYPE_DST_PREFIX, 103, 61, 0x22, 0x46, 0x8a, 0xcf, 0x13, 0x00,
|
||||
FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
|
||||
FLOW_TYPE_NEXT_HEADER, 0x80, 0x06,
|
||||
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
|
||||
FLOW_TYPE_LABEL, 0x80, 0x55,
|
||||
};
|
||||
|
||||
net_addr_flow6 *expect;
|
||||
NET_ADDR_FLOW6_(expect, ip6_build(0, 1, 0x12345678, 0x98000000), 103, nlri);
|
||||
);
|
||||
|
||||
/* Normal order */
|
||||
|
||||
@ -605,9 +611,11 @@ t_builder6(void)
|
||||
static int
|
||||
t_formatting4(void)
|
||||
{
|
||||
char b[1024];
|
||||
const net_addr_flow4 *input[4];
|
||||
const char *expect[4];
|
||||
|
||||
byte nlri[] = {
|
||||
expect[0] = "flow4 { dst 10.0.0.0/8; proto 23; dport > 24 && < 30 || 40..50,60..70,80 && >= 90; sport > 24 && < 30 || 40,50,60..70,80; icmp type 80; icmp code 90; tcp flags 0x3/0x3 && 0x0/0xc; length 0..65535; dscp 63; fragment dont_fragment || !is_fragment; }";
|
||||
input[0] = NET_ADDR_FLOW4_NLRI(
|
||||
0,
|
||||
FLOW_TYPE_DST_PREFIX, 0x08, 10,
|
||||
FLOW_TYPE_IP_PROTOCOL, 0x81, 23,
|
||||
@ -618,18 +626,44 @@ t_formatting4(void)
|
||||
FLOW_TYPE_TCP_FLAGS, 0x01, 0x03, 0xc2, 0x0c,
|
||||
FLOW_TYPE_PACKET_LENGTH, 0x03, 0, 0xd5, 0xff, 0xff,
|
||||
FLOW_TYPE_DSCP, 0x81, 63,
|
||||
FLOW_TYPE_FRAGMENT, 0x01, 0x01, 0x82, 0x02
|
||||
};
|
||||
*nlri = (u8) sizeof(nlri);
|
||||
FLOW_TYPE_FRAGMENT, 0x01, 0x01, 0x82, 0x02,
|
||||
);
|
||||
|
||||
net_addr_flow4 *input;
|
||||
NET_ADDR_FLOW4_(input, ip4_build(5, 6, 7, 0), 24, nlri);
|
||||
/* RFC 8955 4.3.1 Example 1 */
|
||||
expect[1] = "flow4 { dst 192.0.2.0/24; proto 6; port 25; }";
|
||||
input[1] = NET_ADDR_FLOW4_NLRI(
|
||||
0x0b,
|
||||
0x01, 0x18, 0xc0, 0x00, 0x02,
|
||||
0x03, 0x81, 0x06,
|
||||
0x04, 0x81, 0x19,
|
||||
);
|
||||
|
||||
const char *expect = "flow4 { dst 10.0.0.0/8; proto 23; dport > 24 && < 30 || 40..50,60..70,80 && >= 90; sport > 24 && < 30 || 40,50,60..70,80; icmp type 80; icmp code 90; tcp flags 0x3/0x3 && 0x0/0xc; length 0..65535; dscp 63; fragment dont_fragment || !is_fragment; }";
|
||||
/* RFC 8955 4.3.2 Example 2 */
|
||||
expect[2] = "flow4 { dst 192.0.2.0/24; src 203.0.113.0/24; port 137..139,8080; }";
|
||||
input[2] = NET_ADDR_FLOW4_NLRI(
|
||||
0x12,
|
||||
0x01, 0x18, 0xc0, 0x00, 0x02,
|
||||
0x02, 0x18, 0xcb, 0x00, 0x71,
|
||||
0x04, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
|
||||
);
|
||||
|
||||
bt_assert(flow4_net_format(b, sizeof(b), input) == strlen(expect));
|
||||
bt_debug(" expect: '%s',\n output: '%s'\n", expect, b);
|
||||
bt_assert(strcmp(b, expect) == 0);
|
||||
/* RFC 8955 4.3.3 Example 3 */
|
||||
expect[3] = "flow4 { dst 192.0.2.1/32; fragment !0x0/0x5; }";
|
||||
input[3] = NET_ADDR_FLOW4_NLRI(
|
||||
0x09,
|
||||
0x01, 0x20, 0xc0, 0x00, 0x02, 0x01,
|
||||
0x0c, 0x80, 0x05,
|
||||
);
|
||||
|
||||
/* Run the tests */
|
||||
for (uint i = 0; i < ARRAY_SIZE(input); i++)
|
||||
{
|
||||
char buf[1024];
|
||||
uint len = flow4_net_format(buf, sizeof(buf), input[i]);
|
||||
bt_debug(" expect: '%s',\n output: '%s'\n", expect[i], buf);
|
||||
bt_assert(!strcmp(buf, expect[i]));
|
||||
bt_assert(len == strlen(expect[i]));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -637,26 +671,46 @@ t_formatting4(void)
|
||||
static int
|
||||
t_formatting6(void)
|
||||
{
|
||||
char b[1024];
|
||||
const net_addr_flow6 *input[3];
|
||||
const char *expect[3];
|
||||
|
||||
byte nlri[] = {
|
||||
// (ip6_build(0, 1, 0x12345678, 0x98000000), 103, nlri0);
|
||||
expect[0] = "flow6 { dst ::1:1234:5678:9800:0/103 offset 61; src c000::/8; next header 6; port 20..40,273; label < 500000; }";
|
||||
input[0] = NET_ADDR_FLOW6_NLRI(
|
||||
0,
|
||||
FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
|
||||
FLOW_TYPE_DST_PREFIX, 103, 61, 0x22, 0x46, 0x8a, 0xcf, 0x13, 0x00,
|
||||
FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
|
||||
FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
|
||||
FLOW_TYPE_PORT, 0x03, 20, 0x45, 40, 0x91, 0x01, 0x11,
|
||||
FLOW_TYPE_LABEL, 0xa4, 0x00, 0x07, 0xa1, 0x20,
|
||||
};
|
||||
*nlri = (u8) sizeof(nlri);
|
||||
);
|
||||
|
||||
net_addr_flow6 *input;
|
||||
NET_ADDR_FLOW6_(input, ip6_build(0, 1, 0x12345678, 0x98000000), 103, nlri);
|
||||
/* RFC 8956 3.8.1 Example 1 */
|
||||
expect[1] = "flow6 { dst 2001:db8::/32; src ::1234:5678:9a00:0/104 offset 64; next header 6; }";
|
||||
input[1] = NET_ADDR_FLOW6_(ip6_build(0x20010db8, 0, 0, 0), 32, ((const byte[]) {
|
||||
0x12,
|
||||
0x01, 0x20, 0x00, 0x20, 0x01, 0x0d, 0xb8,
|
||||
0x02, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a,
|
||||
0x03, 0x81, 0x06,
|
||||
}));
|
||||
|
||||
const char *expect = "flow6 { dst ::1:1234:5678:9800:0/103 offset 61; src c000::/8; next header 6; port 20..40,273; label < 500000; }";
|
||||
/* RFC 8956 3.8.2 Example 2 */
|
||||
expect[2] = "flow6 { dst 2001:db8::/32; src ::1234:5678:9a00:0/104 offset 65; }";
|
||||
input[2] = NET_ADDR_FLOW6_(ip6_build(0x20010db8, 0, 0, 0), 32, ((const byte[]) {
|
||||
0x0f,
|
||||
0x01, 0x20, 0x00, 0x20, 0x01, 0x0d, 0xb8,
|
||||
0x02, 0x68, 0x41, 0x24, 0x68, 0xac, 0xf1, 0x34,
|
||||
}));
|
||||
|
||||
bt_assert(flow6_net_format(b, sizeof(b), input) == strlen(expect));
|
||||
bt_debug(" expect: '%s',\n output: '%s'\n", expect, b);
|
||||
bt_assert(strcmp(b, expect) == 0);
|
||||
/* Run the tests */
|
||||
for (uint i = 0; i < ARRAY_SIZE(input); i++)
|
||||
{
|
||||
char buf[1024];
|
||||
uint len = flow6_net_format(buf, sizeof(buf), input[i]);
|
||||
bt_debug(" expect: '%s',\n output: '%s'\n", expect[i], buf);
|
||||
bt_assert(!strcmp(buf, expect[i]));
|
||||
bt_assert(len == strlen(expect[i]));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
134
lib/ip.c
134
lib/ip.c
@ -156,6 +156,131 @@ ip6_classify(ip6_addr *a)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* IPv6 bit shifting
|
||||
*/
|
||||
|
||||
ip6_addr
|
||||
ip6_shift_left(ip6_addr a, uint bits)
|
||||
{
|
||||
if (bits == 0)
|
||||
return a;
|
||||
|
||||
if (bits > 127)
|
||||
return IP6_NONE;
|
||||
|
||||
int words = bits / 32;
|
||||
int rem = bits % 32;
|
||||
|
||||
for (int i = 0; i < 3 - words; i++)
|
||||
a.addr[i] = (a.addr[i + words] << rem) |
|
||||
(rem ? (a.addr[i + words + 1] >> (32 - rem)) : 0);
|
||||
|
||||
a.addr[3 - words] = a.addr[3] << rem;
|
||||
memset(&a.addr[4 - words], 0, words * 4);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
ip6_addr
|
||||
ip6_shift_right(ip6_addr a, uint bits)
|
||||
{
|
||||
if (bits == 0)
|
||||
return a;
|
||||
|
||||
if (bits > 127)
|
||||
return IP6_NONE;
|
||||
|
||||
int words = bits / 32;
|
||||
int rem = bits % 32;
|
||||
|
||||
for (int i = 3; i > words; i--)
|
||||
a.addr[i] = (a.addr[i - words] >> rem) |
|
||||
(rem ? (a.addr[i - words - 1] << (32 - rem)) : 0);
|
||||
|
||||
a.addr[words] = a.addr[0] >> rem;
|
||||
memset(&a.addr[0], 0, words * 4);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
static const char numbers[] = {
|
||||
'0', 0, 0, 0, '1', 0, 0, 0, '2', 0, 0, 0, '3', 0, 0, 0, '4', 0, 0, 0, '5', 0, 0, 0, '6', 0, 0, 0, '7', 0, 0, 0,
|
||||
'8', 0, 0, 0, '9', 0, 0, 0, '1', '0', 0, 0, '1', '1', 0, 0, '1', '2', 0, 0, '1', '3', 0, 0, '1', '4', 0, 0, '1', '5', 0, 0,
|
||||
'1', '6', 0, 0, '1', '7', 0, 0, '1', '8', 0, 0, '1', '9', 0, 0, '2', '0', 0, 0, '2', '1', 0, 0, '2', '2', 0, 0, '2', '3', 0, 0,
|
||||
'2', '4', 0, 0, '2', '5', 0, 0, '2', '6', 0, 0, '2', '7', 0, 0, '2', '8', 0, 0, '2', '9', 0, 0, '3', '0', 0, 0, '3', '1', 0, 0,
|
||||
'3', '2', 0, 0, '3', '3', 0, 0, '3', '4', 0, 0, '3', '5', 0, 0, '3', '6', 0, 0, '3', '7', 0, 0, '3', '8', 0, 0, '3', '9', 0, 0,
|
||||
'4', '0', 0, 0, '4', '1', 0, 0, '4', '2', 0, 0, '4', '3', 0, 0, '4', '4', 0, 0, '4', '5', 0, 0, '4', '6', 0, 0, '4', '7', 0, 0,
|
||||
'4', '8', 0, 0, '4', '9', 0, 0, '5', '0', 0, 0, '5', '1', 0, 0, '5', '2', 0, 0, '5', '3', 0, 0, '5', '4', 0, 0, '5', '5', 0, 0,
|
||||
'5', '6', 0, 0, '5', '7', 0, 0, '5', '8', 0, 0, '5', '9', 0, 0, '6', '0', 0, 0, '6', '1', 0, 0, '6', '2', 0, 0, '6', '3', 0, 0,
|
||||
'6', '4', 0, 0, '6', '5', 0, 0, '6', '6', 0, 0, '6', '7', 0, 0, '6', '8', 0, 0, '6', '9', 0, 0, '7', '0', 0, 0, '7', '1', 0, 0,
|
||||
'7', '2', 0, 0, '7', '3', 0, 0, '7', '4', 0, 0, '7', '5', 0, 0, '7', '6', 0, 0, '7', '7', 0, 0, '7', '8', 0, 0, '7', '9', 0, 0,
|
||||
'8', '0', 0, 0, '8', '1', 0, 0, '8', '2', 0, 0, '8', '3', 0, 0, '8', '4', 0, 0, '8', '5', 0, 0, '8', '6', 0, 0, '8', '7', 0, 0,
|
||||
'8', '8', 0, 0, '8', '9', 0, 0, '9', '0', 0, 0, '9', '1', 0, 0, '9', '2', 0, 0, '9', '3', 0, 0, '9', '4', 0, 0, '9', '5', 0, 0,
|
||||
'9', '6', 0, 0, '9', '7', 0, 0, '9', '8', 0, 0, '9', '9', 0, 0, '1', '0', '0', 0, '1', '0', '1', 0, '1', '0', '2', 0, '1', '0', '3', 0,
|
||||
'1', '0', '4', 0, '1', '0', '5', 0, '1', '0', '6', 0, '1', '0', '7', 0, '1', '0', '8', 0, '1', '0', '9', 0, '1', '1', '0', 0, '1', '1', '1', 0,
|
||||
'1', '1', '2', 0, '1', '1', '3', 0, '1', '1', '4', 0, '1', '1', '5', 0, '1', '1', '6', 0, '1', '1', '7', 0, '1', '1', '8', 0, '1', '1', '9', 0,
|
||||
'1', '2', '0', 0, '1', '2', '1', 0, '1', '2', '2', 0, '1', '2', '3', 0, '1', '2', '4', 0, '1', '2', '5', 0, '1', '2', '6', 0, '1', '2', '7', 0,
|
||||
'1', '2', '8', 0, '1', '2', '9', 0, '1', '3', '0', 0, '1', '3', '1', 0, '1', '3', '2', 0, '1', '3', '3', 0, '1', '3', '4', 0, '1', '3', '5', 0,
|
||||
'1', '3', '6', 0, '1', '3', '7', 0, '1', '3', '8', 0, '1', '3', '9', 0, '1', '4', '0', 0, '1', '4', '1', 0, '1', '4', '2', 0, '1', '4', '3', 0,
|
||||
'1', '4', '4', 0, '1', '4', '5', 0, '1', '4', '6', 0, '1', '4', '7', 0, '1', '4', '8', 0, '1', '4', '9', 0, '1', '5', '0', 0, '1', '5', '1', 0,
|
||||
'1', '5', '2', 0, '1', '5', '3', 0, '1', '5', '4', 0, '1', '5', '5', 0, '1', '5', '6', 0, '1', '5', '7', 0, '1', '5', '8', 0, '1', '5', '9', 0,
|
||||
'1', '6', '0', 0, '1', '6', '1', 0, '1', '6', '2', 0, '1', '6', '3', 0, '1', '6', '4', 0, '1', '6', '5', 0, '1', '6', '6', 0, '1', '6', '7', 0,
|
||||
'1', '6', '8', 0, '1', '6', '9', 0, '1', '7', '0', 0, '1', '7', '1', 0, '1', '7', '2', 0, '1', '7', '3', 0, '1', '7', '4', 0, '1', '7', '5', 0,
|
||||
'1', '7', '6', 0, '1', '7', '7', 0, '1', '7', '8', 0, '1', '7', '9', 0, '1', '8', '0', 0, '1', '8', '1', 0, '1', '8', '2', 0, '1', '8', '3', 0,
|
||||
'1', '8', '4', 0, '1', '8', '5', 0, '1', '8', '6', 0, '1', '8', '7', 0, '1', '8', '8', 0, '1', '8', '9', 0, '1', '9', '0', 0, '1', '9', '1', 0,
|
||||
'1', '9', '2', 0, '1', '9', '3', 0, '1', '9', '4', 0, '1', '9', '5', 0, '1', '9', '6', 0, '1', '9', '7', 0, '1', '9', '8', 0, '1', '9', '9', 0,
|
||||
'2', '0', '0', 0, '2', '0', '1', 0, '2', '0', '2', 0, '2', '0', '3', 0, '2', '0', '4', 0, '2', '0', '5', 0, '2', '0', '6', 0, '2', '0', '7', 0,
|
||||
'2', '0', '8', 0, '2', '0', '9', 0, '2', '1', '0', 0, '2', '1', '1', 0, '2', '1', '2', 0, '2', '1', '3', 0, '2', '1', '4', 0, '2', '1', '5', 0,
|
||||
'2', '1', '6', 0, '2', '1', '7', 0, '2', '1', '8', 0, '2', '1', '9', 0, '2', '2', '0', 0, '2', '2', '1', 0, '2', '2', '2', 0, '2', '2', '3', 0,
|
||||
'2', '2', '4', 0, '2', '2', '5', 0, '2', '2', '6', 0, '2', '2', '7', 0, '2', '2', '8', 0, '2', '2', '9', 0, '2', '3', '0', 0, '2', '3', '1', 0,
|
||||
'2', '3', '2', 0, '2', '3', '3', 0, '2', '3', '4', 0, '2', '3', '5', 0, '2', '3', '6', 0, '2', '3', '7', 0, '2', '3', '8', 0, '2', '3', '9', 0,
|
||||
'2', '4', '0', 0, '2', '4', '1', 0, '2', '4', '2', 0, '2', '4', '3', 0, '2', '4', '4', 0, '2', '4', '5', 0, '2', '4', '6', 0, '2', '4', '7', 0,
|
||||
'2', '4', '8', 0, '2', '4', '9', 0, '2', '5', '0', 0, '2', '5', '1', 0, '2', '5', '2', 0, '2', '5', '3', 0, '2', '5', '4', 0, '2', '5', '5', 0,
|
||||
};
|
||||
|
||||
static inline char *
|
||||
print_u8(char *s, u8 n)
|
||||
{
|
||||
memcpy(s, numbers + n * 4, 4);
|
||||
return s + 1 + (n >= 10) + (n >= 100);
|
||||
}
|
||||
|
||||
char *
|
||||
ip4_ntop(ip4_addr a, char *b)
|
||||
{
|
||||
u32 x = _I(a);
|
||||
|
||||
b = print_u8(b, (x >> 24) & 0xff);
|
||||
*b++ = '.';
|
||||
b = print_u8(b, (x >> 16) & 0xff);
|
||||
*b++ = '.';
|
||||
b = print_u8(b, (x >> 8) & 0xff);
|
||||
*b++ = '.';
|
||||
b = print_u8(b, x & 0xff);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
char *
|
||||
ip4_px_ntop(ip4_addr a, int len, char *b)
|
||||
{
|
||||
u32 x = _I(a);
|
||||
|
||||
b = print_u8(b, (x >> 24) & 0xff);
|
||||
*b++ = '.';
|
||||
b = print_u8(b, (x >> 16) & 0xff);
|
||||
*b++ = '.';
|
||||
b = print_u8(b, (x >> 8) & 0xff);
|
||||
*b++ = '.';
|
||||
b = print_u8(b, x & 0xff);
|
||||
*b++ = '/';
|
||||
b = print_u8(b, len & 0xff);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Conversion of IPv6 address to presentation format and vice versa.
|
||||
@ -163,15 +288,6 @@ ip6_classify(ip6_addr *a)
|
||||
* and of course by RFC 2373.
|
||||
*/
|
||||
|
||||
|
||||
char *
|
||||
ip4_ntop(ip4_addr a, char *b)
|
||||
{
|
||||
u32 x = _I(a);
|
||||
return b + bsprintf(b, "%d.%d.%d.%d", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff);
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
ip6_ntop(ip6_addr a, char *b)
|
||||
{
|
||||
|
10
lib/ip.h
10
lib/ip.h
@ -354,6 +354,9 @@ static inline ip4_addr ip4_setbits(ip4_addr a, uint pos, uint val)
|
||||
static inline ip6_addr ip6_setbits(ip6_addr a, uint pos, uint val)
|
||||
{ a.addr[pos / 32] |= val << (31 - pos % 32); return a; }
|
||||
|
||||
ip6_addr ip6_shift_left(ip6_addr a, uint bits);
|
||||
ip6_addr ip6_shift_right(ip6_addr a, uint bits);
|
||||
|
||||
|
||||
static inline ip4_addr ip4_opposite_m1(ip4_addr a)
|
||||
{ return _MI4(_I(a) ^ 1); }
|
||||
@ -398,7 +401,7 @@ typedef struct mpls_label_stack {
|
||||
u32 stack[MPLS_MAX_LABEL_STACK];
|
||||
} mpls_label_stack;
|
||||
|
||||
static inline int
|
||||
static inline int ACCESS_READ(1, 2)
|
||||
mpls_get(const char *buf, int buflen, u32 *stack)
|
||||
{
|
||||
for (int i=0; (i<MPLS_MAX_LABEL_STACK) && (i*4+3 < buflen); i++)
|
||||
@ -454,9 +457,14 @@ static inline void * put_ip6(void *buf, ip6_addr a)
|
||||
* Binary/text form conversions
|
||||
*/
|
||||
|
||||
#define IP4_BUFFER_SIZE 16 /* Required buffer for ip4_ntop() */
|
||||
#define IP4_PX_BUFFER_SIZE 20 /* Required buffer for ip4_ntop_px() */
|
||||
|
||||
char *ip4_ntop(ip4_addr a, char *b);
|
||||
char *ip6_ntop(ip6_addr a, char *b);
|
||||
|
||||
char *ip4_px_ntop(ip4_addr a, int len, char *b);
|
||||
|
||||
static inline char * ip4_ntox(ip4_addr a, char *b)
|
||||
{ return b + bsprintf(b, "%08x", _I(a)); }
|
||||
|
||||
|
@ -231,6 +231,66 @@ t_ip6_prefix_equal(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
t_ip6_shift_left(void)
|
||||
{
|
||||
ip6_addr a = ip6_build(0x8D0D8BDC, 0x1F04DB92, 0xE5117673, 0x70E54449);
|
||||
|
||||
struct { int n; ip6_addr val; } test_vectors[] = {
|
||||
0, ip6_build(0x8D0D8BDC, 0x1F04DB92, 0xE5117673, 0x70E54449),
|
||||
9, ip6_build(0x1B17B83E, 0x09B725CA, 0x22ECE6E1, 0xCA889200),
|
||||
18, ip6_build(0x2F707C13, 0x6E4B9445, 0xD9CDC395, 0x11240000),
|
||||
27, ip6_build(0xE0F826DC, 0x97288BB3, 0x9B872A22, 0x48000000),
|
||||
36, ip6_build(0xF04DB92E, 0x51176737, 0x0E544490, 0x00000000),
|
||||
45, ip6_build(0x9B725CA2, 0x2ECE6E1C, 0xA8892000, 0x00000000),
|
||||
54, ip6_build(0xE4B9445D, 0x9CDC3951, 0x12400000, 0x00000000),
|
||||
63, ip6_build(0x7288BB39, 0xB872A224, 0x80000000, 0x00000000),
|
||||
72, ip6_build(0x11767370, 0xE5444900, 0x00000000, 0x00000000),
|
||||
81, ip6_build(0xECE6E1CA, 0x88920000, 0x00000000, 0x00000000),
|
||||
90, ip6_build(0xCDC39511, 0x24000000, 0x00000000, 0x00000000),
|
||||
99, ip6_build(0x872A2248, 0x00000000, 0x00000000, 0x00000000),
|
||||
108, ip6_build(0x54449000, 0x00000000, 0x00000000, 0x00000000),
|
||||
117, ip6_build(0x89200000, 0x00000000, 0x00000000, 0x00000000),
|
||||
126, ip6_build(0x40000000, 0x00000000, 0x00000000, 0x00000000),
|
||||
128, ip6_build(0x00000000, 0x00000000, 0x00000000, 0x00000000),
|
||||
};
|
||||
|
||||
for (uint i = 0; i < ARRAY_SIZE(test_vectors); i++)
|
||||
bt_assert(ip6_equal(ip6_shift_left(a, test_vectors[i].n), test_vectors[i].val));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
t_ip6_shift_right(void)
|
||||
{
|
||||
ip6_addr a = ip6_build(0x8D0D8BDC, 0x1F04DB92, 0xE5117673, 0x70E54449);
|
||||
|
||||
struct { int n; ip6_addr val; } test_vectors[] = {
|
||||
0, ip6_build(0x8D0D8BDC, 0x1F04DB92, 0xE5117673, 0x70E54449),
|
||||
9, ip6_build(0x004686C5, 0xEE0F826D, 0xC97288BB, 0x39B872A2),
|
||||
18, ip6_build(0x00002343, 0x62F707C1, 0x36E4B944, 0x5D9CDC39),
|
||||
27, ip6_build(0x00000011, 0xA1B17B83, 0xE09B725C, 0xA22ECE6E),
|
||||
36, ip6_build(0x00000000, 0x08D0D8BD, 0xC1F04DB9, 0x2E511767),
|
||||
45, ip6_build(0x00000000, 0x0004686C, 0x5EE0F826, 0xDC97288B),
|
||||
54, ip6_build(0x00000000, 0x00000234, 0x362F707C, 0x136E4B94),
|
||||
63, ip6_build(0x00000000, 0x00000001, 0x1A1B17B8, 0x3E09B725),
|
||||
72, ip6_build(0x00000000, 0x00000000, 0x008D0D8B, 0xDC1F04DB),
|
||||
81, ip6_build(0x00000000, 0x00000000, 0x00004686, 0xC5EE0F82),
|
||||
90, ip6_build(0x00000000, 0x00000000, 0x00000023, 0x4362F707),
|
||||
99, ip6_build(0x00000000, 0x00000000, 0x00000000, 0x11A1B17B),
|
||||
108, ip6_build(0x00000000, 0x00000000, 0x00000000, 0x0008D0D8),
|
||||
117, ip6_build(0x00000000, 0x00000000, 0x00000000, 0x00000468),
|
||||
126, ip6_build(0x00000000, 0x00000000, 0x00000000, 0x00000002),
|
||||
128, ip6_build(0x00000000, 0x00000000, 0x00000000, 0x00000000),
|
||||
};
|
||||
|
||||
for (uint i = 0; i < ARRAY_SIZE(test_vectors); i++)
|
||||
bt_assert(ip6_equal(ip6_shift_right(a, test_vectors[i].n), test_vectors[i].val));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
@ -242,6 +302,8 @@ main(int argc, char *argv[])
|
||||
bt_test_suite(t_ip6_ntop, "Converting ip6_addr struct to IPv6 string");
|
||||
bt_test_suite(t_ip4_prefix_equal, "Testing ip4_prefix_equal()");
|
||||
bt_test_suite(t_ip6_prefix_equal, "Testing ip6_prefix_equal()");
|
||||
bt_test_suite(t_ip6_shift_left, "Testing ip6_shift_left()");
|
||||
bt_test_suite(t_ip6_shift_right, "Testing ip6_shift_right()");
|
||||
|
||||
return bt_exit_value();
|
||||
}
|
||||
|
17
lib/net.c
17
lib/net.c
@ -16,6 +16,7 @@ const char * const net_label[] = {
|
||||
[NET_FLOW6] = "flow6",
|
||||
[NET_IP6_SADR]= "ipv6-sadr",
|
||||
[NET_MPLS] = "mpls",
|
||||
[NET_ASPA] = "aspa",
|
||||
};
|
||||
|
||||
const u16 net_addr_length[] = {
|
||||
@ -29,6 +30,7 @@ const u16 net_addr_length[] = {
|
||||
[NET_FLOW6] = 0,
|
||||
[NET_IP6_SADR]= sizeof(net_addr_ip6_sadr),
|
||||
[NET_MPLS] = sizeof(net_addr_mpls),
|
||||
[NET_ASPA] = sizeof(net_addr_aspa),
|
||||
};
|
||||
|
||||
const u8 net_max_prefix_length[] = {
|
||||
@ -42,6 +44,7 @@ const u8 net_max_prefix_length[] = {
|
||||
[NET_FLOW6] = IP6_MAX_PREFIX_LENGTH,
|
||||
[NET_IP6_SADR]= IP6_MAX_PREFIX_LENGTH,
|
||||
[NET_MPLS] = 0,
|
||||
[NET_ASPA] = 0,
|
||||
};
|
||||
|
||||
const u16 net_max_text_length[] = {
|
||||
@ -55,6 +58,7 @@ const u16 net_max_text_length[] = {
|
||||
[NET_FLOW6] = 0, /* "flow6 { ... }" */
|
||||
[NET_IP6_SADR]= 92, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128 from ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
|
||||
[NET_MPLS] = 7, /* "1048575" */
|
||||
[NET_ASPA] = 10, /* "4294967295" */
|
||||
};
|
||||
|
||||
/* There should be no implicit padding in net_addr structures */
|
||||
@ -69,6 +73,7 @@ STATIC_ASSERT(sizeof(net_addr_flow4) == 8);
|
||||
STATIC_ASSERT(sizeof(net_addr_flow6) == 20);
|
||||
STATIC_ASSERT(sizeof(net_addr_ip6_sadr) == 40);
|
||||
STATIC_ASSERT(sizeof(net_addr_mpls) == 8);
|
||||
STATIC_ASSERT(sizeof(net_addr_aspa) == 8);
|
||||
|
||||
|
||||
int
|
||||
@ -95,7 +100,7 @@ net_format(const net_addr *N, char *buf, int buflen)
|
||||
switch (n->n.type)
|
||||
{
|
||||
case NET_IP4:
|
||||
return bsnprintf(buf, buflen, "%I4/%d", n->ip4.prefix, n->ip4.pxlen);
|
||||
return (buflen < IP4_PX_BUFFER_SIZE) ? -1 : ip4_px_ntop(n->ip4.prefix, n->ip4.pxlen, buf) - buf;
|
||||
case NET_IP6:
|
||||
return bsnprintf(buf, buflen, "%I6/%d", n->ip6.prefix, n->ip6.pxlen);
|
||||
case NET_VPN4:
|
||||
@ -123,6 +128,8 @@ net_format(const net_addr *N, char *buf, int buflen)
|
||||
return bsnprintf(buf, buflen, "%I6/%d from %I6/%d", n->ip6_sadr.dst_prefix, n->ip6_sadr.dst_pxlen, n->ip6_sadr.src_prefix, n->ip6_sadr.src_pxlen);
|
||||
case NET_MPLS:
|
||||
return bsnprintf(buf, buflen, "%u", n->mpls.label);
|
||||
case NET_ASPA:
|
||||
return bsnprintf(buf, buflen, "%u", n->aspa.asn);
|
||||
}
|
||||
|
||||
bug("unknown network type");
|
||||
@ -147,6 +154,7 @@ net_pxmask(const net_addr *a)
|
||||
return ipa_from_ip6(ip6_mkmask(net6_pxlen(a)));
|
||||
|
||||
case NET_MPLS:
|
||||
case NET_ASPA:
|
||||
default:
|
||||
return IPA_NONE;
|
||||
}
|
||||
@ -180,6 +188,8 @@ net_compare(const net_addr *a, const net_addr *b)
|
||||
return net_compare_ip6_sadr((const net_addr_ip6_sadr *) a, (const net_addr_ip6_sadr *) b);
|
||||
case NET_MPLS:
|
||||
return net_compare_mpls((const net_addr_mpls *) a, (const net_addr_mpls *) b);
|
||||
case NET_ASPA:
|
||||
return net_compare_aspa((const net_addr_aspa *) a, (const net_addr_aspa *) b);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -201,6 +211,7 @@ net_hash(const net_addr *n)
|
||||
case NET_FLOW6: return NET_HASH(n, flow6);
|
||||
case NET_IP6_SADR: return NET_HASH(n, ip6_sadr);
|
||||
case NET_MPLS: return NET_HASH(n, mpls);
|
||||
case NET_ASPA: return NET_HASH(n, aspa);
|
||||
default: bug("invalid type");
|
||||
}
|
||||
}
|
||||
@ -223,6 +234,7 @@ net_validate(const net_addr *n)
|
||||
case NET_FLOW6: return NET_VALIDATE(n, flow6);
|
||||
case NET_IP6_SADR: return NET_VALIDATE(n, ip6_sadr);
|
||||
case NET_MPLS: return NET_VALIDATE(n, mpls);
|
||||
case NET_ASPA: return NET_VALIDATE(n, aspa);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
@ -250,6 +262,7 @@ net_normalize(net_addr *N)
|
||||
return net_normalize_ip6_sadr(&n->ip6_sadr);
|
||||
|
||||
case NET_MPLS:
|
||||
case NET_ASPA:
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -277,6 +290,7 @@ net_classify(const net_addr *N)
|
||||
return ip6_zero(n->ip6_sadr.dst_prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->ip6_sadr.dst_prefix);
|
||||
|
||||
case NET_MPLS:
|
||||
case NET_ASPA:
|
||||
return IADDR_HOST | SCOPE_UNIVERSE;
|
||||
}
|
||||
|
||||
@ -310,6 +324,7 @@ ipa_in_netX(const ip_addr a, const net_addr *n)
|
||||
ip6_mkmask(net6_pxlen(n))));
|
||||
|
||||
case NET_MPLS:
|
||||
case NET_ASPA:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
44
lib/net.h
44
lib/net.h
@ -23,7 +23,8 @@
|
||||
#define NET_FLOW6 8
|
||||
#define NET_IP6_SADR 9
|
||||
#define NET_MPLS 10
|
||||
#define NET_MAX 11
|
||||
#define NET_ASPA 11
|
||||
#define NET_MAX 12
|
||||
|
||||
#define NB_IP4 (1 << NET_IP4)
|
||||
#define NB_IP6 (1 << NET_IP6)
|
||||
@ -35,6 +36,7 @@
|
||||
#define NB_FLOW6 (1 << NET_FLOW6)
|
||||
#define NB_IP6_SADR (1 << NET_IP6_SADR)
|
||||
#define NB_MPLS (1 << NET_MPLS)
|
||||
#define NB_ASPA (1 << NET_ASPA)
|
||||
|
||||
#define NB_IP (NB_IP4 | NB_IP6)
|
||||
#define NB_VPN (NB_VPN4 | NB_VPN6)
|
||||
@ -124,6 +126,13 @@ typedef struct net_addr_mpls {
|
||||
u32 label;
|
||||
} net_addr_mpls;
|
||||
|
||||
typedef struct net_addr_aspa {
|
||||
u8 type;
|
||||
u8 pxlen;
|
||||
u16 length;
|
||||
u32 asn;
|
||||
} net_addr_aspa;
|
||||
|
||||
typedef struct net_addr_ip6_sadr {
|
||||
u8 type;
|
||||
u8 dst_pxlen;
|
||||
@ -145,6 +154,7 @@ typedef union net_addr_union {
|
||||
net_addr_flow6 flow6;
|
||||
net_addr_ip6_sadr ip6_sadr;
|
||||
net_addr_mpls mpls;
|
||||
net_addr_aspa aspa;
|
||||
} net_addr_union;
|
||||
|
||||
|
||||
@ -201,6 +211,9 @@ extern const u16 net_max_text_length[];
|
||||
#define NET_ADDR_IP6_SADR(dst_prefix,dst_pxlen,src_prefix,src_pxlen) \
|
||||
((net_addr_ip6_sadr) { NET_IP6_SADR, dst_pxlen, sizeof(net_addr_ip6_sadr), dst_prefix, src_pxlen, src_prefix })
|
||||
|
||||
#define NET_ADDR_ASPA(asn) \
|
||||
((net_addr_aspa) { NET_ASPA, 32, sizeof(net_addr_aspa), asn })
|
||||
|
||||
#define NET_ADDR_MPLS(label) \
|
||||
((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label })
|
||||
|
||||
@ -229,6 +242,9 @@ static inline void net_fill_ip6_sadr(net_addr *a, ip6_addr dst_prefix, uint dst_
|
||||
static inline void net_fill_mpls(net_addr *a, u32 label)
|
||||
{ *(net_addr_mpls *)a = NET_ADDR_MPLS(label); }
|
||||
|
||||
static inline void net_fill_aspa(net_addr *a, u32 asn)
|
||||
{ *(net_addr_aspa *)a = NET_ADDR_ASPA(asn); }
|
||||
|
||||
static inline void net_fill_ipa(net_addr *a, ip_addr prefix, uint pxlen)
|
||||
{
|
||||
if (ipa_is_ip4(prefix))
|
||||
@ -287,6 +303,9 @@ static inline int net_is_roa(const net_addr *a)
|
||||
static inline int net_is_flow(const net_addr *a)
|
||||
{ return (a->type == NET_FLOW4) || (a->type == NET_FLOW6); }
|
||||
|
||||
static inline int net_is_aspa(const net_addr *a)
|
||||
{ return (a->type == NET_ASPA); }
|
||||
|
||||
static inline int net_is_sadr(const net_addr *a)
|
||||
{ return (a->type == NET_IP6_SADR); }
|
||||
|
||||
@ -314,6 +333,7 @@ static inline ip_addr net_prefix(const net_addr *a)
|
||||
return ipa_from_ip6(net6_prefix(a));
|
||||
|
||||
case NET_MPLS:
|
||||
case NET_ASPA:
|
||||
default:
|
||||
return IPA_NONE;
|
||||
}
|
||||
@ -384,6 +404,9 @@ static inline int net_equal_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_
|
||||
static inline int net_equal_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
|
||||
{ return !memcmp(a, b, sizeof(net_addr_mpls)); }
|
||||
|
||||
static inline int net_equal_aspa(const net_addr_aspa *a, const net_addr_aspa *b)
|
||||
{ return !memcmp(a, b, sizeof(net_addr_aspa)); }
|
||||
|
||||
|
||||
static inline int net_equal_prefix_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
|
||||
{ return ip4_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
|
||||
@ -425,6 +448,9 @@ static inline int net_zero_flow6(const net_addr_flow6 *a)
|
||||
static inline int net_zero_mpls(const net_addr_mpls *a)
|
||||
{ return !a->label; }
|
||||
|
||||
static inline int net_zero_aspa(const net_addr_aspa *a)
|
||||
{ return !a->asn; }
|
||||
|
||||
|
||||
static inline int net_compare_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
|
||||
{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
|
||||
@ -460,6 +486,9 @@ static inline int net_compare_ip6_sadr(const net_addr_ip6_sadr *a, const net_add
|
||||
static inline int net_compare_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
|
||||
{ return uint_cmp(a->label, b->label); }
|
||||
|
||||
static inline int net_compare_aspa(const net_addr_aspa *a, const net_addr_aspa *b)
|
||||
{ return uint_cmp(a->asn, b->asn); }
|
||||
|
||||
int net_compare(const net_addr *a, const net_addr *b);
|
||||
|
||||
|
||||
@ -496,6 +525,9 @@ static inline void net_copy_ip6_sadr(net_addr_ip6_sadr *dst, const net_addr_ip6_
|
||||
static inline void net_copy_mpls(net_addr_mpls *dst, const net_addr_mpls *src)
|
||||
{ memcpy(dst, src, sizeof(net_addr_mpls)); }
|
||||
|
||||
static inline void net_copy_aspa(net_addr_aspa *dst, const net_addr_aspa *src)
|
||||
{ memcpy(dst, src, sizeof(net_addr_aspa)); }
|
||||
|
||||
|
||||
static inline u32 px4_hash(ip4_addr prefix, u32 pxlen)
|
||||
{ return ip4_hash(prefix) ^ (pxlen << 26); }
|
||||
@ -539,6 +571,9 @@ static inline u32 net_hash_ip6_sadr(const net_addr_ip6_sadr *n)
|
||||
static inline u32 net_hash_mpls(const net_addr_mpls *n)
|
||||
{ return u32_hash(n->label); }
|
||||
|
||||
static inline u32 net_hash_aspa(const net_addr_aspa *n)
|
||||
{ return u32_hash(n->asn); }
|
||||
|
||||
u32 net_hash(const net_addr *a);
|
||||
|
||||
|
||||
@ -588,6 +623,9 @@ static inline int net_validate_flow6(const net_addr_flow6 *n)
|
||||
static inline int net_validate_mpls(const net_addr_mpls *n)
|
||||
{ return n->label < (1 << 20); }
|
||||
|
||||
static inline int net_validate_aspa(const net_addr_aspa *n)
|
||||
{ return n->asn > 0; }
|
||||
|
||||
static inline int net_validate_ip6_sadr(const net_addr_ip6_sadr *n)
|
||||
{ return net_validate_px6(n->dst_prefix, n->dst_pxlen) && net_validate_px6(n->src_prefix, n->src_pxlen); }
|
||||
|
||||
@ -616,8 +654,8 @@ void net_normalize(net_addr *N);
|
||||
|
||||
|
||||
int net_classify(const net_addr *N);
|
||||
int net_format(const net_addr *N, char *buf, int buflen);
|
||||
int rd_format(const u64 rd, char *buf, int buflen);
|
||||
int net_format(const net_addr *N, char *buf, int buflen) ACCESS_WRITE(2, 3);
|
||||
int rd_format(const u64 rd, char *buf, int buflen) ACCESS_WRITE(2, 3);
|
||||
|
||||
static inline int ipa_in_px4(ip4_addr a, ip4_addr prefix, uint pxlen)
|
||||
{ return ip4_zero(ip4_and(ip4_xor(a, prefix), ip4_mkmask(pxlen))); }
|
||||
|
23
lib/printf.c
23
lib/printf.c
@ -73,10 +73,25 @@ static char * number(char * str, u64 num, uint base, int size, int precision,
|
||||
i = 0;
|
||||
if (num == 0)
|
||||
tmp[i++]='0';
|
||||
else while (num != 0) {
|
||||
uint res = num % base;
|
||||
num = num / base;
|
||||
tmp[i++] = digits[res];
|
||||
else if (base == 10) {
|
||||
/* Separate cases to have fixed divisors */
|
||||
while (num != 0) {
|
||||
uint res = num % 10;
|
||||
num = num / 10;
|
||||
tmp[i++] = digits[res];
|
||||
}
|
||||
} else if (base == 16) {
|
||||
while (num != 0) {
|
||||
uint res = num % 16;
|
||||
num = num / 16;
|
||||
tmp[i++] = digits[res];
|
||||
}
|
||||
} else {
|
||||
while (num != 0) {
|
||||
uint res = num % base;
|
||||
num = num / base;
|
||||
tmp[i++] = digits[res];
|
||||
}
|
||||
}
|
||||
if (i > precision)
|
||||
precision = i;
|
||||
|
@ -81,9 +81,9 @@ static inline pool *resource_parent(resource *r)
|
||||
|
||||
/* Normal memory blocks */
|
||||
|
||||
void *mb_alloc(pool *, unsigned size);
|
||||
void *mb_allocz(pool *, unsigned size);
|
||||
void *mb_realloc(void *m, unsigned size);
|
||||
void *mb_alloc(pool *, unsigned size) ALLOC_SIZE(2);
|
||||
void *mb_allocz(pool *, unsigned size) ALLOC_SIZE(2);
|
||||
void *mb_realloc(void *m, unsigned size) ALLOC_SIZE(2);
|
||||
void mb_free(void *);
|
||||
|
||||
/* Memory pools with linear allocation */
|
||||
@ -97,9 +97,9 @@ typedef struct lp_state {
|
||||
} lp_state;
|
||||
|
||||
linpool *lp_new(pool *);
|
||||
void *lp_alloc(linpool *, unsigned size); /* Aligned */
|
||||
void *lp_allocu(linpool *, unsigned size); /* Unaligned */
|
||||
void *lp_allocz(linpool *, unsigned size); /* With clear */
|
||||
void *lp_alloc(linpool *, unsigned size) ALLOC_SIZE(2); /* Aligned */
|
||||
void *lp_allocu(linpool *, unsigned size) ALLOC_SIZE(2); /* Unaligned */
|
||||
void *lp_allocz(linpool *, unsigned size) ALLOC_SIZE(2); /* With clear */
|
||||
void lp_flush(linpool *); /* Free everything, but leave linpool */
|
||||
lp_state *lp_save(linpool *m); /* Save state */
|
||||
void lp_restore(linpool *m, lp_state *p); /* Restore state */
|
||||
|
@ -278,6 +278,7 @@ struct ea_class {
|
||||
void (*format)(const eattr *ea, byte *buf, uint size); \
|
||||
void (*stored)(const eattr *ea); /* When stored into global hash */ \
|
||||
void (*freed)(const eattr *ea); /* When released from global hash */ \
|
||||
struct f_val (*empty)(const struct ea_class *); /* Return this instead of T_VOID as default value for filters */ \
|
||||
|
||||
EA_CLASS_INSIDE;
|
||||
};
|
||||
@ -557,6 +558,10 @@ static inline int rte_dest(const rte *r)
|
||||
return nhea_dest(ea_find(r->attrs, &ea_gen_nexthop));
|
||||
}
|
||||
|
||||
/* ASPA Providers eattr */
|
||||
extern struct ea_class ea_gen_aspa_providers;
|
||||
|
||||
|
||||
void rta_init(void);
|
||||
|
||||
ea_list *ea_lookup_slow(ea_list *r, u32 squash_upto, enum ea_stored oid);
|
||||
|
@ -139,6 +139,8 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou
|
||||
#define SKF_HDRINCL 0x400 /* Used internally */
|
||||
#define SKF_PKTINFO 0x800 /* Used internally */
|
||||
|
||||
#define SKF_UDP6_NO_CSUM_RX 0x1000 /* Accept zero checksums for received UDPv6 packets */
|
||||
|
||||
/*
|
||||
* Socket types SA SP DA DP IF TTL SendTo (?=may, -=must not, *=must)
|
||||
*/
|
||||
|
@ -17,8 +17,8 @@
|
||||
|
||||
int bsprintf(char *str, const char *fmt, ...);
|
||||
int bvsprintf(char *str, const char *fmt, va_list args);
|
||||
int bsnprintf(char *str, int size, const char *fmt, ...);
|
||||
int bvsnprintf(char *str, int size, const char *fmt, va_list args);
|
||||
int bsnprintf(char *str, int size, const char *fmt, ...) ACCESS_WRITE(1, 2);
|
||||
int bvsnprintf(char *str, int size, const char *fmt, va_list args) ACCESS_WRITE(1, 2);
|
||||
|
||||
char *mb_sprintf(pool *p, const char *fmt, ...);
|
||||
char *mb_vsprintf(pool *p, const char *fmt, va_list args);
|
||||
@ -34,7 +34,7 @@ u64 bstrtoul16(const char *str, char **end);
|
||||
byte bstrtobyte16(const char *str);
|
||||
|
||||
int bstrhextobin(const char *s, byte *b);
|
||||
int bstrbintohex(const byte *b, size_t len, char *buf, size_t size, char delim);
|
||||
int bstrbintohex(const byte *b, size_t len, char *buf, size_t size, char delim) ACCESS_READ(1, 2) ACCESS_WRITE(3, 4);
|
||||
|
||||
int patmatch(const byte *pat, const byte *str);
|
||||
|
||||
|
@ -68,9 +68,9 @@ tm_dump(resource *r, unsigned indent UNUSED)
|
||||
if (t->randomize)
|
||||
debug("rand %d, ", t->randomize);
|
||||
if (t->recurrent)
|
||||
debug("recur %d, ", t->recurrent);
|
||||
debug("recur %ld, ", t->recurrent);
|
||||
if (t->expires)
|
||||
debug("in loop %p expires in %d ms)\n", t->loop, (t->expires - current_time()) TO_MS);
|
||||
debug("in loop %p expires in %ld ms)\n", t->loop, (t->expires - current_time()) TO_MS);
|
||||
else
|
||||
debug("inactive)\n");
|
||||
}
|
||||
|
@ -28,8 +28,8 @@ typedef struct timer
|
||||
void *data;
|
||||
|
||||
btime expires; /* 0=inactive */
|
||||
btime recurrent; /* Timer recurrence */
|
||||
uint randomize; /* Amount of randomization */
|
||||
uint recurrent; /* Timer recurrence */
|
||||
|
||||
struct timeloop *loop; /* Loop where the timer is active */
|
||||
|
||||
@ -84,7 +84,7 @@ tm_remains(timer *t)
|
||||
}
|
||||
|
||||
static inline timer *
|
||||
tm_new_init(pool *p, void (*hook)(struct timer *), void *data, uint rec, uint rand)
|
||||
tm_new_init(pool *p, void (*hook)(struct timer *), void *data, btime rec, uint rand)
|
||||
{
|
||||
timer *t = tm_new(p);
|
||||
t->hook = hook;
|
||||
|
@ -193,6 +193,7 @@ static inline void TLIST_NAME(rem_node)(TLIST_LIST_STRUCT *list, TLIST_TYPE *nod
|
||||
#undef TLIST_WANT_ADD_HEAD
|
||||
#undef TLIST_WANT_ADD_TAIL
|
||||
#undef TLIST_WANT_UPDATE_NODE
|
||||
#undef TLIST_DEFINED_BEFORE
|
||||
|
||||
# endif
|
||||
#else
|
||||
|
@ -91,12 +91,13 @@ enum btype {
|
||||
T_ENUM_LO = 0x12,
|
||||
T_ENUM_HI = 0x3f,
|
||||
|
||||
T_ENUM_ASPA = 0x2f, /* ASPA validation result */
|
||||
T_ENUM_RTS = 0x31,
|
||||
T_ENUM_SCOPE = 0x33,
|
||||
T_ENUM_MPLS_POLICY = 0x35,
|
||||
T_ENUM_RTD = 0x37,
|
||||
T_ENUM_ROA = 0x39,
|
||||
T_ENUM_NETTYPE = 0x3b,
|
||||
T_ENUM_NET_TYPE = 0x3b,
|
||||
T_ENUM_AF = 0x3d,
|
||||
|
||||
/* new enums go here */
|
||||
|
@ -21,6 +21,8 @@ struct bfd_options {
|
||||
u8 passive;
|
||||
u8 passive_set;
|
||||
u8 mode;
|
||||
u8 auth_type; /* Authentication type (BFD_AUTH_*) */
|
||||
list *passwords; /* Passwords for authentication */
|
||||
};
|
||||
|
||||
struct bfd_request {
|
||||
|
36
nest/cli.c
36
nest/cli.c
@ -272,7 +272,7 @@ cli_event(void *data)
|
||||
}
|
||||
|
||||
cli *
|
||||
cli_new(struct birdsock *sock)
|
||||
cli_new(struct birdsock *sock, struct cli_config *cf)
|
||||
{
|
||||
pool *p = rp_new(cli_pool, the_bird_domain.the_bird, "CLI");
|
||||
cli *c = mb_alloc(p, sizeof(cli));
|
||||
@ -286,6 +286,10 @@ cli_new(struct birdsock *sock)
|
||||
c->cont = cli_hello;
|
||||
c->parser_pool = lp_new_default(c->pool);
|
||||
c->rx_buf = mb_alloc(c->pool, CLI_RX_BUF_SIZE);
|
||||
|
||||
if (cf->restricted)
|
||||
c->restricted = 1;
|
||||
|
||||
ev_schedule(c->event);
|
||||
return c;
|
||||
}
|
||||
@ -300,6 +304,36 @@ cli_kick(cli *c)
|
||||
static list cli_log_hooks;
|
||||
static int cli_log_inited;
|
||||
|
||||
/* Set time format override for the current session */
|
||||
void
|
||||
cli_set_timeformat(cli *c, const struct timeformat tf)
|
||||
{
|
||||
size_t len1 = strlen(tf.fmt1) + 1;
|
||||
size_t len2 = tf.fmt2 ? strlen(tf.fmt2) + 1 : 0;
|
||||
|
||||
if (len1 > TM_DATETIME_BUFFER_SIZE || len2 > TM_DATETIME_BUFFER_SIZE)
|
||||
{
|
||||
cli_msg(9003, "Format string too long");
|
||||
return;
|
||||
}
|
||||
|
||||
struct timeformat *old_tf = c->tf;
|
||||
struct timeformat *new_tf = mb_allocz(c->pool, sizeof(struct timeformat));
|
||||
new_tf->fmt1 = memcpy(mb_alloc(c->pool, len1), tf.fmt1, len1);
|
||||
new_tf->fmt2 = tf.fmt2 ? memcpy(mb_alloc(c->pool, len2), tf.fmt2, len2) : NULL;
|
||||
new_tf->limit = tf.limit;
|
||||
c->tf = new_tf;
|
||||
|
||||
if (old_tf)
|
||||
{
|
||||
mb_free((void *) old_tf->fmt1);
|
||||
mb_free((void *) old_tf->fmt2);
|
||||
mb_free(old_tf);
|
||||
}
|
||||
|
||||
cli_msg(0, "");
|
||||
}
|
||||
|
||||
/* Hack for scheduled undo notification */
|
||||
extern cli *cmd_reconfig_stored_cli;
|
||||
|
||||
|
24
nest/cli.h
24
nest/cli.h
@ -12,6 +12,9 @@
|
||||
#include "lib/resource.h"
|
||||
#include "lib/lists.h"
|
||||
#include "lib/event.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/tlists.h"
|
||||
#include "conf/conf.h"
|
||||
|
||||
#define CLI_RX_BUF_SIZE 4096
|
||||
#define CLI_TX_BUF_SIZE 4096
|
||||
@ -39,12 +42,30 @@ typedef struct cli {
|
||||
struct config *main_config; /* Main config currently in use */
|
||||
int last_reply;
|
||||
int restricted; /* CLI is restricted to read-only commands */
|
||||
struct timeformat *tf; /* Time format override */
|
||||
struct linpool *parser_pool; /* Pool used during parsing */
|
||||
uint log_mask; /* Mask of allowed message levels */
|
||||
uint log_threshold; /* When free < log_threshold, store only important messages */
|
||||
uint async_msg_size; /* Total size of async messages queued in tx_buf */
|
||||
} cli;
|
||||
|
||||
struct cli_config {
|
||||
#define TLIST_PREFIX cli_config
|
||||
#define TLIST_TYPE struct cli_config
|
||||
#define TLIST_ITEM n
|
||||
#define TLIST_DEFINED_BEFORE
|
||||
#define TLIST_WANT_ADD_TAIL
|
||||
#define TLIST_WANT_WALK
|
||||
TLIST_DEFAULT_NODE;
|
||||
const char *name;
|
||||
struct config *config;
|
||||
uint uid, gid, mode;
|
||||
_Bool restricted;
|
||||
};
|
||||
#include "lib/tlists.h"
|
||||
|
||||
void cli_config_listen(struct cli_config *, const char *);
|
||||
|
||||
extern pool *cli_pool;
|
||||
extern struct cli *this_cli; /* Used during parsing */
|
||||
|
||||
@ -54,13 +75,14 @@ extern struct cli *this_cli; /* Used during parsing */
|
||||
|
||||
void cli_printf(cli *, int, char *, ...);
|
||||
#define cli_msg(x...) cli_printf(this_cli, x)
|
||||
void cli_set_timeformat(cli *c, const struct timeformat tf);
|
||||
|
||||
static inline void cli_separator(cli *c)
|
||||
{ if (c->last_reply) cli_printf(c, -c->last_reply, ""); };
|
||||
|
||||
/* Functions provided to sysdep layer */
|
||||
|
||||
cli *cli_new(struct birdsock *);
|
||||
cli *cli_new(struct birdsock *, struct cli_config *);
|
||||
void cli_init(void);
|
||||
void cli_free(cli *);
|
||||
void cli_kick(cli *);
|
||||
|
@ -22,11 +22,10 @@ extern int configuring;
|
||||
void
|
||||
cmd_show_status(void)
|
||||
{
|
||||
byte tim[TM_DATETIME_BUFFER_SIZE];
|
||||
|
||||
rcu_read_lock();
|
||||
struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_acquire);
|
||||
struct timeformat *tf = &gr->tf_base;
|
||||
struct timeformat *tf = this_cli->tf ?: &gr->tf_base;
|
||||
byte tim[TM_DATETIME_BUFFER_SIZE];
|
||||
|
||||
cli_msg(-1000, "BIRD " BIRD_VERSION);
|
||||
tm_format_time(tim, tf, current_time());
|
||||
|
@ -153,7 +153,7 @@ CF_DECLS
|
||||
|
||||
CF_KEYWORDS(ROUTER, ID, HOSTNAME, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT, PIPE)
|
||||
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, DEFAULT, TABLE, TABLES, STATES, ROUTES, FILTERS)
|
||||
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS)
|
||||
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS, ASPA)
|
||||
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED, RPKI)
|
||||
CF_KEYWORDS(PASSWORD, KEY, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, CHANNELS, INTERFACES)
|
||||
CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512, BLAKE2S128, BLAKE2S256, BLAKE2B256, BLAKE2B512)
|
||||
@ -162,20 +162,22 @@ CF_KEYWORDS(BGP, PASSWORDS, DESCRIPTION)
|
||||
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, CLASS, DSCP, PARTIAL)
|
||||
CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
|
||||
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, AS)
|
||||
CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
|
||||
CF_KEYWORDS(CHECK, LINK)
|
||||
CF_KEYWORDS(CORK, SORTED, TRIE, MIN, MAX, ROA, DIGEST, ROUTE, REFRESH, SETTLE, TIME, GC, THRESHOLD, PERIOD)
|
||||
CF_KEYWORDS(MPLS_LABEL, MPLS_POLICY, MPLS_CLASS)
|
||||
CF_KEYWORDS(ASPA_PROVIDERS)
|
||||
|
||||
/* For r_args_channel */
|
||||
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
|
||||
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC, ASPA)
|
||||
|
||||
CF_ENUM(T_ENUM_NET_TYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP6_SADR, MPLS, ASPA)
|
||||
CF_ENUM(T_ENUM_RTS, RTS_, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
|
||||
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL, RPKI, L3VPN,
|
||||
AGGREGATED)
|
||||
CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
|
||||
CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
|
||||
CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
|
||||
CF_ENUM(T_ENUM_ASPA, ASPA_, UNKNOWN, VALID, INVALID)
|
||||
CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
|
||||
CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE, VRF)
|
||||
|
||||
@ -189,6 +191,8 @@ CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE, VRF)
|
||||
%type <ps> proto_patt proto_patt2
|
||||
%type <cc> channel_start proto_channel
|
||||
%type <cl> limit_spec
|
||||
%type <tf> timeformat_spec
|
||||
%type <tfp> timeformat_which
|
||||
%type <net> r_args_for_val
|
||||
%type <net_ptr> r_args_for
|
||||
%type <t> channel_sym
|
||||
@ -241,6 +245,7 @@ net_type_base:
|
||||
| ROA6 { $$ = NET_ROA6; }
|
||||
| FLOW4{ $$ = NET_FLOW4; }
|
||||
| FLOW6{ $$ = NET_FLOW6; }
|
||||
| ASPA { $$ = NET_ASPA; }
|
||||
;
|
||||
|
||||
net_type:
|
||||
@ -248,8 +253,6 @@ net_type:
|
||||
| MPLS { $$ = NET_MPLS; }
|
||||
;
|
||||
|
||||
CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP6_SADR, MPLS)
|
||||
|
||||
|
||||
/* Creation of routing tables */
|
||||
|
||||
@ -452,6 +455,10 @@ debug_default:
|
||||
|
||||
conf: timeformat_base ;
|
||||
|
||||
timeformat_base:
|
||||
TIMEFORMAT timeformat_which timeformat_spec ';' { *$2 = $3; }
|
||||
;
|
||||
|
||||
timeformat_which:
|
||||
ROUTE { $$ = &new_config->tf_route; }
|
||||
| PROTOCOL { $$ = &new_config->tf_proto; }
|
||||
@ -460,18 +467,14 @@ timeformat_which:
|
||||
;
|
||||
|
||||
timeformat_spec:
|
||||
timeformat_which TEXT { *$1 = (struct timeformat){$2, NULL, 0}; }
|
||||
| timeformat_which TEXT expr TEXT { *$1 = (struct timeformat){$2, $4, (s64) $3 S_}; }
|
||||
| timeformat_which ISO SHORT { *$1 = TM_ISO_SHORT_S; }
|
||||
| timeformat_which ISO SHORT MS { *$1 = TM_ISO_SHORT_MS; }
|
||||
| timeformat_which ISO SHORT US { *$1 = TM_ISO_SHORT_US; }
|
||||
| timeformat_which ISO LONG { *$1 = TM_ISO_LONG_S; }
|
||||
| timeformat_which ISO LONG MS { *$1 = TM_ISO_LONG_MS; }
|
||||
| timeformat_which ISO LONG US { *$1 = TM_ISO_LONG_US; }
|
||||
;
|
||||
|
||||
timeformat_base:
|
||||
TIMEFORMAT timeformat_spec ';'
|
||||
TEXT { $$ = (struct timeformat){$1, NULL, 0}; }
|
||||
| TEXT expr TEXT { $$ = (struct timeformat){$1, $3, (s64) $2 S_}; }
|
||||
| ISO SHORT { $$ = TM_ISO_SHORT_S; }
|
||||
| ISO SHORT MS { $$ = TM_ISO_SHORT_MS; }
|
||||
| ISO SHORT US { $$ = TM_ISO_SHORT_US; }
|
||||
| ISO LONG { $$ = TM_ISO_LONG_S; }
|
||||
| ISO LONG MS { $$ = TM_ISO_LONG_MS; }
|
||||
| ISO LONG US { $$ = TM_ISO_LONG_US; }
|
||||
;
|
||||
|
||||
/* Interface patterns */
|
||||
@ -662,26 +665,9 @@ password_item_end:
|
||||
};
|
||||
|
||||
|
||||
/* BFD options */
|
||||
/* BFD options - just dummy rule, rest in proto/bfd/config.Y */
|
||||
bfd_opts: '{' INVALID_TOKEN '}';
|
||||
|
||||
bfd_item:
|
||||
INTERVAL expr_us { this_bfd_opts->min_rx_int = this_bfd_opts->min_tx_int = $2; }
|
||||
| MIN RX INTERVAL expr_us { this_bfd_opts->min_rx_int = $4; }
|
||||
| MIN TX INTERVAL expr_us { this_bfd_opts->min_tx_int = $4; }
|
||||
| IDLE TX INTERVAL expr_us { this_bfd_opts->idle_tx_int = $4; }
|
||||
| MULTIPLIER expr { this_bfd_opts->multiplier = $2; }
|
||||
| PASSIVE bool { this_bfd_opts->passive = $2; this_bfd_opts->passive_set = 1; }
|
||||
| GRACEFUL { this_bfd_opts->mode = BGP_BFD_GRACEFUL; }
|
||||
;
|
||||
|
||||
bfd_items:
|
||||
/* empty */
|
||||
| bfd_items bfd_item ';'
|
||||
;
|
||||
|
||||
bfd_opts:
|
||||
'{' bfd_items '}'
|
||||
;
|
||||
|
||||
/* Core commands */
|
||||
CF_CLI_HELP(SHOW, ..., [[Show status information]])
|
||||
@ -908,6 +894,7 @@ channel_sym:
|
||||
| FLOW4 { $$ = "flow4"; }
|
||||
| FLOW6 { $$ = "flow6"; }
|
||||
| MPLS { $$ = "mpls"; }
|
||||
| ASPA { $$ = "aspa"; }
|
||||
| PRI { $$ = "pri"; }
|
||||
| SEC { $$ = "sec"; }
|
||||
;
|
||||
@ -1019,6 +1006,16 @@ CF_CLI(MRTDUMP, proto_patt mrtdump_mask, (<protocol> | \"<pattern>\" | all) (all
|
||||
CF_CLI(RESTRICT,,,[[Restrict current CLI session to safe commands]])
|
||||
{ this_cli->restricted = 1; cli_msg(16, "Access restricted"); } ;
|
||||
|
||||
CF_CLI_HELP(TIMEFORMAT, ..., [[Set time format for this CLI session]])
|
||||
CF_CLI(TIMEFORMAT, timeformat_spec, \"<format1>\" [limit \"format2\"] | iso (short | long) [ ms | us ], [[Set time format for this CLI session]])
|
||||
{ cli_set_timeformat(this_cli, $2); } ;
|
||||
|
||||
CF_CLI_OPT(TIMEFORMAT ISO)
|
||||
CF_CLI_OPT(TIMEFORMAT SHORT)
|
||||
CF_CLI_OPT(TIMEFORMAT LONG)
|
||||
CF_CLI_OPT(TIMEFORMAT MS)
|
||||
CF_CLI_OPT(TIMEFORMAT US)
|
||||
|
||||
proto_patt:
|
||||
CF_SYM_KNOWN { cf_assert_symbol($1, SYM_PROTO); $$.ptr = $1; $$.patt = 0; }
|
||||
| ALL { $$.ptr = NULL; $$.patt = 1; }
|
||||
|
@ -232,7 +232,8 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
|
||||
struct ifa *addr = NULL;
|
||||
|
||||
WALK_LIST(n, neigh_hash_table[h]) /* Search the cache */
|
||||
if ((n->proto == p) && ipa_equal(n->addr, a) && (n->ifreq == iface))
|
||||
if ((n->proto == p) && ipa_equal(n->addr, a) && (n->ifreq == iface) &&
|
||||
((n->flags & NEF_ONLINK) == (flags & NEF_ONLINK)))
|
||||
{
|
||||
IFACE_UNLOCK;
|
||||
return n;
|
||||
|
@ -613,6 +613,11 @@ channel_roa_subscribe_filter(struct channel *c, int dir)
|
||||
found = 1;
|
||||
break;
|
||||
|
||||
case FI_ASPA_CHECK_EXPLICIT:
|
||||
tab = fi->i_FI_ASPA_CHECK_EXPLICIT.rtc->table;
|
||||
if (valid) channel_roa_subscribe(c, tab, dir);
|
||||
found = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -2610,7 +2615,7 @@ proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt)
|
||||
p->proto->get_status(p, buf);
|
||||
|
||||
rcu_read_lock();
|
||||
tm_format_time(tbuf, &atomic_load_explicit(&global_runtime, memory_order_acquire)->tf_proto, p->last_state_change);
|
||||
tm_format_time(tbuf, this_cli->tf ?: &atomic_load_explicit(&global_runtime, memory_order_acquire)->tf_proto, p->last_state_change);
|
||||
rcu_read_unlock();
|
||||
cli_msg(-1002, "%-10s %-10s %-10s %-6s %-12s %s",
|
||||
p->name,
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <stdatomic.h>
|
||||
|
||||
struct ea_list;
|
||||
struct adata;
|
||||
struct protocol;
|
||||
struct proto;
|
||||
struct channel;
|
||||
@ -928,6 +929,13 @@ void ea_show_nexthop_list(struct cli *c, struct nexthop_adata *nhad);
|
||||
#define ROA_VALID 1
|
||||
#define ROA_INVALID 2
|
||||
|
||||
enum aspa_result {
|
||||
ASPA_UNKNOWN = 0,
|
||||
ASPA_VALID,
|
||||
ASPA_INVALID,
|
||||
};
|
||||
|
||||
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
|
||||
enum aspa_result aspa_check(rtable *tab, const struct adata *path, bool force_upstream);
|
||||
|
||||
#endif
|
||||
|
@ -188,6 +188,18 @@ const char * flowspec_valid_names[FLOWSPEC__MAX] = {
|
||||
[FLOWSPEC_INVALID] = "invalid",
|
||||
};
|
||||
|
||||
static void
|
||||
ea_gen_aspa_providers_format(const eattr *a, byte *buf, uint size)
|
||||
{
|
||||
int_set_format(a->u.ad, ISF_NUMBERS, -1, buf, size - 5);
|
||||
}
|
||||
|
||||
struct ea_class ea_gen_aspa_providers = {
|
||||
.name = "aspa_providers",
|
||||
.type = T_CLIST,
|
||||
.format = ea_gen_aspa_providers_format,
|
||||
};
|
||||
|
||||
DOMAIN(attrs) attrs_domain;
|
||||
|
||||
pool *rta_pool;
|
||||
@ -1454,7 +1466,7 @@ ea_show(struct cli *c, const eattr *e)
|
||||
as_path_format(ad, pos, end - pos);
|
||||
break;
|
||||
case T_CLIST:
|
||||
ea_show_int_set(c, cls->name, ad, 1, buf);
|
||||
ea_show_int_set(c, cls->name, ad, ISF_COMMUNITY_LIST, buf);
|
||||
return;
|
||||
case T_ECLIST:
|
||||
ea_show_ec_set(c, cls->name, ad, buf);
|
||||
@ -1876,6 +1888,9 @@ rta_init(void)
|
||||
ea_register_init(&ea_gen_mpls_policy);
|
||||
ea_register_init(&ea_gen_mpls_class);
|
||||
ea_register_init(&ea_gen_mpls_label);
|
||||
|
||||
/* ASPA providers */
|
||||
ea_register_init(&ea_gen_aspa_providers);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -279,6 +279,7 @@ fib_find(struct fib *f, const net_addr *a)
|
||||
case NET_FLOW6: return FIB_FIND(f, a, flow6);
|
||||
case NET_IP6_SADR: return FIB_FIND(f, a, ip6_sadr);
|
||||
case NET_MPLS: return FIB_FIND(f, a, mpls);
|
||||
case NET_ASPA: return FIB_FIND(f, a, aspa);
|
||||
default: bug("invalid type");
|
||||
}
|
||||
}
|
||||
@ -300,6 +301,7 @@ fib_insert(struct fib *f, const net_addr *a, struct fib_node *e)
|
||||
case NET_FLOW6: FIB_INSERT(f, a, e, flow6); return;
|
||||
case NET_IP6_SADR: FIB_INSERT(f, a, e, ip6_sadr); return;
|
||||
case NET_MPLS: FIB_INSERT(f, a, e, mpls); return;
|
||||
case NET_ASPA: FIB_INSERT(f, a, e, aspa); return;
|
||||
default: bug("invalid type");
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
|
||||
int dest = nhad ? (NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest) : RTD_NONE;
|
||||
int flowspec_valid = net_is_flow(e->net) ? rt_get_flowspec_valid(e) : FLOWSPEC_UNKNOWN;
|
||||
|
||||
tm_format_time(tm, &d->tf_route, e->lastmod);
|
||||
tm_format_time(tm, c->tf ?: &d->tf_route, e->lastmod);
|
||||
ip_addr a_from = ea_get_ip(a, &ea_gen_from, IPA_NONE);
|
||||
if (ipa_nonzero(a_from) && (!nhad || !ipa_equal(a_from, nhad->nh.gw)))
|
||||
bsprintf(from, " from %I", a_from);
|
||||
|
130
nest/rt-table.c
130
nest/rt-table.c
@ -717,6 +717,136 @@ net_roa_check(rtable *tp, const net_addr *n, u32 asn)
|
||||
#undef TW
|
||||
}
|
||||
|
||||
/**
|
||||
* aspa_check - check validity of AS Path in an ASPA table
|
||||
* @tab: ASPA table
|
||||
* @path: AS Path to check
|
||||
*
|
||||
* Implements draft-ietf-sidrops-aspa-verification-16.
|
||||
*/
|
||||
enum aspa_result aspa_check(rtable *tab, const adata *path, bool force_upstream)
|
||||
{
|
||||
/* Restore tmp linpool state after this check */
|
||||
CLEANUP(lp_saved_cleanup) struct lp_state *_lps = lp_save(tmp_linpool);
|
||||
|
||||
/* No support for confed paths */
|
||||
if (as_path_contains_confed(path))
|
||||
return ASPA_INVALID;
|
||||
|
||||
/* Check path length */
|
||||
uint len = as_path_getlen(path);
|
||||
if (len == 0)
|
||||
return ASPA_INVALID;
|
||||
|
||||
/* Normalize the AS Path: drop stuffings */
|
||||
u32 *asns = alloca(sizeof(u32) * len);
|
||||
uint ppos = 0;
|
||||
uint nsz = 0;
|
||||
while (as_path_walk(path, &ppos, &asns[nsz]))
|
||||
if ((nsz == 0) || (asns[nsz] != asns[nsz-1]))
|
||||
nsz++;
|
||||
|
||||
/* Find the provider blocks for every AS on the path
|
||||
* and check allowed directions */
|
||||
uint max_up = 0, min_up = 0, max_down = 0, min_down = 0;
|
||||
|
||||
RT_READ(tab, tr);
|
||||
|
||||
for (uint ap=0; ap<nsz; ap++)
|
||||
{
|
||||
net_addr_union nau = { .aspa = NET_ADDR_ASPA(asns[ap]), };
|
||||
|
||||
/* Find some ASPAs */
|
||||
struct netindex *ni = net_find_index(tr->t->netindex, &nau.n);
|
||||
net *n = ni ? net_find(tr, ni) : NULL;
|
||||
|
||||
bool found = false, down = false, up = false;
|
||||
|
||||
if (n) NET_READ_WALK_ROUTES(tr, n, ep, e)
|
||||
{
|
||||
if (!rte_is_valid(&e->rte))
|
||||
continue;
|
||||
|
||||
eattr *ea = ea_find(e->rte.attrs, &ea_gen_aspa_providers);
|
||||
if (!ea)
|
||||
continue;
|
||||
|
||||
/* Actually found some ASPA */
|
||||
found = true;
|
||||
|
||||
for (uint i=0; i * sizeof(u32) < ea->u.ptr->length; i++)
|
||||
{
|
||||
if ((ap > 0) && ((u32 *) ea->u.ptr->data)[i] == asns[ap-1])
|
||||
up = true;
|
||||
if ((ap + 1 < nsz) && ((u32 *) ea->u.ptr->data)[i] == asns[ap+1])
|
||||
down = true;
|
||||
|
||||
if (down && up)
|
||||
/* Both peers found */
|
||||
goto end_of_aspa;
|
||||
}
|
||||
}
|
||||
end_of_aspa:;
|
||||
|
||||
/* Fast path for the upstream check */
|
||||
if (force_upstream)
|
||||
{
|
||||
if (!found)
|
||||
/* Move min-upstream */
|
||||
min_up = ap;
|
||||
else if (ap && !up)
|
||||
/* Exists but doesn't allow this upstream */
|
||||
return ASPA_INVALID;
|
||||
}
|
||||
|
||||
/* Fast path for no ASPA here */
|
||||
else if (!found)
|
||||
{
|
||||
/* Extend max-downstream (min-downstream is stopped by unknown) */
|
||||
max_down = ap+1;
|
||||
|
||||
/* Move min-upstream (can't include unknown) */
|
||||
min_up = ap;
|
||||
}
|
||||
|
||||
/* ASPA exists and downstream may be extended */
|
||||
else if (down)
|
||||
{
|
||||
/* Extending max-downstream always */
|
||||
max_down = ap+1;
|
||||
|
||||
/* Extending min-downstream unless unknown seen */
|
||||
if (min_down == ap)
|
||||
min_down = ap+1;
|
||||
|
||||
/* Downstream only */
|
||||
if (!up)
|
||||
min_up = max_up = ap;
|
||||
}
|
||||
|
||||
/* No extension for downstream, force upstream only from now */
|
||||
else
|
||||
{
|
||||
force_upstream = 1;
|
||||
|
||||
/* Not even upstream, move the ending here */
|
||||
if (!up)
|
||||
min_up = max_up = ap;
|
||||
}
|
||||
}
|
||||
|
||||
/* Is the path surely valid? */
|
||||
if (min_up <= min_down)
|
||||
return ASPA_VALID;
|
||||
|
||||
/* Is the path maybe valid? */
|
||||
if (max_up <= max_down)
|
||||
return ASPA_UNKNOWN;
|
||||
|
||||
/* Now there is surely a valley there. */
|
||||
return ASPA_INVALID;
|
||||
}
|
||||
|
||||
struct rte_storage *
|
||||
rte_store(const rte *r, struct netindex *i, struct rtable_private *tab)
|
||||
{
|
||||
|
@ -181,6 +181,8 @@ bfd_merge_options(const struct bfd_iface_config *cf, const struct bfd_options *o
|
||||
.idle_tx_int = opts->idle_tx_int ?: cf->idle_tx_int,
|
||||
.multiplier = opts->multiplier ?: cf->multiplier,
|
||||
.passive = opts->passive_set ? opts->passive : cf->passive,
|
||||
.auth_type = opts->auth_type ?: cf->auth_type,
|
||||
.passwords = opts->passwords ?: cf->passwords,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1187,7 +1189,8 @@ bfd_reconfigure(struct proto *P, struct proto_config *c)
|
||||
(new->accept_ipv6 != old->accept_ipv6) ||
|
||||
(new->accept_direct != old->accept_direct) ||
|
||||
(new->accept_multihop != old->accept_multihop) ||
|
||||
(new->strict_bind != old->strict_bind))
|
||||
(new->strict_bind != old->strict_bind) ||
|
||||
(new->zero_udp6_checksum_rx != old->zero_udp6_checksum_rx))
|
||||
return 0;
|
||||
|
||||
birdloop_mask_wakeups(p->p.loop);
|
||||
@ -1235,7 +1238,7 @@ bfd_show_session(struct bfd_session *s, int details)
|
||||
const char *ifname = (s->ifa && s->ifa->iface) ? s->ifa->iface->name : "---";
|
||||
btime tx_int = s->last_tx ? MAX(s->des_min_tx_int, s->rem_min_rx_int) : 0;
|
||||
btime timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult;
|
||||
u8 auth_type = s->ifa->cf->auth_type;
|
||||
u8 auth_type = s->cf.auth_type;
|
||||
|
||||
loc_state = (loc_state < 4) ? loc_state : 0;
|
||||
rem_state = (rem_state < 4) ? rem_state : 0;
|
||||
@ -1245,7 +1248,7 @@ bfd_show_session(struct bfd_session *s, int details)
|
||||
|
||||
rcu_read_lock();
|
||||
struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_relaxed);
|
||||
tm_format_time(tbuf, &gr->tf_proto, s->last_state_change);
|
||||
tm_format_time(tbuf, this_cli->tf ?: &gr->tf_proto, s->last_state_change);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!details)
|
||||
|
@ -48,6 +48,7 @@ struct bfd_config
|
||||
u8 accept_direct;
|
||||
u8 accept_multihop;
|
||||
u8 strict_bind;
|
||||
u8 zero_udp6_checksum_rx;
|
||||
};
|
||||
|
||||
struct bfd_iface_config
|
||||
@ -69,6 +70,8 @@ struct bfd_session_config
|
||||
u32 idle_tx_int;
|
||||
u8 multiplier;
|
||||
u8 passive;
|
||||
u8 auth_type; /* Authentication type (BFD_AUTH_*) */
|
||||
list *passwords; /* Passwords for authentication */
|
||||
};
|
||||
|
||||
struct bfd_neighbor
|
||||
|
@ -24,7 +24,7 @@ CF_DECLS
|
||||
CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE,
|
||||
INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL, AUTHENTICATION,
|
||||
NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1, IPV4, IPV6, DIRECT,
|
||||
STRICT, BIND)
|
||||
STRICT, BIND, ZERO, UDP6, CHECKSUM, RX)
|
||||
|
||||
%type <iface> bfd_neigh_iface
|
||||
%type <a> bfd_neigh_local
|
||||
@ -53,6 +53,7 @@ bfd_proto_item:
|
||||
| MULTIHOP bfd_multihop
|
||||
| NEIGHBOR bfd_neighbor
|
||||
| STRICT BIND bool { BFD_CFG->strict_bind = $3; }
|
||||
| ZERO UDP6 CHECKSUM RX bool { BFD_CFG->zero_udp6_checksum_rx = $5; }
|
||||
;
|
||||
|
||||
bfd_proto_opts:
|
||||
@ -185,6 +186,52 @@ bfd_neighbor: ipa bfd_neigh_iface bfd_neigh_local bfd_neigh_multihop
|
||||
};
|
||||
|
||||
|
||||
/* BFD options */
|
||||
|
||||
bfd_item:
|
||||
INTERVAL expr_us { this_bfd_opts->min_rx_int = this_bfd_opts->min_tx_int = $2; }
|
||||
| MIN RX INTERVAL expr_us { this_bfd_opts->min_rx_int = $4; }
|
||||
| MIN TX INTERVAL expr_us { this_bfd_opts->min_tx_int = $4; }
|
||||
| IDLE TX INTERVAL expr_us { this_bfd_opts->idle_tx_int = $4; }
|
||||
| MULTIPLIER expr { this_bfd_opts->multiplier = $2; }
|
||||
| PASSIVE bool { this_bfd_opts->passive = $2; this_bfd_opts->passive_set = 1; }
|
||||
| GRACEFUL { this_bfd_opts->mode = BGP_BFD_GRACEFUL; }
|
||||
| AUTHENTICATION bfd_auth_type { this_bfd_opts->auth_type = $2; }
|
||||
| password_list {}
|
||||
;
|
||||
|
||||
bfd_items:
|
||||
/* empty */
|
||||
| bfd_items bfd_item ';'
|
||||
;
|
||||
|
||||
bfd_opts_start:
|
||||
{ reset_passwords(); } ;
|
||||
|
||||
bfd_opts_end:
|
||||
{
|
||||
this_bfd_opts->passwords = get_passwords();
|
||||
|
||||
if (!this_bfd_opts->auth_type != !this_bfd_opts->passwords)
|
||||
cf_warn("Authentication and password options should be used together");
|
||||
|
||||
if (this_bfd_opts->passwords)
|
||||
{
|
||||
struct password_item *pass;
|
||||
WALK_LIST(pass, *this_bfd_opts->passwords)
|
||||
{
|
||||
if (pass->alg)
|
||||
cf_error("Password algorithm option not available in BFD protocol");
|
||||
|
||||
pass->alg = bfd_auth_type_to_hash_alg[this_bfd_opts->auth_type];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bfd_opts:
|
||||
'{' bfd_opts_start bfd_items '}' bfd_opts_end;
|
||||
|
||||
|
||||
CF_CLI_HELP(SHOW BFD, ..., [[Show information about BFD protocol]]);
|
||||
|
||||
CF_CLI_HELP(SHOW BFD SESSIONS, ..., [[Show information about BFD sessions]]);
|
||||
|
@ -109,7 +109,7 @@ const u8 bfd_auth_type_to_hash_alg[] = {
|
||||
static void
|
||||
bfd_fill_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt)
|
||||
{
|
||||
struct bfd_iface_config *cf = s->ifa->cf;
|
||||
struct bfd_session_config *cf = &s->cf;
|
||||
struct password_item *pass = password_find(cf->passwords, 0);
|
||||
uint meticulous = 0;
|
||||
|
||||
@ -179,7 +179,7 @@ bfd_fill_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_c
|
||||
static int
|
||||
bfd_check_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt)
|
||||
{
|
||||
struct bfd_iface_config *cf = s->ifa->cf;
|
||||
struct bfd_session_config *cf = &s->cf;
|
||||
const char *err_dsc = NULL;
|
||||
uint err_val = 0;
|
||||
uint auth_type = 0;
|
||||
@ -306,7 +306,7 @@ bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final)
|
||||
else if (s->poll_active)
|
||||
pkt->flags |= BFD_FLAG_POLL;
|
||||
|
||||
if (s->ifa->cf->auth_type)
|
||||
if (s->cf.auth_type)
|
||||
bfd_fill_authentication(p, s, pkt);
|
||||
|
||||
if (sk->tbuf != sk->tpos)
|
||||
@ -416,6 +416,8 @@ bfd_err_hook(sock *sk, int err)
|
||||
sock *
|
||||
bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af)
|
||||
{
|
||||
struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
|
||||
|
||||
sock *sk = sk_new(p->p.pool);
|
||||
sk->type = SK_UDP;
|
||||
sk->subtype = af;
|
||||
@ -432,6 +434,9 @@ bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af)
|
||||
sk->priority = sk_priority_control;
|
||||
sk->flags = SKF_LADDR_RX | (!multihop ? SKF_TTL_RX : 0);
|
||||
|
||||
if (cf->zero_udp6_checksum_rx)
|
||||
sk->flags |= SKF_UDP6_NO_CSUM_RX;
|
||||
|
||||
if (sk_open(sk, p->p.loop) < 0)
|
||||
goto err;
|
||||
|
||||
@ -446,6 +451,8 @@ err:
|
||||
sock *
|
||||
bfd_open_rx_sk_bound(struct bfd_proto *p, ip_addr local, struct iface *ifa)
|
||||
{
|
||||
struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
|
||||
|
||||
sock *sk = sk_new(p->tpool);
|
||||
sk->type = SK_UDP;
|
||||
sk->saddr = local;
|
||||
@ -463,6 +470,9 @@ bfd_open_rx_sk_bound(struct bfd_proto *p, ip_addr local, struct iface *ifa)
|
||||
sk->priority = sk_priority_control;
|
||||
sk->flags = SKF_BIND | (ifa ? SKF_TTL_RX : 0);
|
||||
|
||||
if (cf->zero_udp6_checksum_rx)
|
||||
sk->flags |= SKF_UDP6_NO_CSUM_RX;
|
||||
|
||||
if (sk_open(sk, p->p.loop) < 0)
|
||||
goto err;
|
||||
|
||||
|
@ -729,7 +729,7 @@ static void
|
||||
bgp_format_cluster_list(const eattr *a, byte *buf, uint size)
|
||||
{
|
||||
/* Truncates cluster lists larger than buflen, probably not a problem */
|
||||
int_set_format(a->u.ptr, 0, -1, buf, size);
|
||||
int_set_format(a->u.ptr, ISF_ROUTER_ID, -1, buf, size);
|
||||
}
|
||||
|
||||
|
||||
|
@ -104,6 +104,7 @@
|
||||
* RFC 9072 - Extended Optional Parameters Length for BGP OPEN Message
|
||||
* RFC 9117 - Revised Validation Procedure for BGP Flow Specifications
|
||||
* RFC 9234 - Route Leak Prevention and Detection Using Roles
|
||||
* RFC 9687 - Send Hold Timer
|
||||
* draft-uttaro-idr-bgp-persistence-04
|
||||
* draft-walton-bgp-hostname-capability-02
|
||||
*/
|
||||
|
@ -33,12 +33,14 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
|
||||
FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER,
|
||||
RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND)
|
||||
|
||||
CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
|
||||
|
||||
%type <i> bgp_nh
|
||||
%type <i32> bgp_afi
|
||||
|
||||
CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
|
||||
CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION,
|
||||
OUT, OF, RESOURCES)
|
||||
OUT, OF, RESOURCES, ASPA_CHECK_UPSTREAM, ASPA_CHECK_DOWNSTREAM)
|
||||
|
||||
%type<i> bgp_cease_mask bgp_cease_list bgp_cease_flag bgp_role_name
|
||||
|
||||
@ -365,8 +367,6 @@ custom_attr: ATTRIBUTE BGP NUM type symbol ';' {
|
||||
cf_define_symbol(new_config, $5, SYM_ATTRIBUTE, attribute, ac);
|
||||
};
|
||||
|
||||
CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
|
||||
|
||||
CF_CLI(RELOAD BGP, proto_patt, [<name>], [[Send and request route refresh to/from neighbor]])
|
||||
{
|
||||
proto_apply_cmd($3, bgp_reload_in, 1, 0);
|
||||
@ -383,6 +383,29 @@ CF_CLI(RELOAD BGP OUT, proto_patt, [<name>], [[Refresh routes to neighbor]])
|
||||
proto_apply_cmd($4, bgp_reload_out, 1, 0);
|
||||
}
|
||||
|
||||
/* ASPA shortcuts */
|
||||
term: ASPA_CHECK_DOWNSTREAM '(' rtable ')' { $$ =
|
||||
f_new_inst(FI_ASPA_CHECK_EXPLICIT,
|
||||
f_new_inst(FI_EA_GET,
|
||||
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL, }),
|
||||
ea_class_find_by_name("bgp_path")
|
||||
),
|
||||
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }),
|
||||
$3
|
||||
);
|
||||
}
|
||||
|
||||
term: ASPA_CHECK_UPSTREAM '(' rtable ')' { $$ =
|
||||
f_new_inst(FI_ASPA_CHECK_EXPLICIT,
|
||||
f_new_inst(FI_EA_GET,
|
||||
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_ROUTE, .val.rte = NULL, }),
|
||||
ea_class_find_by_name("bgp_path")
|
||||
),
|
||||
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }),
|
||||
$3
|
||||
);
|
||||
}
|
||||
|
||||
CF_CODE
|
||||
|
||||
CF_END
|
||||
|
@ -3342,7 +3342,7 @@ static struct {
|
||||
{ 6, 8, "Out of Resources" },
|
||||
{ 7, 0, "Invalid ROUTE-REFRESH message" }, /* [RFC7313] */
|
||||
{ 7, 1, "Invalid ROUTE-REFRESH message length" }, /* [RFC7313] */
|
||||
{ 8, 0, "Send hold timer expired" }, /* [draft-ietf-idr-bgp-sendholdtimer] */
|
||||
{ 8, 0, "Send hold timer expired" }, /* [RFC9687] */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -252,6 +252,7 @@ struct ospf_proto
|
||||
u32 last_vlink_id; /* Interface IDs for vlinks (starts at 0x80000000) */
|
||||
struct tbf log_pkt_tbf; /* TBF for packet messages */
|
||||
struct tbf log_lsa_tbf; /* TBF for LSA messages */
|
||||
ip_addr loopback_addr; /* IP address used as common next hop (in OSPFv3-IPv4) */
|
||||
};
|
||||
|
||||
struct ospf_area
|
||||
@ -331,6 +332,7 @@ struct ospf_iface
|
||||
struct top_hash_entry **flood_queue; /* LSAs queued for LSUPD */
|
||||
u8 update_link_lsa;
|
||||
u8 update_net_lsa;
|
||||
u8 loopback_addr_used; /* The Link-LSA depends on p->loopback_addr */
|
||||
u16 flood_queue_used; /* The current number of LSAs in flood_queue */
|
||||
u16 flood_queue_size; /* The maximum number of LSAs in flood_queue */
|
||||
int fadj; /* Number of fully adjacent neighbors */
|
||||
|
@ -2029,6 +2029,14 @@ again1:
|
||||
{
|
||||
neighbor *nbr = neigh_find(&p->p, nh->gw, nh->iface,
|
||||
(nh->flags & RNF_ONLINK) ? NEF_ONLINK : 0);
|
||||
|
||||
/* According to RFC 5838 2.5 Direct Interface Address */
|
||||
if (ospf_is_v3(p) && !nbr && ipa_is_ip4(nh->gw))
|
||||
{
|
||||
nh->flags |= RNF_ONLINK;
|
||||
nbr = neigh_find(&p->p, nh->gw, nh->iface, NEF_ONLINK);
|
||||
}
|
||||
|
||||
if (!nbr || (nbr->scope == SCOPE_HOST))
|
||||
{ reset_ri(nf); break; }
|
||||
}
|
||||
|
@ -1381,6 +1381,46 @@ lsab_put_prefix(struct ospf_proto *p, net_addr *n, u32 cost)
|
||||
ospf3_put_prefix(buf, n, flags, cost);
|
||||
}
|
||||
|
||||
static inline void
|
||||
update_loopback_addr(struct ospf_proto *p)
|
||||
{
|
||||
ip_addr old_addr = p->loopback_addr;
|
||||
ip_addr best_addr = IPA_NONE;
|
||||
int best_pref = 0;
|
||||
|
||||
struct ospf_iface *ifa;
|
||||
WALK_LIST(ifa, p->iface_list)
|
||||
{
|
||||
if (ifa->type == OSPF_IT_VLINK)
|
||||
continue;
|
||||
|
||||
struct ifa *a;
|
||||
WALK_LIST(a, ifa->iface->addrs)
|
||||
{
|
||||
if ((a->prefix.type != ospf_get_af(p)) ||
|
||||
(a->flags & IA_SECONDARY) ||
|
||||
(a->scope <= SCOPE_LINK))
|
||||
continue;
|
||||
|
||||
int pref = (a->flags & IA_HOST) ? 3 : (ifa->stub ? 2 : 1);
|
||||
if ((pref > best_pref) || ((pref == best_pref) && ipa_equal(a->ip, old_addr)))
|
||||
{
|
||||
best_addr = a->ip;
|
||||
best_pref = pref;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ipa_equal(best_addr, old_addr))
|
||||
return;
|
||||
|
||||
p->loopback_addr = best_addr;
|
||||
|
||||
WALK_LIST(ifa, p->iface_list)
|
||||
if (ifa->loopback_addr_used)
|
||||
ospf_notify_link_lsa(ifa);
|
||||
}
|
||||
|
||||
static void
|
||||
prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
|
||||
{
|
||||
@ -1406,6 +1446,12 @@ prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
|
||||
i++;
|
||||
}
|
||||
|
||||
if (ospf_is_ip4(p) && ipa_zero(nh))
|
||||
{
|
||||
nh = p->loopback_addr;
|
||||
ifa->loopback_addr_used = 1;
|
||||
}
|
||||
|
||||
/* Filling the preallocated header */
|
||||
struct ospf_lsa_link *ll = p->lsab;
|
||||
ll->options = ifa->oa->options | (ifa->priority << 24);
|
||||
@ -1832,6 +1878,9 @@ ospf_update_topology(struct ospf_proto *p)
|
||||
}
|
||||
}
|
||||
|
||||
if (ospf_is_v3(p) && ospf_is_ip4(p))
|
||||
update_loopback_addr(p);
|
||||
|
||||
WALK_LIST(ifa, p->iface_list)
|
||||
{
|
||||
if (ifa->type == OSPF_IT_VLINK)
|
||||
@ -1839,6 +1888,8 @@ ospf_update_topology(struct ospf_proto *p)
|
||||
|
||||
if (ifa->update_link_lsa)
|
||||
{
|
||||
ifa->loopback_addr_used = 0;
|
||||
|
||||
if ((ifa->state > OSPF_IS_LOOP) && !ifa->link_lsa_suppression)
|
||||
ospf_originate_link_lsa(p, ifa);
|
||||
else
|
||||
|
@ -13,6 +13,7 @@ CF_HDR
|
||||
CF_DEFINES
|
||||
|
||||
#define RPKI_CFG ((struct rpki_config *) this_proto)
|
||||
#define RPKI_TR_TCP_CFG ((struct rpki_tr_tcp_config *) RPKI_CFG->tr_config.spec)
|
||||
#define RPKI_TR_SSH_CFG ((struct rpki_tr_ssh_config *) RPKI_CFG->tr_config.spec)
|
||||
|
||||
static void
|
||||
@ -32,7 +33,8 @@ rpki_check_unused_transport(void)
|
||||
CF_DECLS
|
||||
|
||||
CF_KEYWORDS(RPKI, REMOTE, BIRD, PRIVATE, PUBLIC, KEY, TCP, SSH, TRANSPORT, USER,
|
||||
RETRY, REFRESH, EXPIRE, KEEP, IGNORE, MAX, LENGTH, LOCAL, ADDRESS)
|
||||
RETRY, REFRESH, EXPIRE, KEEP, IGNORE, MAX, MIN, LENGTH, LOCAL, ADDRESS,
|
||||
AUTHENTICATION, NONE, MD5, PASSWORD, VERSION)
|
||||
|
||||
%type <i> rpki_keep_interval
|
||||
|
||||
@ -46,6 +48,8 @@ rpki_proto_start: proto_start RPKI {
|
||||
RPKI_CFG->retry_interval = RPKI_RETRY_INTERVAL;
|
||||
RPKI_CFG->refresh_interval = RPKI_REFRESH_INTERVAL;
|
||||
RPKI_CFG->expire_interval = RPKI_EXPIRE_INTERVAL;
|
||||
RPKI_CFG->min_version = 0;
|
||||
RPKI_CFG->max_version = RPKI_MAX_VERSION;
|
||||
};
|
||||
|
||||
rpki_proto: rpki_proto_start proto_name '{' rpki_proto_opts '}' { rpki_check_config(RPKI_CFG); };
|
||||
@ -82,6 +86,14 @@ rpki_proto_item:
|
||||
RPKI_CFG->keep_expire_interval = $2;
|
||||
}
|
||||
| IGNORE MAX LENGTH bool { RPKI_CFG->ignore_max_length = $4; }
|
||||
| MIN VERSION expr {
|
||||
if ($3 > RPKI_MAX_VERSION) cf_error("RPKI version %u unsupported, min version must be in range 0-%u", $3, RPKI_MAX_VERSION);
|
||||
RPKI_CFG->min_version = $3;
|
||||
}
|
||||
| MAX VERSION expr {
|
||||
if ($3 > RPKI_MAX_VERSION) cf_error("RPKI version %u unsupported, max version must be in range 0-%u", $3, RPKI_MAX_VERSION);
|
||||
RPKI_CFG->max_version = $3;
|
||||
}
|
||||
;
|
||||
|
||||
rpki_keep_interval:
|
||||
@ -109,7 +121,7 @@ rpki_cache_addr: text_or_ipa
|
||||
};
|
||||
|
||||
rpki_transport:
|
||||
TCP rpki_transport_tcp_init
|
||||
TCP rpki_transport_tcp_init rpki_transport_tcp_opts_list rpki_transport_tcp_check
|
||||
| SSH rpki_transport_ssh_init '{' rpki_transport_ssh_opts '}' rpki_transport_ssh_check
|
||||
;
|
||||
|
||||
@ -120,6 +132,28 @@ rpki_transport_tcp_init:
|
||||
RPKI_CFG->tr_config.type = RPKI_TR_TCP;
|
||||
};
|
||||
|
||||
rpki_transport_tcp_opts_list:
|
||||
/* empty */
|
||||
| '{' rpki_transport_tcp_opts '}'
|
||||
;
|
||||
|
||||
rpki_transport_tcp_opts:
|
||||
/* empty */
|
||||
| rpki_transport_tcp_opts rpki_transport_tcp_item ';'
|
||||
;
|
||||
|
||||
rpki_transport_tcp_item:
|
||||
AUTHENTICATION NONE { RPKI_TR_TCP_CFG->auth_type = RPKI_TCP_AUTH_NONE; }
|
||||
| AUTHENTICATION MD5 { RPKI_TR_TCP_CFG->auth_type = RPKI_TCP_AUTH_MD5; }
|
||||
| PASSWORD text { RPKI_TR_TCP_CFG->password = $2; }
|
||||
;
|
||||
|
||||
rpki_transport_tcp_check:
|
||||
{
|
||||
if (!RPKI_TR_TCP_CFG->auth_type != !RPKI_TR_TCP_CFG->password)
|
||||
cf_error("Authentication and password options should be used together");
|
||||
};
|
||||
|
||||
rpki_transport_ssh_init:
|
||||
{
|
||||
#if HAVE_LIBSSH
|
||||
|
@ -62,6 +62,7 @@ enum pdu_type {
|
||||
CACHE_RESET = 8,
|
||||
ROUTER_KEY = 9,
|
||||
ERROR = 10,
|
||||
ASPA = 11,
|
||||
PDU_TYPE_MAX
|
||||
};
|
||||
|
||||
@ -76,7 +77,8 @@ static const char *str_pdu_type_[] = {
|
||||
[END_OF_DATA] = "End of Data",
|
||||
[CACHE_RESET] = "Cache Reset",
|
||||
[ROUTER_KEY] = "Router Key",
|
||||
[ERROR] = "Error"
|
||||
[ERROR] = "Error",
|
||||
[ASPA] = "ASPA",
|
||||
};
|
||||
|
||||
static const char *str_pdu_type(uint type) {
|
||||
@ -193,6 +195,35 @@ struct pdu_error {
|
||||
* Error Diagnostic Message */
|
||||
} PACKED;
|
||||
|
||||
/*
|
||||
*0 8 16 24 31
|
||||
* .-------------------------------------------.
|
||||
* | Protocol | PDU | | |
|
||||
* | Version | Type | Flags | zero |
|
||||
* | 2 | 11 | | |
|
||||
* +-------------------------------------------+
|
||||
* | |
|
||||
* | Length |
|
||||
* | |
|
||||
* +-------------------------------------------+
|
||||
* | |
|
||||
* | Customer Autonomous System Number |
|
||||
* | |
|
||||
* +-------------------------------------------+
|
||||
* | |
|
||||
* ~ Provider Autonomous System Numbers ~
|
||||
* | |
|
||||
* ~-------------------------------------------~ */
|
||||
struct pdu_aspa {
|
||||
u8 ver;
|
||||
u8 type;
|
||||
u8 flags;
|
||||
u8 zero;
|
||||
u32 len;
|
||||
u32 customer_as_num;
|
||||
u32 provider_as_nums[0];
|
||||
} PACKED;
|
||||
|
||||
struct pdu_reset_query {
|
||||
u8 ver;
|
||||
u8 type;
|
||||
@ -230,9 +261,13 @@ static const size_t min_pdu_size[] = {
|
||||
[END_OF_DATA] = sizeof(struct pdu_end_of_data_v0),
|
||||
[CACHE_RESET] = sizeof(struct pdu_cache_response),
|
||||
[ROUTER_KEY] = sizeof(struct pdu_header), /* FIXME */
|
||||
[ASPA] = sizeof(struct pdu_aspa),
|
||||
[ERROR] = 16,
|
||||
};
|
||||
|
||||
static inline int rpki_pdu_aspa_provider_asn_count(const struct pdu_aspa *pdu)
|
||||
{ return (pdu->len - sizeof(struct pdu_aspa)) / (sizeof(u32)); }
|
||||
|
||||
static int rpki_send_error_pdu_(struct rpki_cache *cache, const enum pdu_error_type error_code, const u32 err_pdu_len, const struct pdu_header *erroneous_pdu, const char *fmt, ...);
|
||||
|
||||
#define rpki_send_error_pdu(cache, error_code, err_pdu_len, erroneous_pdu, fmt...) ({ \
|
||||
@ -276,29 +311,25 @@ rpki_pdu_to_network_byte_order(struct pdu_header *pdu)
|
||||
static void
|
||||
rpki_pdu_to_host_byte_order(struct pdu_header *pdu)
|
||||
{
|
||||
/* The Router Key PDU has two one-byte fields instead of one two-bytes field. */
|
||||
if (pdu->type != ROUTER_KEY)
|
||||
pdu->reserved = ntohs(pdu->reserved);
|
||||
|
||||
pdu->len = ntohl(pdu->len);
|
||||
|
||||
switch (pdu->type)
|
||||
{
|
||||
case SERIAL_NOTIFY:
|
||||
{
|
||||
/* Note that a session_id is converted using converting header->reserved */
|
||||
struct pdu_serial_notify *sn_pdu = (void *) pdu;
|
||||
sn_pdu->session_id = ntohs(sn_pdu->session_id);
|
||||
sn_pdu->serial_num = ntohl(sn_pdu->serial_num);
|
||||
break;
|
||||
}
|
||||
|
||||
case END_OF_DATA:
|
||||
{
|
||||
/* Note that a session_id is converted using converting header->reserved */
|
||||
struct pdu_end_of_data_v0 *eod0 = (void *) pdu;
|
||||
eod0->session_id = ntohs(eod0->session_id);
|
||||
eod0->serial_num = ntohl(eod0->serial_num); /* Same either for version 1 */
|
||||
|
||||
if (pdu->ver == RPKI_VERSION_1)
|
||||
if (pdu->ver > RPKI_VERSION_0)
|
||||
{
|
||||
struct pdu_end_of_data_v1 *eod1 = (void *) pdu;
|
||||
eod1->expire_interval = ntohl(eod1->expire_interval);
|
||||
@ -326,14 +357,29 @@ rpki_pdu_to_host_byte_order(struct pdu_header *pdu)
|
||||
|
||||
case ERROR:
|
||||
{
|
||||
/* Note that a error_code is converted using converting header->reserved */
|
||||
struct pdu_error *err = (void *) pdu;
|
||||
err->error_code = ntohs(err->error_code);
|
||||
err->len_enc_pdu = ntohl(err->len_enc_pdu);
|
||||
u32 *err_text_len = (u32 *)(err->rest + err->len_enc_pdu);
|
||||
*err_text_len = htonl(*err_text_len);
|
||||
break;
|
||||
}
|
||||
|
||||
case ASPA:
|
||||
{
|
||||
struct pdu_aspa *aspa = (void *) pdu;
|
||||
int provider_asn_count = rpki_pdu_aspa_provider_asn_count(aspa);
|
||||
|
||||
/* Convert customer ASN */
|
||||
aspa->customer_as_num = ntohl(aspa->customer_as_num);
|
||||
|
||||
/* Convert provider ASNs */
|
||||
for (int i = 0; i < provider_asn_count ; i++)
|
||||
aspa->provider_as_nums[i] = ntohl(aspa->provider_as_nums[i]);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ROUTER_KEY:
|
||||
/* Router Key PDU is not supported yet */
|
||||
|
||||
@ -343,8 +389,14 @@ rpki_pdu_to_host_byte_order(struct pdu_header *pdu)
|
||||
* We don't care here. */
|
||||
|
||||
case CACHE_RESPONSE:
|
||||
{
|
||||
struct pdu_cache_response *cr = (void *) pdu;
|
||||
cr->session_id = ntohs(cr->session_id);
|
||||
break;
|
||||
}
|
||||
|
||||
case CACHE_RESET:
|
||||
/* Converted with pdu->reserved */
|
||||
/* Nothing to convert */
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -359,6 +411,9 @@ rpki_pdu_to_host_byte_order(struct pdu_header *pdu)
|
||||
static struct pdu_header *
|
||||
rpki_pdu_back_to_network_byte_order(struct pdu_header *out, const struct pdu_header *in)
|
||||
{
|
||||
/* Only valid for fixed-length PDUs */
|
||||
ASSERT_DIE(in->type != ERROR && in->type != ASPA);
|
||||
|
||||
memcpy(out, in, in->len);
|
||||
rpki_pdu_to_host_byte_order(out);
|
||||
return out;
|
||||
@ -392,7 +447,7 @@ rpki_log_packet(struct rpki_cache *cache, const struct pdu_header *pdu, const en
|
||||
case END_OF_DATA:
|
||||
{
|
||||
const struct pdu_end_of_data_v1 *eod = (void *) pdu;
|
||||
if (eod->ver == RPKI_VERSION_1)
|
||||
if (eod->ver > RPKI_VERSION_0)
|
||||
SAVE(bsnprintf(detail, sizeof(detail), "(session id: %u, serial number: %u, refresh: %us, retry: %us, expire: %us)", eod->session_id, eod->serial_num, eod->refresh_interval, eod->retry_interval, eod->expire_interval));
|
||||
else
|
||||
SAVE(bsnprintf(detail, sizeof(detail), "(session id: %u, serial number: %u)", eod->session_id, eod->serial_num));
|
||||
@ -460,6 +515,25 @@ rpki_log_packet(struct rpki_cache *cache, const struct pdu_header *pdu, const en
|
||||
break;
|
||||
}
|
||||
|
||||
case ASPA:
|
||||
{
|
||||
const struct pdu_aspa *aspa = (void *) pdu;
|
||||
int provider_asn_count = rpki_pdu_aspa_provider_asn_count(aspa);
|
||||
|
||||
if (provider_asn_count <= 0)
|
||||
SAVE(bsnprintf(detail + strlen(detail), sizeof(detail) - strlen(detail),
|
||||
"%u transit", aspa->customer_as_num));
|
||||
else
|
||||
{
|
||||
SAVE(bsnprintf(detail + strlen(detail), sizeof(detail) - strlen(detail),
|
||||
"%u (providers", aspa->customer_as_num));
|
||||
for (int i = 0; i < provider_asn_count; i++)
|
||||
SAVE(bsnprintf(detail + strlen(detail), sizeof(detail) - strlen(detail),
|
||||
" %u%c", aspa->provider_as_nums[i], (i == provider_asn_count-1) ? ')' : ','));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
*detail = '\0';
|
||||
}
|
||||
@ -561,7 +635,9 @@ rpki_check_receive_packet(struct rpki_cache *cache, const struct pdu_header *pdu
|
||||
}
|
||||
else if (!cache->last_update &&
|
||||
(pdu->ver <= RPKI_MAX_VERSION) &&
|
||||
(pdu->ver < cache->version))
|
||||
(pdu->ver < cache->version) &&
|
||||
(pdu->ver >= cache->min_version)
|
||||
)
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "Downgrade session to %s from %u to %u version", rpki_get_cache_ident(cache), cache->version, pdu->ver);
|
||||
cache->version = pdu->ver;
|
||||
@ -576,15 +652,21 @@ rpki_check_receive_packet(struct rpki_cache *cache, const struct pdu_header *pdu
|
||||
}
|
||||
}
|
||||
|
||||
if ((pdu->type >= PDU_TYPE_MAX) || (pdu->ver == RPKI_VERSION_0 && pdu->type == ROUTER_KEY))
|
||||
if ((pdu->type >= PDU_TYPE_MAX) ||
|
||||
(pdu->ver < RPKI_VERSION_1 && pdu->type == ROUTER_KEY) ||
|
||||
(pdu->ver < RPKI_VERSION_2 && pdu->type == ASPA))
|
||||
{
|
||||
rpki_send_error_pdu(cache, UNSUPPORTED_PDU_TYPE, pdu_len, pdu, "Unsupported PDU type %u received", pdu->type);
|
||||
return RPKI_ERROR;
|
||||
}
|
||||
|
||||
if (pdu_len < min_pdu_size[pdu->type])
|
||||
uint min_pdu_length = min_pdu_size[pdu->type];
|
||||
if (pdu->type == END_OF_DATA && pdu->ver >= RPKI_VERSION_1)
|
||||
min_pdu_length = sizeof(struct pdu_end_of_data_v1);
|
||||
|
||||
if (pdu_len < min_pdu_length)
|
||||
{
|
||||
rpki_send_error_pdu(cache, CORRUPT_DATA, pdu_len, pdu, "Received %s packet with %d bytes, but expected at least %d bytes", str_pdu_type(pdu->type), pdu_len, min_pdu_size[pdu->type]);
|
||||
rpki_send_error_pdu(cache, CORRUPT_DATA, pdu_len, pdu, "Received %s packet with %u bytes, but expected at least %u bytes", str_pdu_type(pdu->type), pdu_len, min_pdu_length);
|
||||
return RPKI_ERROR;
|
||||
}
|
||||
|
||||
@ -611,7 +693,8 @@ rpki_handle_error_pdu(struct rpki_cache *cache, const struct pdu_error *pdu)
|
||||
case UNSUPPORTED_PROTOCOL_VER:
|
||||
CACHE_TRACE(D_PACKETS, cache, "Client uses unsupported protocol version");
|
||||
if (pdu->ver <= RPKI_MAX_VERSION &&
|
||||
pdu->ver < cache->version)
|
||||
pdu->ver < cache->version &&
|
||||
pdu->ver >= cache->min_version)
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "Downgrading from protocol version %d to version %d", cache->version, pdu->ver);
|
||||
cache->version = pdu->ver;
|
||||
@ -789,6 +872,29 @@ rpki_handle_prefix_pdu(struct rpki_cache *cache, const struct pdu_header *pdu)
|
||||
return RPKI_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
rpki_handle_aspa_pdu(struct rpki_cache *cache, const struct pdu_header *pdu)
|
||||
{
|
||||
struct pdu_aspa *aspa = (void *) pdu;
|
||||
struct channel *channel = cache->p->aspa_channel;
|
||||
uint providers_length = aspa->len - sizeof(struct pdu_aspa);
|
||||
|
||||
if (!channel)
|
||||
{
|
||||
CACHE_TRACE(D_ROUTES, cache, "Skip AS%u, missing aspa channel", aspa->customer_as_num);
|
||||
return RPKI_ERROR;
|
||||
}
|
||||
|
||||
cache->last_rx_prefix = current_time();
|
||||
|
||||
if (aspa->flags & RPKI_ADD_FLAG)
|
||||
rpki_table_add_aspa(cache, channel, aspa->customer_as_num, aspa->provider_as_nums, providers_length);
|
||||
else
|
||||
rpki_table_remove_aspa(cache, channel, aspa->customer_as_num);
|
||||
|
||||
return RPKI_SUCCESS;
|
||||
}
|
||||
|
||||
static uint
|
||||
rpki_check_interval(struct rpki_cache *cache, const char *(check_fn)(uint), uint interval)
|
||||
{
|
||||
@ -814,7 +920,7 @@ rpki_handle_end_of_data_pdu(struct rpki_cache *cache, const struct pdu_end_of_da
|
||||
return;
|
||||
}
|
||||
|
||||
if (pdu->ver == RPKI_VERSION_1)
|
||||
if (pdu->ver > RPKI_VERSION_0)
|
||||
{
|
||||
if (!cf->keep_refresh_interval && rpki_check_interval(cache, rpki_check_refresh_interval, pdu->refresh_interval))
|
||||
cache->refresh_interval = pdu->refresh_interval;
|
||||
@ -898,6 +1004,10 @@ rpki_rx_packet(struct rpki_cache *cache, struct pdu_header *pdu)
|
||||
/* TODO: Implement Router Key PDU handling */
|
||||
break;
|
||||
|
||||
case ASPA:
|
||||
rpki_handle_aspa_pdu(cache, (void *) pdu);
|
||||
break;
|
||||
|
||||
default:
|
||||
CACHE_TRACE(D_PACKETS, cache, "Received unsupported type (%u)", pdu->type);
|
||||
};
|
||||
|
@ -23,8 +23,10 @@
|
||||
* +4 bytes (Length of inserted text)
|
||||
* +800 bytes (UTF-8 text 400*2 bytes)
|
||||
* ------------
|
||||
* = 848 bytes (Maximal expected PDU size) */
|
||||
#define RPKI_PDU_MAX_LEN 848
|
||||
* = 848 bytes (Maximal expected PDU size)
|
||||
*
|
||||
* Received ASPA PDU can have any size, so let's start with 4k */
|
||||
#define RPKI_PDU_MAX_LEN 4096
|
||||
|
||||
/* RX buffer size has a great impact to scheduler granularity */
|
||||
#define RPKI_RX_BUFFER_SIZE 4096
|
||||
|
@ -137,6 +137,33 @@ rpki_table_remove_roa(struct rpki_cache *cache, struct channel *channel, const n
|
||||
rte_update(channel, &pfxr->n, NULL, p->p.main_source);
|
||||
}
|
||||
|
||||
void
|
||||
rpki_table_add_aspa(struct rpki_cache *cache, struct channel *channel,
|
||||
u32 customer, void *providers, uint providers_length)
|
||||
{
|
||||
struct rpki_proto *p = cache->p;
|
||||
|
||||
net_addr_union n = { .aspa = NET_ADDR_ASPA(customer) };
|
||||
|
||||
ea_list *ea = NULL;
|
||||
ea_set_attr_u32(&ea, &ea_gen_preference, 0, channel->preference);
|
||||
ea_set_attr_u32(&ea, &ea_gen_source, 0, RTS_RPKI);
|
||||
|
||||
ea_set_attr_data(&ea, &ea_gen_aspa_providers, 0, providers, providers_length);
|
||||
|
||||
rte e0 = { .attrs = ea, .src = p->p.main_source, };
|
||||
|
||||
rte_update(channel, &n.n, &e0, p->p.main_source);
|
||||
}
|
||||
|
||||
void
|
||||
rpki_table_remove_aspa(struct rpki_cache *cache, struct channel *channel, u32 customer)
|
||||
{
|
||||
struct rpki_proto *p = cache->p;
|
||||
net_addr_union n = { .aspa = NET_ADDR_ASPA(customer) };
|
||||
rte_update(channel, &n.n, NULL, p->p.main_source);
|
||||
}
|
||||
|
||||
void
|
||||
rpki_start_refresh(struct rpki_proto *p)
|
||||
{
|
||||
@ -144,6 +171,8 @@ rpki_start_refresh(struct rpki_proto *p)
|
||||
rt_refresh_begin(&p->roa4_channel->in_req);
|
||||
if (p->roa6_channel)
|
||||
rt_refresh_begin(&p->roa6_channel->in_req);
|
||||
if (p->aspa_channel)
|
||||
rt_refresh_begin(&p->aspa_channel->in_req);
|
||||
|
||||
p->refresh_channels = 1;
|
||||
}
|
||||
@ -160,6 +189,8 @@ rpki_stop_refresh(struct rpki_proto *p)
|
||||
rt_refresh_end(&p->roa4_channel->in_req);
|
||||
if (p->roa6_channel)
|
||||
rt_refresh_end(&p->roa6_channel->in_req);
|
||||
if (p->aspa_channel)
|
||||
rt_refresh_end(&p->aspa_channel->in_req);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -608,7 +639,8 @@ rpki_init_cache(struct rpki_proto *p, struct rpki_config *cf)
|
||||
|
||||
cache->state = RPKI_CS_SHUTDOWN;
|
||||
cache->request_session_id = 1;
|
||||
cache->version = RPKI_MAX_VERSION;
|
||||
cache->version = cf->max_version;
|
||||
cache->min_version = cf->min_version;
|
||||
|
||||
cache->refresh_interval = cf->refresh_interval;
|
||||
cache->retry_interval = cf->retry_interval;
|
||||
@ -714,6 +746,23 @@ rpki_reconfigure_cache(struct rpki_proto *p UNUSED, struct rpki_cache *cache, st
|
||||
return NEED_RESTART;
|
||||
}
|
||||
|
||||
if (new->min_version > cache->version)
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "Protocol min version %u higher than current version %u",
|
||||
new->min_version, cache->version);
|
||||
return NEED_RESTART;
|
||||
}
|
||||
else
|
||||
cache->min_version = new->min_version;
|
||||
|
||||
if (new->max_version < cache->version)
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "Protocol max version %u lower than current version %u",
|
||||
new->max_version, cache->version);
|
||||
cache->version = new->max_version;
|
||||
try_reset = 1;
|
||||
}
|
||||
|
||||
if (old->tr_config.type != new->tr_config.type)
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "Transport type changed");
|
||||
@ -726,6 +775,24 @@ rpki_reconfigure_cache(struct rpki_proto *p UNUSED, struct rpki_cache *cache, st
|
||||
try_reset = 1;
|
||||
}
|
||||
|
||||
if (new->tr_config.type == RPKI_TR_TCP)
|
||||
{
|
||||
struct rpki_tr_tcp_config *tcp_old = (void *) old->tr_config.spec;
|
||||
struct rpki_tr_tcp_config *tcp_new = (void *) new->tr_config.spec;
|
||||
|
||||
if (tcp_old->auth_type != tcp_new->auth_type)
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "Authentication type changed");
|
||||
return NEED_RESTART;
|
||||
}
|
||||
|
||||
if (bstrcmp(tcp_old->password, tcp_new->password))
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "MD5 password changed");
|
||||
return NEED_RESTART;
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_LIBSSH
|
||||
else if (new->tr_config.type == RPKI_TR_SSH)
|
||||
{
|
||||
@ -796,7 +863,8 @@ rpki_reconfigure(struct proto *P, struct proto_config *CF)
|
||||
struct rpki_cache *cache = p->cache;
|
||||
|
||||
if (!proto_configure_channel(&p->p, &p->roa4_channel, proto_cf_find_channel(CF, NET_ROA4)) ||
|
||||
!proto_configure_channel(&p->p, &p->roa6_channel, proto_cf_find_channel(CF, NET_ROA6)))
|
||||
!proto_configure_channel(&p->p, &p->roa6_channel, proto_cf_find_channel(CF, NET_ROA6)) ||
|
||||
!proto_configure_channel(&p->p, &p->aspa_channel, proto_cf_find_channel(CF, NET_ASPA)))
|
||||
return NEED_RESTART;
|
||||
|
||||
if (rpki_reconfigure_cache(p, cache, new, old) != SUCCESSFUL_RECONF)
|
||||
@ -818,6 +886,7 @@ rpki_init(struct proto_config *CF)
|
||||
|
||||
proto_configure_channel(&p->p, &p->roa4_channel, proto_cf_find_channel(CF, NET_ROA4));
|
||||
proto_configure_channel(&p->p, &p->roa6_channel, proto_cf_find_channel(CF, NET_ROA6));
|
||||
proto_configure_channel(&p->p, &p->aspa_channel, proto_cf_find_channel(CF, NET_ASPA));
|
||||
|
||||
return P;
|
||||
}
|
||||
@ -883,8 +952,12 @@ rpki_show_proto_info(struct proto *P)
|
||||
default_port = RPKI_SSH_PORT;
|
||||
break;
|
||||
#endif
|
||||
case RPKI_TR_TCP:
|
||||
transport_name = "Unprotected over TCP";
|
||||
case RPKI_TR_TCP:;
|
||||
struct rpki_tr_tcp_config *tcp_cf = (void *) cf->tr_config.spec;
|
||||
if (tcp_cf->auth_type == RPKI_TCP_AUTH_MD5)
|
||||
transport_name = "TCP-MD5";
|
||||
else
|
||||
transport_name = "Unprotected over TCP";
|
||||
default_port = RPKI_TCP_PORT;
|
||||
break;
|
||||
};
|
||||
@ -927,6 +1000,11 @@ rpki_show_proto_info(struct proto *P)
|
||||
channel_show_info(p->roa6_channel);
|
||||
else
|
||||
cli_msg(-1006, " No roa6 channel");
|
||||
|
||||
if (p->aspa_channel)
|
||||
channel_show_info(p->aspa_channel);
|
||||
else
|
||||
cli_msg(-1006, " No aspa channel");
|
||||
}
|
||||
}
|
||||
|
||||
@ -944,6 +1022,9 @@ rpki_show_proto_info(struct proto *P)
|
||||
void
|
||||
rpki_check_config(struct rpki_config *cf)
|
||||
{
|
||||
if (cf->min_version > cf->max_version)
|
||||
cf_error("Impossible min/max version for RPKI: %u/%u", cf->min_version, cf->max_version);
|
||||
|
||||
/* Do not check templates at all */
|
||||
if (cf->c.class == SYM_TEMPLATE)
|
||||
return;
|
||||
@ -998,7 +1079,7 @@ struct protocol proto_rpki = {
|
||||
.init = rpki_init,
|
||||
.start = rpki_start,
|
||||
.postconfig = rpki_postconfig,
|
||||
.channel_mask = (NB_ROA4 | NB_ROA6),
|
||||
.channel_mask = (NB_ROA4 | NB_ROA6 | NB_ASPA),
|
||||
.show_proto_info = rpki_show_proto_info,
|
||||
.shutdown = rpki_shutdown,
|
||||
.copy_config = rpki_copy_config,
|
||||
|
@ -29,7 +29,8 @@
|
||||
|
||||
#define RPKI_VERSION_0 0
|
||||
#define RPKI_VERSION_1 1
|
||||
#define RPKI_MAX_VERSION RPKI_VERSION_1
|
||||
#define RPKI_VERSION_2 2
|
||||
#define RPKI_MAX_VERSION RPKI_VERSION_2
|
||||
|
||||
|
||||
/*
|
||||
@ -60,6 +61,7 @@ struct rpki_cache {
|
||||
u8 request_session_id; /* 1: have to request new session id; 0: we have already received session id */
|
||||
u32 serial_num; /* Serial number denotes the logical version of data from cache server */
|
||||
u8 version; /* Protocol version */
|
||||
u8 min_version; /* Minimum allowed protocol version */
|
||||
btime last_update; /* Last successful synchronization with cache server */
|
||||
btime last_rx_prefix; /* Last received prefix PDU */
|
||||
|
||||
@ -83,6 +85,9 @@ const char *rpki_cache_state_to_str(enum rpki_cache_state state);
|
||||
void rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr);
|
||||
void rpki_table_remove_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr);
|
||||
|
||||
void rpki_table_add_aspa(struct rpki_cache *cache, struct channel *channel, u32 customer, void *providers, uint providers_length);
|
||||
void rpki_table_remove_aspa(struct rpki_cache *cache, struct channel *channel, u32 customer);
|
||||
|
||||
void rpki_start_refresh(struct rpki_proto *p);
|
||||
void rpki_stop_refresh(struct rpki_proto *p);
|
||||
|
||||
@ -112,6 +117,7 @@ struct rpki_proto {
|
||||
|
||||
struct channel *roa4_channel;
|
||||
struct channel *roa6_channel;
|
||||
struct channel *aspa_channel;
|
||||
u8 refresh_channels; /* For non-incremental updates using rt_refresh_begin(), rt_refresh_end() */
|
||||
};
|
||||
|
||||
@ -129,6 +135,8 @@ struct rpki_config {
|
||||
u8 keep_retry_interval:1; /* Do not overwrite retry interval by cache server update */
|
||||
u8 keep_expire_interval:1; /* Do not overwrite expire interval by cache server update */
|
||||
u8 ignore_max_length:1; /* Ignore received max length and use MAX_PREFIX_LENGTH instead */
|
||||
u8 min_version; /* Minimum version allowed */
|
||||
u8 max_version; /* Maximum version allowed (to start with) */
|
||||
};
|
||||
|
||||
void rpki_check_config(struct rpki_config *cf);
|
||||
|
@ -24,10 +24,16 @@
|
||||
static int
|
||||
rpki_tr_tcp_open(struct rpki_tr_sock *tr)
|
||||
{
|
||||
struct rpki_cache *cache = tr->cache;
|
||||
struct rpki_config *cf = (void *) cache->p->p.cf;
|
||||
struct rpki_tr_tcp_config *tcp_cf = (void *) cf->tr_config.spec;
|
||||
sock *sk = tr->sk;
|
||||
|
||||
sk->type = SK_TCP_ACTIVE;
|
||||
|
||||
if (tcp_cf->auth_type == RPKI_TCP_AUTH_MD5)
|
||||
sk->password = tcp_cf->password;
|
||||
|
||||
if (sk_open(sk, tr->cache->p->p.loop) != 0)
|
||||
return RPKI_TR_ERROR;
|
||||
|
||||
|
@ -56,6 +56,12 @@ enum rpki_tr_type {
|
||||
#endif
|
||||
};
|
||||
|
||||
/* TCP authentication types */
|
||||
enum rpki_tcp_auth {
|
||||
RPKI_TCP_AUTH_NONE,
|
||||
RPKI_TCP_AUTH_MD5
|
||||
};
|
||||
|
||||
/* Common configure structure for transports */
|
||||
struct rpki_tr_config {
|
||||
enum rpki_tr_type type; /* RPKI_TR_TCP or RPKI_TR_SSH */
|
||||
@ -63,7 +69,8 @@ struct rpki_tr_config {
|
||||
};
|
||||
|
||||
struct rpki_tr_tcp_config {
|
||||
/* No internal configuration data */
|
||||
enum rpki_tcp_auth auth_type; /* Authentication type */
|
||||
const char *password; /* Password used for authentication */
|
||||
};
|
||||
|
||||
struct rpki_tr_ssh_config {
|
||||
|
@ -15,6 +15,7 @@ CF_DEFINES
|
||||
#define STATIC_CFG ((struct static_config *) this_proto)
|
||||
static struct static_route *this_srt, *this_snh;
|
||||
static struct f_inst *this_srt_cmds, *this_srt_last_cmd;
|
||||
static uint this_srt_aspa_max;
|
||||
|
||||
static struct static_route *
|
||||
static_nexthop_new(void)
|
||||
@ -47,6 +48,7 @@ CF_DECLS
|
||||
|
||||
CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK, DEV)
|
||||
CF_KEYWORDS(ONLINK, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD, MPLS)
|
||||
CF_KEYWORDS(TRANSIT, PROVIDERS)
|
||||
|
||||
|
||||
CF_GRAMMAR
|
||||
@ -148,8 +150,35 @@ stat_route:
|
||||
| stat_route0 BLACKHOLE { this_srt->dest = RTD_BLACKHOLE; }
|
||||
| stat_route0 UNREACHABLE { this_srt->dest = RTD_UNREACHABLE; }
|
||||
| stat_route0 PROHIBIT { this_srt->dest = RTD_PROHIBIT; }
|
||||
| stat_route0 PROVIDER {
|
||||
if (this_srt->net->type != NET_ASPA) cf_error("Provider settings available only for ASPA");
|
||||
this_srt->aspa = cfg_alloc(sizeof (adata) + (this_srt_aspa_max = 8) * sizeof (u32));
|
||||
this_srt->aspa->length = 0;
|
||||
} stat_aspa_providers
|
||||
| stat_route0 TRANSIT {
|
||||
if (this_srt->net->type != NET_ASPA) cf_error("Transit settings available only for ASPA");
|
||||
/* Allocate an explicit zero */
|
||||
this_srt->aspa = cfg_alloc(sizeof (adata) + sizeof (u32));
|
||||
this_srt->aspa->length = sizeof(u32);
|
||||
((u32 *) this_srt->aspa->data)[0] = 0;
|
||||
}
|
||||
;
|
||||
|
||||
stat_aspa_provider: NUM {
|
||||
if (this_srt->aspa->length == this_srt_aspa_max * sizeof(u32))
|
||||
{
|
||||
adata *new = cfg_alloc(sizeof (adata) + (this_srt_aspa_max * 2) * sizeof (u32));
|
||||
memcpy(new, this_srt->aspa, this_srt->aspa->length + sizeof(adata));
|
||||
this_srt->aspa = new;
|
||||
this_srt_aspa_max *= 2;
|
||||
}
|
||||
|
||||
((u32 *) this_srt->aspa->data)[this_srt->aspa->length / sizeof(u32)] = $1;
|
||||
this_srt->aspa->length += sizeof(u32);
|
||||
}
|
||||
|
||||
stat_aspa_providers: stat_aspa_provider | stat_aspa_providers ',' stat_aspa_provider ;
|
||||
|
||||
stat_route_item:
|
||||
cmd {
|
||||
if (this_srt_last_cmd)
|
||||
|
@ -112,6 +112,26 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
|
||||
else if (r->dest)
|
||||
ea_set_dest(&ea, 0, r->dest);
|
||||
|
||||
if (r->net->type == NET_ASPA)
|
||||
{
|
||||
if (r->dest != RTD_NONE)
|
||||
{
|
||||
log(L_WARN "%s: ASPA %u configured with nexthop, ignoring the nexthop",
|
||||
p->p.name, ((struct net_addr_aspa *) r->net)->asn);
|
||||
r->dest = RTD_NONE;
|
||||
}
|
||||
|
||||
if (!r->aspa)
|
||||
{
|
||||
log(L_WARN "%s: ASPA %u configured with no provider list, ignoring the whole rule",
|
||||
p->p.name, ((struct net_addr_aspa *) r->net)->asn);
|
||||
goto withdraw;
|
||||
}
|
||||
|
||||
ea_set_attr(&ea, EA_LITERAL_DIRECT_ADATA(
|
||||
&ea_gen_aspa_providers, 0, r->aspa));
|
||||
}
|
||||
|
||||
if (p->p.mpls_channel)
|
||||
{
|
||||
struct mpls_channel *mc = (void *) p->p.mpls_channel;
|
||||
|
@ -50,7 +50,10 @@ struct static_route {
|
||||
byte use_bfd; /* Configured to use BFD */
|
||||
uint mpls_label; /* Local MPLS label, -1 if unused */
|
||||
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
|
||||
struct adata *mls; /* MPLS label stack; may be NULL */
|
||||
union {
|
||||
adata *mls; /* MPLS label stack; may be NULL */
|
||||
adata *aspa; /* ASPA provider list; may be NULL */
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -309,3 +309,9 @@ sk_set_freebind(sock *s)
|
||||
{
|
||||
ERR_MSG("Freebind is not supported");
|
||||
}
|
||||
|
||||
static inline int
|
||||
sk_set_udp6_no_csum_rx(sock *s)
|
||||
{
|
||||
ERR_MSG("UDPv6 zero checksum is not supported");
|
||||
}
|
||||
|
@ -51,6 +51,8 @@ struct nl_parse_state
|
||||
#define KRT_FEATURES_MAX 4
|
||||
|
||||
static void krt_bitfield_format(const eattr *e, byte *buf, uint buflen);
|
||||
static struct f_val krt_bitfield_empty(const struct ea_class *cls UNUSED)
|
||||
{ return (struct f_val) { .type = T_INT }; }
|
||||
|
||||
static struct ea_class
|
||||
ea_krt_prefsrc = {
|
||||
@ -71,11 +73,13 @@ static struct ea_class ea_krt_metrics[] = {
|
||||
.name = "krt_lock",
|
||||
.type = T_INT,
|
||||
.format = krt_bitfield_format,
|
||||
.empty = krt_bitfield_empty,
|
||||
},
|
||||
[RTAX_FEATURES] = {
|
||||
.name = "krt_features",
|
||||
.type = T_INT,
|
||||
.format = krt_bitfield_format,
|
||||
.empty = krt_bitfield_empty,
|
||||
},
|
||||
[RTAX_CC_ALGO] = {
|
||||
.name = "krt_congctl",
|
||||
@ -1589,8 +1593,24 @@ done:
|
||||
}
|
||||
|
||||
static inline int
|
||||
nl_allow_replace(struct krt_proto *p, rte *new)
|
||||
nl_allow_replace(struct krt_proto *p, const rte *new, const rte *old)
|
||||
{
|
||||
/*
|
||||
* In kernel routing tables, (net, metric) is the primary key. Therefore, we
|
||||
* can use NL_OP_REPLACE only if the new and and the old route have the same
|
||||
* kernel metric, otherwise the replace would just add the new route while
|
||||
* keep the old one.
|
||||
*/
|
||||
|
||||
if ((p->af != AF_MPLS) && (KRT_CF->sys.metric == 0))
|
||||
{
|
||||
uint new_metric = ea_get_int(new->attrs, &ea_krt_metric, 0);
|
||||
uint old_metric = ea_get_int(old->attrs, &ea_krt_metric, 0);
|
||||
|
||||
if (new_metric != old_metric)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We use NL_OP_REPLACE for IPv4, it has an issue with not checking for
|
||||
* matching rtm_protocol, but that is OK when dedicated priority is used.
|
||||
@ -1622,7 +1642,7 @@ krt_replace_rte(struct krt_proto *p, const net_addr *n UNUSED, rte *new, const r
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (old && new && nl_allow_replace(p, new))
|
||||
if (old && new && nl_allow_replace(p, new, old))
|
||||
{
|
||||
err = nl_send_route(p, new, NL_OP_REPLACE);
|
||||
}
|
||||
|
@ -285,3 +285,14 @@ sk_set_freebind(sock *s)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
sk_set_udp6_no_csum_rx(sock *s)
|
||||
{
|
||||
int y = 1;
|
||||
|
||||
if (setsockopt(s->fd, SOL_UDP, UDP_NO_CHECK6_RX, &y, sizeof(y)) < 0)
|
||||
ERR("UDP_NO_CHECK6_RX");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -14,17 +14,17 @@ CF_HDR
|
||||
CF_DEFINES
|
||||
|
||||
static struct log_config *this_log;
|
||||
static struct cli_config *this_cli_config;
|
||||
|
||||
CF_DECLS
|
||||
|
||||
CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT, UDP, PORT)
|
||||
CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT, UDP, PORT, CLI)
|
||||
CF_KEYWORDS(NAME, CONFIRM, UNDO, CHECK, TIMEOUT, DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING, STATUS)
|
||||
CF_KEYWORDS(PING, WAKEUP, SOCKETS, SCHEDULING, EVENTS, TIMERS, ALLOCATOR)
|
||||
CF_KEYWORDS(GRACEFUL, RESTART, FIXED)
|
||||
|
||||
%type <i> log_mask log_mask_list log_cat cfg_timeout debug_unix latency_debug_mask latency_debug_flag latency_debug_list
|
||||
%type <t> cfg_name
|
||||
%type <tf> timeformat_which
|
||||
%type <t> syslog_name
|
||||
|
||||
CF_GRAMMAR
|
||||
@ -122,6 +122,28 @@ mrtdump_base:
|
||||
}
|
||||
;
|
||||
|
||||
conf: cli ;
|
||||
|
||||
cli: CLI text cli_opts {
|
||||
this_cli_config->name = $2;
|
||||
cli_config_add_tail(&new_config->cli, this_cli_config);
|
||||
this_cli_config = NULL;
|
||||
} ;
|
||||
|
||||
cli_opts: cli_opts_begin '{' cli_opts_block '}' ';' | cli_opts_begin ';' ;
|
||||
|
||||
cli_opts_begin: {
|
||||
this_cli_config = cfg_alloc(sizeof *this_cli_config);
|
||||
*this_cli_config = (typeof (*this_cli_config)) {
|
||||
.config = new_config,
|
||||
.mode = 0660,
|
||||
};
|
||||
};
|
||||
|
||||
cli_opts_block:
|
||||
/* EMPTY */ |
|
||||
cli_opts_block RESTRICT { this_cli_config->restricted = 1; }
|
||||
;
|
||||
|
||||
conf: THREADS expr {
|
||||
if ($2 < 1) cf_error("Number of threads must be at least one.");
|
||||
|
@ -668,6 +668,40 @@ sk_set_high_port(sock *s UNUSED)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
sk_set_min_rcvbuf_(sock *s, int bufsize)
|
||||
{
|
||||
int oldsize = 0, oldsize_s = sizeof(oldsize);
|
||||
|
||||
if (getsockopt(s->fd, SOL_SOCKET, SO_RCVBUF, &oldsize, &oldsize_s) < 0)
|
||||
ERR("SO_RCVBUF");
|
||||
|
||||
if (oldsize >= bufsize)
|
||||
return 0;
|
||||
|
||||
bufsize = BIRD_ALIGN(bufsize, 64);
|
||||
if (setsockopt(s->fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) < 0)
|
||||
ERR("SO_RCVBUF");
|
||||
|
||||
/*
|
||||
int newsize = 0, newsize_s = sizeof(newsize);
|
||||
if (getsockopt(s->fd, SOL_SOCKET, SO_RCVBUF, &newsize, &newsize_s) < 0)
|
||||
ERR("SO_RCVBUF");
|
||||
|
||||
log(L_INFO "Setting rcvbuf on %s from %d to %d",
|
||||
s->iface ? s->iface->name : "*", oldsize, newsize);
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sk_set_min_rcvbuf(sock *s, int bufsize)
|
||||
{
|
||||
if (sk_set_min_rcvbuf_(s, bufsize) < 0)
|
||||
log(L_WARN "Socket error: %s%#m", s->err);
|
||||
}
|
||||
|
||||
static inline byte *
|
||||
sk_skip_ip_header(byte *pkt, int *len)
|
||||
{
|
||||
@ -999,6 +1033,9 @@ sk_set_rbsize(sock *s, uint val)
|
||||
xfree(s->rbuf_alloc);
|
||||
s->rbuf_alloc = xmalloc(val);
|
||||
s->rpos = s->rbuf = s->rbuf_alloc;
|
||||
|
||||
if ((s->type == SK_UDP) || (s->type == SK_IP))
|
||||
sk_set_min_rcvbuf(s, s->rbsize);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1108,10 +1145,11 @@ sk_setup(sock *s)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s->vrf && (s->vrf != &default_vrf) && !s->iface)
|
||||
if (s->vrf && (s->vrf != &default_vrf) && !s->iface && (s->type != SK_TCP))
|
||||
{
|
||||
/* Bind socket to associated VRF interface.
|
||||
This is Linux-specific, but so is SO_BINDTODEVICE. */
|
||||
This is Linux-specific, but so is SO_BINDTODEVICE.
|
||||
For accepted TCP sockets it is inherited from the listening one. */
|
||||
#ifdef SO_BINDTODEVICE
|
||||
struct ifreq ifr = {};
|
||||
strcpy(ifr.ifr_name, s->vrf->name);
|
||||
@ -1183,6 +1221,10 @@ sk_setup(sock *s)
|
||||
if (s->tos >= 0)
|
||||
if (sk_set_tos6(s, s->tos) < 0)
|
||||
return -1;
|
||||
|
||||
if ((s->flags & SKF_UDP6_NO_CSUM_RX) && (s->type == SK_UDP))
|
||||
if (sk_set_udp6_no_csum_rx(s) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Must be after sk_set_tos4() as setting ToS on Linux also mangles priority */
|
||||
@ -1190,6 +1232,9 @@ sk_setup(sock *s)
|
||||
if (sk_set_priority(s, s->priority) < 0)
|
||||
return -1;
|
||||
|
||||
if ((s->type == SK_UDP) || (s->type == SK_IP))
|
||||
sk_set_min_rcvbuf(s, s->rbsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1680,7 +1725,7 @@ err:
|
||||
}
|
||||
|
||||
int
|
||||
sk_open_unix(sock *s, struct birdloop *loop, char *name)
|
||||
sk_open_unix(sock *s, struct birdloop *loop, const char *name)
|
||||
{
|
||||
struct sockaddr_un sa;
|
||||
int fd;
|
||||
@ -2572,7 +2617,7 @@ io_loop(void)
|
||||
}
|
||||
|
||||
void
|
||||
test_old_bird(char *path)
|
||||
test_old_bird(const char *path)
|
||||
{
|
||||
int fd;
|
||||
struct sockaddr_un sa;
|
||||
|
@ -51,7 +51,7 @@ static struct log_channel * _Atomic global_logs;
|
||||
/* Logging flags to validly prepare logging messages */
|
||||
|
||||
static _Atomic uint logging_flags;
|
||||
static _Atomic uint logging_mask;
|
||||
static _Atomic uint logging_mask = ~0U;
|
||||
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
#include <sys/syslog.h>
|
||||
@ -189,9 +189,20 @@ log_commit(log_buffer *buf)
|
||||
memcpy(buf->buf.end - sizeof TOO_LONG, TOO_LONG, sizeof TOO_LONG);
|
||||
#undef TOO_LONG
|
||||
|
||||
struct log_channel *glogs = atomic_load_explicit(&global_logs, memory_order_acquire);
|
||||
if (!glogs)
|
||||
{
|
||||
static struct log_channel initial_stderr_log = {
|
||||
.rf = &rf_stderr,
|
||||
.mask = ~0,
|
||||
.prepare = BIT32_ALL(LBPP_TERMINAL, LBP_CLASS, LBP_MSG),
|
||||
};
|
||||
|
||||
glogs = &initial_stderr_log;
|
||||
}
|
||||
|
||||
for (
|
||||
struct log_channel *l = atomic_load_explicit(&global_logs, memory_order_acquire);
|
||||
l;
|
||||
struct log_channel *l = glogs; l;
|
||||
l = atomic_load_explicit(&l->next, memory_order_acquire)
|
||||
)
|
||||
{
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "lib/event.h"
|
||||
#include "lib/locking.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/tlists.h"
|
||||
#include "lib/string.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
@ -186,6 +187,8 @@ cf_read(byte *dest, uint len, int fd)
|
||||
return l;
|
||||
}
|
||||
|
||||
static void cli_preconfig(struct config *c);
|
||||
|
||||
void
|
||||
sysdep_preconfig(struct config *c)
|
||||
{
|
||||
@ -200,14 +203,19 @@ sysdep_preconfig(struct config *c)
|
||||
read_iproute_table(c, PATH_IPROUTE_DIR "/rt_scopes", "ips_", 255);
|
||||
read_iproute_table(c, PATH_IPROUTE_DIR "/rt_tables", "ipt_", 0xffffffff);
|
||||
#endif
|
||||
|
||||
cli_preconfig(c);
|
||||
}
|
||||
|
||||
static void cli_commit(struct config *new, struct config *old);
|
||||
|
||||
void
|
||||
sysdep_commit(struct config *new, struct config *old)
|
||||
{
|
||||
if (!new->shutdown)
|
||||
log_switch(0, &new->logfiles, new->syslog_name);
|
||||
|
||||
cli_commit(new, old);
|
||||
bird_thread_commit(new, old);
|
||||
}
|
||||
|
||||
@ -400,9 +408,28 @@ cmd_reconfig_status(void)
|
||||
* Command-Line Interface
|
||||
*/
|
||||
|
||||
static sock *cli_sk;
|
||||
static char *path_control_socket = PATH_CONTROL_SOCKET;
|
||||
static struct cli_config initial_control_socket_config = {
|
||||
.name = PATH_CONTROL_SOCKET,
|
||||
.mode = 0660,
|
||||
};
|
||||
#define path_control_socket initial_control_socket_config.name
|
||||
|
||||
static struct cli_config *main_control_socket_config = NULL;
|
||||
|
||||
#define TLIST_PREFIX cli_listener
|
||||
#define TLIST_TYPE struct cli_listener
|
||||
#define TLIST_ITEM n
|
||||
#define TLIST_WANT_ADD_TAIL
|
||||
#define TLIST_WANT_WALK
|
||||
static struct cli_listener {
|
||||
TLIST_DEFAULT_NODE;
|
||||
sock *s;
|
||||
struct cli_config *config;
|
||||
} *main_control_socket = NULL;
|
||||
|
||||
#include "lib/tlists.h"
|
||||
|
||||
static TLIST_LIST(cli_listener) cli_listeners;
|
||||
|
||||
static void
|
||||
cli_write(cli *c)
|
||||
@ -523,7 +550,7 @@ cli_connect(sock *s, uint size UNUSED)
|
||||
s->rx_hook = cli_rx;
|
||||
s->tx_hook = cli_tx;
|
||||
s->err_hook = cli_err;
|
||||
s->data = c = cli_new(s);
|
||||
s->data = c = cli_new(s, ((struct cli_listener *) s->data)->config);
|
||||
s->pool = c->pool; /* We need to have all the socket buffers allocated in the cli pool */
|
||||
s->fast_rx = 1;
|
||||
c->rx_pos = c->rx_buf;
|
||||
@ -531,33 +558,116 @@ cli_connect(sock *s, uint size UNUSED)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
cli_init_unix(uid_t use_uid, gid_t use_gid)
|
||||
static struct cli_listener *
|
||||
cli_listen(struct cli_config *cf)
|
||||
{
|
||||
sock *s;
|
||||
|
||||
cli_init();
|
||||
s = cli_sk = sk_new(cli_pool);
|
||||
struct cli_listener *l = mb_allocz(cli_pool, sizeof *l);
|
||||
l->config = cf;
|
||||
sock *s = l->s = sk_new(cli_pool);
|
||||
s->type = SK_UNIX_PASSIVE;
|
||||
s->rx_hook = cli_connect;
|
||||
s->err_hook = cli_connect_err;
|
||||
s->data = l;
|
||||
s->rbsize = 1024;
|
||||
s->fast_rx = 1;
|
||||
|
||||
/* Return value intentionally ignored */
|
||||
unlink(path_control_socket);
|
||||
unlink(cf->name);
|
||||
|
||||
if (sk_open_unix(s, &main_birdloop, path_control_socket) < 0)
|
||||
die("Cannot create control socket %s: %m", path_control_socket);
|
||||
if (sk_open_unix(s, &main_birdloop, cf->name) < 0)
|
||||
{
|
||||
log(L_ERR "Cannot create control socket %s: %m", cf->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (use_uid || use_gid)
|
||||
if (chown(path_control_socket, use_uid, use_gid) < 0)
|
||||
die("chown: %m");
|
||||
if (cf->uid || cf->gid)
|
||||
if (chown(cf->name, cf->uid, cf->gid) < 0)
|
||||
{
|
||||
log(L_ERR "Cannot chown control socket %s: %m", cf->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (chmod(path_control_socket, 0660) < 0)
|
||||
die("chmod: %m");
|
||||
if (chmod(cf->name, cf->mode) < 0)
|
||||
{
|
||||
log(L_ERR "Cannot chmod control socket %s: %m", cf->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cli_listener_add_tail(&cli_listeners, l);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static void
|
||||
cli_deafen(struct cli_listener *l)
|
||||
{
|
||||
rfree(l->s);
|
||||
unlink(l->config->name);
|
||||
cli_listener_rem_node(&cli_listeners, l);
|
||||
mb_free(l);
|
||||
}
|
||||
|
||||
static void
|
||||
cli_init_unix(uid_t use_uid, gid_t use_gid)
|
||||
{
|
||||
ASSERT_DIE(main_control_socket_config == NULL);
|
||||
|
||||
main_control_socket_config = &initial_control_socket_config;
|
||||
main_control_socket_config->uid = use_uid;
|
||||
main_control_socket_config->gid = use_gid;
|
||||
|
||||
ASSERT_DIE(main_control_socket == NULL);
|
||||
main_control_socket = cli_listen(main_control_socket_config);
|
||||
if (!main_control_socket)
|
||||
die("Won't run without control socket");
|
||||
}
|
||||
|
||||
static void
|
||||
cli_preconfig(struct config *c)
|
||||
{
|
||||
if (!main_control_socket_config)
|
||||
return;
|
||||
|
||||
struct cli_config *ccf = mb_alloc(cli_pool, sizeof *ccf);
|
||||
memcpy(ccf, main_control_socket_config, sizeof *ccf);
|
||||
ccf->n = (struct cli_config_node) {};
|
||||
ccf->config = c;
|
||||
cli_config_add_tail(&c->cli, ccf);
|
||||
}
|
||||
|
||||
static void
|
||||
cli_commit(struct config *new, struct config *old)
|
||||
{
|
||||
if (new->shutdown)
|
||||
{
|
||||
/* Keep the main CLI throughout the shutdown */
|
||||
initial_control_socket_config.config = new;
|
||||
main_control_socket->config = &initial_control_socket_config;
|
||||
}
|
||||
|
||||
WALK_TLIST(cli_config, c, &new->cli)
|
||||
{
|
||||
_Bool seen = 0;
|
||||
WALK_TLIST(cli_listener, l, &cli_listeners)
|
||||
if (l->config->config != new)
|
||||
if (!strcmp(l->config->name, c->name))
|
||||
{
|
||||
ASSERT_DIE(l->config->config == old);
|
||||
l->config = c;
|
||||
seen = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!seen)
|
||||
cli_listen(c);
|
||||
}
|
||||
|
||||
WALK_TLIST_DELSAFE(cli_listener, l, &cli_listeners)
|
||||
if (l->config->config != new)
|
||||
cli_deafen(l);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PID file
|
||||
*/
|
||||
@ -635,7 +745,7 @@ void
|
||||
sysdep_shutdown_done(void)
|
||||
{
|
||||
unlink_pid_file();
|
||||
unlink(path_control_socket);
|
||||
cli_deafen(main_control_socket);
|
||||
log_msg(L_FATAL "Shutdown completed");
|
||||
exit(0);
|
||||
}
|
||||
@ -920,6 +1030,8 @@ main(int argc, char **argv)
|
||||
uid_t use_uid = get_uid(use_user);
|
||||
gid_t use_gid = get_gid(use_group);
|
||||
|
||||
cli_init();
|
||||
|
||||
if (!parse_and_exit)
|
||||
{
|
||||
test_old_bird(path_control_socket);
|
||||
|
@ -114,7 +114,7 @@ extern volatile sig_atomic_t async_shutdown_flag;
|
||||
void io_init(void);
|
||||
void io_loop(void);
|
||||
void io_log_dump(void);
|
||||
int sk_open_unix(struct birdsock *s, struct birdloop *, char *name);
|
||||
int sk_open_unix(struct birdsock *s, struct birdloop *, const char *name);
|
||||
|
||||
enum rf_mode {
|
||||
RF_APPEND = 1,
|
||||
@ -130,7 +130,7 @@ int rf_fileno(struct rfile *f);
|
||||
|
||||
extern struct rfile rf_stderr;
|
||||
|
||||
void test_old_bird(char *path);
|
||||
void test_old_bird(const char *path);
|
||||
ip_addr resolve_hostname(const char *host, int type, const char **err_msg);
|
||||
|
||||
/* krt.c bits */
|
||||
|
Loading…
Reference in New Issue
Block a user