0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-10-18 09:58:43 +00:00

Merge tag 'v1.6.0' into bgpsec-mbaer

v1.6.0
This commit is contained in:
Pavel Tvrdik 2016-06-02 10:24:34 +02:00
commit f857f67485
121 changed files with 10942 additions and 2611 deletions

17
NEWS
View File

@ -1,3 +1,20 @@
Version 1.6.0 (2016-04-29)
o Major RIP protocol redesign
o New Babel routing protocol
o BGP multipath support
o KRT: Add support for plenty of kernel route metrics
o KRT: Allow more than 256 routing tables
o Static: Allow to specify attributes for static routes
o Static: Support for BFD controlled static routes
o FreeBSD: Setup password for BGP MD5 authentication
o IO: Remove socket number limit
o Plenty of bug fixes
Upgrade notes:
For RIP, most protocol options were moved to interface blocks.
Version 1.5.0 (2015-04-20)
o Major OSPF protocol redesign.
o OSPFv2 multi-instance extension (RFC 6549).

1
README
View File

@ -64,6 +64,7 @@ What do we support:
o Static routes
o Inter-table protocol
o IPv6 router advertisements
o Bidirectional Forwarding Detection (BFD)
o Command-line interface (using the `birdc' client; to get
some help, just press `?')
o Soft reconfiguration -- no online commands for changing the

View File

@ -37,7 +37,7 @@
#define SERVER_READ_BUF_LEN 4096
static char *opt_list = "s:vr";
static char *opt_list = "s:vrl";
static int verbose, restricted, once;
static char *init_cmd;
@ -59,13 +59,14 @@ int term_lns, term_cls;
static void
usage(char *name)
{
fprintf(stderr, "Usage: %s [-s <control-socket>] [-v] [-r]\n", name);
fprintf(stderr, "Usage: %s [-s <control-socket>] [-v] [-r] [-l]\n", name);
exit(1);
}
static void
parse_args(int argc, char **argv)
{
int server_changed = 0;
int c;
while ((c = getopt(argc, argv, opt_list)) >= 0)
@ -73,6 +74,7 @@ parse_args(int argc, char **argv)
{
case 's':
server_path = optarg;
server_changed = 1;
break;
case 'v':
verbose++;
@ -80,6 +82,10 @@ parse_args(int argc, char **argv)
case 'r':
restricted = 1;
break;
case 'l':
if (!server_changed)
server_path = xbasename(server_path);
break;
default:
usage(argv[0]);
}

View File

@ -38,7 +38,7 @@ static struct cmd_node cmd_root;
void
cmd_build_tree(void)
{
unsigned int i;
uint i;
cmd_root.plastson = &cmd_root.son;
@ -67,7 +67,7 @@ cmd_build_tree(void)
new->plastson = &new->son;
new->len = c-d;
memcpy(new->token, d, c-d);
new->prio = (new->len == 3 && !memcmp(new->token, "roa", 3)) ? 0 : 1; /* Hack */
new->prio = (new->len == 3 && (!memcmp(new->token, "roa", 3) || !memcmp(new->token, "rip", 3))) ? 0 : 1; /* Hack */
}
old = new;
while (isspace(*c))

View File

@ -70,7 +70,7 @@ struct sym_scope {
static struct sym_scope *conf_this_scope;
static int cf_hash(byte *c);
static struct symbol *cf_find_sym(byte *c, unsigned int h0);
static inline struct symbol * cf_get_sym(byte *c, uint h0);
linpool *cfg_mem;
@ -194,7 +194,7 @@ else: {
}
k=k->next;
}
cf_lval.s = cf_find_sym(yytext, h);
cf_lval.s = cf_get_sym(yytext, h);
return SYM;
}
@ -426,8 +426,9 @@ check_eof(void)
}
static struct symbol *
cf_new_sym(byte *c, unsigned int h)
cf_new_sym(byte *c, uint h0)
{
uint h = h0 & (SYM_HASH_SIZE-1);
struct symbol *s, **ht;
int l;
@ -449,56 +450,77 @@ cf_new_sym(byte *c, unsigned int h)
}
static struct symbol *
cf_find_sym(byte *c, unsigned int h0)
cf_find_sym(struct config *cfg, byte *c, uint h0)
{
unsigned int h = h0 & (SYM_HASH_SIZE-1);
uint h = h0 & (SYM_HASH_SIZE-1);
struct symbol *s, **ht;
if (ht = new_config->sym_hash)
if (ht = cfg->sym_hash)
{
for(s = ht[h]; s; s=s->next)
if (!strcmp(s->name, c) && s->scope->active)
return s;
}
if (new_config->sym_fallback)
if (ht = cfg->sym_fallback)
{
/* We know only top-level scope is active */
for(s = new_config->sym_fallback[h]; s; s=s->next)
for(s = ht[h]; s; s=s->next)
if (!strcmp(s->name, c) && s->scope->active)
return s;
}
return cf_new_sym(c, h);
return NULL;
}
static inline struct symbol *
cf_get_sym(byte *c, uint h0)
{
return cf_find_sym(new_config, c, h0) ?: cf_new_sym(c, h0);
}
/**
* cf_find_symbol - find a symbol by name
* @cfg: specificed config
* @c: symbol name
*
* This functions searches the symbol table for a symbol of given
* name. First it examines the current scope, then the second recent
* one and so on until it either finds the symbol and returns a pointer
* to its &symbol structure or reaches the end of the scope chain
* and returns %NULL to signify no match.
* This functions searches the symbol table in the config @cfg for a symbol of
* given name. First it examines the current scope, then the second recent one
* and so on until it either finds the symbol and returns a pointer to its
* &symbol structure or reaches the end of the scope chain and returns %NULL to
* signify no match.
*/
struct symbol *
cf_find_symbol(byte *c)
cf_find_symbol(struct config *cfg, byte *c)
{
return cf_find_sym(c, cf_hash(c));
return cf_find_sym(cfg, c, cf_hash(c));
}
/**
* cf_get_symbol - get a symbol by name
* @c: symbol name
*
* This functions searches the symbol table of the currently parsed config
* (@new_config) for a symbol of given name. It returns either the already
* existing symbol or a newly allocated undefined (%SYM_VOID) symbol if no
* existing symbol is found.
*/
struct symbol *
cf_get_symbol(byte *c)
{
return cf_get_sym(c, cf_hash(c));
}
struct symbol *
cf_default_name(char *template, int *counter)
{
char buf[32];
char buf[SYM_MAX_LEN];
struct symbol *s;
char *perc = strchr(template, '%');
for(;;)
{
bsprintf(buf, template, ++(*counter));
s = cf_find_sym(buf, cf_hash(buf));
if (!s)
break;
s = cf_get_sym(buf, cf_hash(buf));
if (s->class == SYM_VOID)
return s;
if (!perc)
@ -529,7 +551,7 @@ cf_define_symbol(struct symbol *sym, int type, void *def)
{
if (sym->scope == conf_this_scope)
cf_error("Symbol already defined");
sym = cf_new_sym(sym->name, cf_hash(sym->name) & (SYM_HASH_SIZE-1));
sym = cf_new_sym(sym->name, cf_hash(sym->name));
}
sym->class = type;
sym->def = def;

View File

@ -20,19 +20,19 @@
*
* There can exist up to four different configurations at one time: an active
* one (pointed to by @config), configuration we are just switching from
* (@old_config), one queued for the next reconfiguration (@future_config;
* if there is one and the user wants to reconfigure once again, we just
* free the previous queued config and replace it with the new one) and
* finally a config being parsed (@new_config). The stored @old_config
* is also used for undo reconfiguration, which works in a similar way.
* Reconfiguration could also have timeout (using @config_timer) and undo
* is automatically called if the new configuration is not confirmed later.
* (@old_config), one queued for the next reconfiguration (@future_config; if
* there is one and the user wants to reconfigure once again, we just free the
* previous queued config and replace it with the new one) and finally a config
* being parsed (@new_config). The stored @old_config is also used for undo
* reconfiguration, which works in a similar way. Reconfiguration could also
* have timeout (using @config_timer) and undo is automatically called if the
* new configuration is not confirmed later. The new config (@new_config) and
* associated linear pool (@cfg_mem) is non-NULL only during parsing.
*
* Loading of new configuration is very simple: just call config_alloc()
* to get a new &config structure, then use config_parse() to parse a
* configuration file and fill all fields of the structure
* and finally ask the config manager to switch to the new
* config by calling config_commit().
* Loading of new configuration is very simple: just call config_alloc() to get
* a new &config structure, then use config_parse() to parse a configuration
* file and fill all fields of the structure and finally ask the config manager
* to switch to the new config by calling config_commit().
*
* CLI commands are parsed in a very similar way -- there is also a stripped-down
* &config structure associated with them and they are lex-ed and parsed by the
@ -91,10 +91,15 @@ config_alloc(byte *name)
linpool *l = lp_new(p, 4080);
struct config *c = lp_allocz(l, sizeof(struct config));
/* Duplication of name string in local linear pool */
uint nlen = strlen(name) + 1;
char *ndup = lp_allocu(l, nlen);
memcpy(ndup, name, nlen);
c->mrtdump_file = -1; /* Hack, this should be sysdep-specific */
c->pool = p;
cfg_mem = c->mem = l;
c->file_name = cfg_strdup(name);
c->mem = l;
c->file_name = ndup;
c->load_time = now;
c->tf_route = c->tf_proto = (struct timeformat){"%T", "%F", 20*3600};
c->tf_base = c->tf_log = (struct timeformat){"%F %T", NULL, 0};
@ -119,11 +124,13 @@ config_alloc(byte *name)
int
config_parse(struct config *c)
{
int done = 0;
DBG("Parsing configuration file `%s'\n", c->file_name);
new_config = c;
cfg_mem = c->mem;
if (setjmp(conf_jmpbuf))
return 0;
goto cleanup;
cf_lex_init(0, c);
sysdep_preconfig(c);
protos_preconfig(c);
@ -137,7 +144,12 @@ config_parse(struct config *c)
if (!c->router_id)
cf_error("Router ID must be configured manually on IPv6 routers");
#endif
return 1;
done = 1;
cleanup:
new_config = NULL;
cfg_mem = NULL;
return done;
}
/**
@ -150,14 +162,22 @@ config_parse(struct config *c)
int
cli_parse(struct config *c)
{
new_config = c;
int done = 0;
c->sym_fallback = config->sym_hash;
new_config = c;
cfg_mem = c->mem;
if (setjmp(conf_jmpbuf))
return 0;
goto cleanup;
cf_lex_init(1, c);
cf_parse();
return 1;
done = 1;
cleanup:
c->sym_fallback = NULL;
new_config = NULL;
cfg_mem = NULL;
return done;
}
/**
@ -237,10 +257,6 @@ config_do_commit(struct config *c, int type)
if (old_config && !config->shutdown)
log(L_INFO "Reconfiguring");
/* This should not be necessary, but it seems there are some
functions that access new_config instead of config */
new_config = config;
if (old_config)
old_config->obstacle_count++;
@ -254,9 +270,6 @@ config_do_commit(struct config *c, int type)
DBG("protos_commit\n");
protos_commit(c, old_config, force_restart, type);
/* Just to be sure nobody uses that now */
new_config = NULL;
int obs = 0;
if (old_config)
obs = --old_config->obstacle_count;

View File

@ -100,7 +100,7 @@ void cfg_copy_list(list *dest, list *src, unsigned node_size);
/* Lexer */
extern int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
extern int (*cf_read_hook)(byte *buf, uint max, int fd);
struct symbol {
struct symbol *next;
@ -147,7 +147,9 @@ int cf_lex(void);
void cf_lex_init(int is_cli, struct config *c);
void cf_lex_unwind(void);
struct symbol *cf_find_symbol(byte *c);
struct symbol *cf_find_symbol(struct config *cfg, byte *c);
struct symbol *cf_get_symbol(byte *c);
struct symbol *cf_default_name(char *template, int *counter);
struct symbol *cf_define_symbol(struct symbol *symbol, int type, void *def);
void cf_push_scope(struct symbol *);

View File

@ -85,12 +85,12 @@ if test -z "$GCC" ; then
fi
# Enable threads by default just in Linux and FreeBSD
if test "$enable_pthreads" = try ; then
case "$host_os" in
(linux* | freebsd*) enable_pthreads=try ;;
(*) enable_pthreads=no ;;
esac
fi
#if test "$enable_pthreads" = try ; then
# case "$host_os" in
# (linux* | freebsd* | openbsd* | netbsd* ) enable_pthreads=try ;;
# (*) enable_pthreads=no ;;
# esac
#fi
if test "$enable_pthreads" != no ; then
BIRD_CHECK_PTHREADS
@ -206,6 +206,9 @@ fi
AC_SUBST(iproutedir)
all_protocols="$proto_bfd bgp ospf pipe $proto_radv rip static"
if test "$ip" = ipv6 ; then
all_protocols="$all_protocols babel"
fi
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
if test "$with_protocols" = all ; then

View File

@ -171,6 +171,11 @@ BIRD executable by configuring out routing protocols you don't use, and
<tag>-f</tag>
run bird in foreground.
<tag>-l</tag>
look for a configuration file and a communication socket in the current
working directory instead of in default system locations. However, paths
specified by options <cf/-c/, <cf/-s/ have higher priority.
<tag>-R</tag>
apply graceful restart recovery after start.
</descrip>
@ -318,8 +323,9 @@ protocol rip {
<p><descrip>
<tag>include "<m/filename/"</tag>
This statement causes inclusion of a new file. <m/Filename/ could also
be a wildcard. The maximal depth is 8. Note that this statement could be
used anywhere in the config file, not just as a top-level option.
be a wildcard, in that case matching files are included in alphabetic
order. The maximal depth is 8. Note that this statement could be used
anywhere in the config file, not just as a top-level option.
<tag><label id="dsc-log">log "<m/filename/"|syslog [name <m/name/]|stderr all|{ <m/list of classes/ }</tag>
Set logging of messages having the given class (either <cf/all/ or
@ -711,6 +717,10 @@ This argument can be omitted if there exists only a single instance.
Show router status, that is BIRD version, uptime and time from last
reconfiguration.
<tag>show interfaces [summary]</tag>
Show the list of interfaces. For each interface, print its type, state,
MTU and addresses assigned.
<tag>show protocols [all]</tag>
Show list of protocol instances along with tables they are connected to
and protocol status, possibly giving verbose information, if <cf/all/ is
@ -738,16 +748,18 @@ This argument can be omitted if there exists only a single instance.
Show contents of an OSPF LSA database. Options could be used to filter
entries.
<tag>show rip interfaces [<m/name/] ["<m/interface/"]</tag>
Show detailed information about RIP interfaces.
<tag>show rip neighbors [<m/name/] ["<m/interface/"]</tag>
Show a list of RIP neighbors and associated state.
<tag>show static [<m/name/]</tag>
Show detailed information about static routes.
<tag>show bfd sessions [<m/name/]</tag>
Show information about BFD sessions.
<tag>show interfaces [summary]</tag>
Show the list of interfaces. For each interface, print its type, state,
MTU and addresses assigned.
<tag>show symbols [table|filter|function|protocol|template|roa|<m/symbol/]</tag>
Show the list of symbols defined in the configuration (names of
protocols, routing tables etc.).
@ -1119,9 +1131,12 @@ foot).
<cf><m/P/.last</cf> returns the last ASN (the source ASN) in path <m/P/.
<cf><m/P/.last_nonaggregated</cf> returns the last ASN in the non-aggregated part of the path <m/P/.
Both <cf/first/ and <cf/last/ return zero if there is no appropriate
ASN, for example if the path contains an AS set element as the first (or
the last) part.
the last) part. If the path ends with an AS set, <cf/last_nonaggregated/
may be used to get last ASN before any AS set.
<cf><m/P/.len</cf> returns the length of path <m/P/.
@ -1365,6 +1380,102 @@ corresponding protocol sections.
<chapt>Protocols
<sect>Babel
<sect1>Introduction
<p>The Babel protocol (RFC6126) is a loop-avoiding distance-vector routing
protocol that is robust and efficient both in ordinary wired networks and in
wireless mesh networks. Babel is conceptually very simple in its operation and
"just works" in its default configuration, though some configuration is possible
and in some cases desirable.
<p>While the Babel protocol is dual stack (i.e., can carry both IPv4 and IPv6
routes over the same IPv6 transport), BIRD presently implements only the IPv6
subset of the protocol. No Babel extensions are implemented, but the BIRD
implementation can coexist with implementations using the extensions (and will
just ignore extension messages).
<p>The Babel protocol implementation in BIRD is currently in alpha stage.
<sect1>Configuration
<p>Babel supports no global configuration options apart from those common to all
other protocols, but supports the following per-interface configuration options:
<code>
protocol babel [<name>] {
interface <interface pattern> {
type <wired|wireless>;
rxcost <number>;
hello interval <number>;
update interval <number>;
port <number>;
tx class|dscp <number>;
tx priority <number>;
rx buffer <number>;
tx length <number>;
check link <switch>;
};
}
</code>
<descrip>
<tag>type wired|wireless </tag>
This option specifies the interface type: Wired or wireless. Wired
interfaces are considered more reliable, and so the default hello
interval is higher, and a neighbour is considered unreachable after only
a small number of "hello" packets are lost. On wireless interfaces,
hello packets are sent more often, and the ETX link quality estimation
technique is used to compute the metrics of routes discovered over this
interface. This technique will gradually degrade the metric of routes
when packets are lost rather than the more binary up/down mechanism of
wired type links. Default: <cf/wired/.
<tag>rxcost <m/num/</tag>
This specifies the RX cost of the interface. The route metrics will be
computed from this value with a mechanism determined by the interface
<cf/type/. Default: 96 for wired interfaces, 256 for wireless.
<tag>hello interval <m/num/</tag>
Interval at which periodic "hello" messages are sent on this interface,
in seconds. Default: 4 seconds.
<tag>update interval <m/num/</tag>
Interval at which periodic (full) updates are sent. Default: 4 times the
hello interval.
<tag>port <m/number/</tag>
This option selects an UDP port to operate on. The default is to operate
on port 6696 as specified in the Babel RFC.
<tag>tx class|dscp|priority <m/number/</tag>
These options specify the ToS/DiffServ/Traffic class/Priority of the
outgoing Babel packets. See <ref id="dsc-prio" name="tx class"> common
option for detailed description.
<tag>rx buffer <m/number/</tag>
This option specifies the size of buffers used for packet processing.
The buffer size should be bigger than maximal size of received packets.
The default value is the interface MTU, and the value will be clamped to a
minimum of 512 bytes + IP packet overhead.
<tag>tx length <m/number/</tag>
This option specifies the maximum length of generated Babel packets. To
avoid IP fragmentation, it should not exceed the interface MTU value.
The default value is the interface MTU value, and the value will be
clamped to a minimum of 512 bytes + IP packet overhead.
<tag>check link <m/switch/</tag>
If set, the hardware link state (as reported by OS) is taken into
consideration. When the link disappears (e.g. an ethernet cable is
unplugged), neighbors are immediately considered unreachable and all
routes received from them are withdrawn. It is possible that some
hardware drivers or platforms do not implement this feature. Default:
yes.
</descrip>
<sect><label id="sect-bfd">BFD
<sect1>Introduction
@ -1571,7 +1682,7 @@ RFC 4271<htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc4271.txt">
It also supports the community attributes
(RFC 1997<htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc1997.txt">),
capability negotiation
(RFC 3392<htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc3392.txt">),
(RFC 5492<htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc5492.txt">),
MD5 password authentication
(RFC 2385<htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc2385.txt">),
extended communities
@ -1707,7 +1818,11 @@ using the following configuration parameters:
<cf/bgp_next_hop/ is used if it is directly reachable, otherwise the
neighbor IP address is used. Recursive mode means that the gateway is
computed by an IGP routing table lookup for the IP address from
<cf/bgp_next_hop/. Recursive mode is the behavior specified by the BGP
<cf/bgp_next_hop/. Note that there is just one level of indirection in
recursive mode - the route obtained by the lookup must not be recursive
itself, to prevent mutually recursive routes.
Recursive mode is the behavior specified by the BGP
standard. Direct mode is simpler, does not require any routes in a
routing table, and was used in older versions of BIRD, but does not
handle well nontrivial iBGP setups and multihop. Recursive mode is
@ -1745,9 +1860,20 @@ using the following configuration parameters:
only. Default: disabled.
<tag>password <m/string/</tag>
Use this password for MD5 authentication of BGP sessions. Default: no
authentication. Password has to be set by external utility
(e.g. setkey(8)) on BSD systems.
Use this password for MD5 authentication of BGP sessions (RFC 2385).
When used on BSD systems, see also <cf/setkey/ option below. Default:
no authentication.
<tag>setkey <m/switch/</tag>
On BSD systems, keys for TCP MD5 authentication are stored in the global
SA/SP database, which can be accessed by external utilities (e.g.
setkey(8)). BIRD configures security associations in the SA/SP database
automatically based on <cf/password/ options (see above), this option
allows to disable automatic updates by BIRD when manual configuration by
external utilities is preferred. Note that automatic SA/SP database
updates are currently implemented only for FreeBSD. Passwords have to be
set manually by an external utility on NetBSD and OpenBSD. Default:
enabled (ignored on non-FreeBSD).
<tag>passive <m/switch/</tag>
Standard BGP behavior is both initiating outgoing connections and
@ -1855,6 +1981,11 @@ using the following configuration parameters:
in neighbor's implementation of 4B AS extension. Even when disabled
(off), BIRD behaves internally as AS4-aware BGP router. Default: on.
<tag>enable extended messages <m/switch/</tag>
The BGP protocol uses maximum message length of 4096 bytes. This option
provides an extension to allow extended messages with length up
to 65535 bytes. Default: off.
<tag>capabilities <m/switch/</tag>
Use capability advertisement to advertise optional capabilities. This is
standard behavior for newer BGP implementations, but there might be some
@ -2050,7 +2181,7 @@ protocol bgp {
multihop; # ... which is connected indirectly
export filter { # We use non-trivial export rules
if source = RTS_STATIC then { # Export only static routes
# Assign our community
# Assign our community
bgp_community.add((65000,64501));
# Artificially increase path length
# by advertising local AS number twice
@ -2081,7 +2212,6 @@ interfaces to be defined for them to work with.
<p><descrip>
<tag>scan time <m/number/</tag>
Time in seconds between two scans of the network interface list. On
systems where we are notified about interface status changes
asynchronously (such as newer versions of Linux), we need to scan the
@ -2138,7 +2268,7 @@ conditions, because a lower priority IGP route for the same network is not
exported to the kernel routing table. This is an issue on BSD systems only, as
on Linux systems BIRD cannot change non-BIRD route in the kernel routing table.
<p>The only configurable thing about direct is what interfaces it watches:
<p>There are just few configuration options for the Direct protocol:
<p><descrip>
<tag>interface <m/pattern [, ...]/</tag>
@ -2149,6 +2279,12 @@ on Linux systems BIRD cannot change non-BIRD route in the kernel routing table.
interfaces), just use this clause. See <ref id="dsc-iface" name="interface">
common option for detailed description. The Direct protocol uses
extended interface clauses.
<tag>check link <m/switch/</tag>
If enabled, a hardware link state (reported by OS) is taken into
consideration. Routes for directly connected networks are generated only
if link up is reported and they are withdrawn when link disappears
(e.g., an ethernet cable is unplugged). Default value is no.
</descrip>
<p>Direct device routes don't contain any specific attributes.
@ -2227,6 +2363,18 @@ limitations can be overcome using another routing table and the pipe protocol.
a graceful restart recovery is active, the Kernel protocol will defer
synchronization of routing tables until the end of the recovery. Note
that import of kernel routes to BIRD is not affected.
<tag>merge paths <M>switch</M> [limit <M>number</M>]</tag>
Usually, only best routes are exported to the kernel protocol. With path
merging enabled, both best routes and equivalent non-best routes are
merged during export to generate one ECMP (equal-cost multipath) route
for each network. This is useful e.g. for BGP multipath. Note that best
routes are still pivotal for route export (responsible for most
properties of resulting ECMP routes), while exported non-best routes are
responsible just for additional multipath next hops. This option also
allows to specify a limit on maximal number of nexthops in one route. By
default, multipath merging is disabled. If enabled, default value of the
limit is 16.
</descrip>
<sect1>Attributes
@ -2248,12 +2396,26 @@ these attributes:
<tag>ip <cf/krt_prefsrc/</tag> (Linux)
The preferred source address. Used in source address selection for
outgoing packets. Have to be one of IP addresses of the router.
outgoing packets. Has to be one of the IP addresses of the router.
<tag>int <cf/krt_realm/</tag> (Linux)
The realm of the route. Can be used for traffic classification.
</descrip>
<p>In Linux, there is also a plenty of obscure route attributes mostly focused
on tuning TCP performance of local connections. BIRD supports most of these
attributes, see Linux or iproute2 documentation for their meaning. Attributes
<cf/krt_lock_*/ and <cf/krt_feature_*/ have type bool, others have type int.
Supported attributes are:
<cf/krt_mtu/, <cf/krt_lock_mtu/, <cf/krt_window/, <cf/krt_lock_window/,
<cf/krt_rtt/, <cf/krt_lock_rtt/, <cf/krt_rttvar/, <cf/krt_lock_rttvar/,
<cf/krt_sstresh/, <cf/krt_lock_sstresh/, <cf/krt_cwnd/, <cf/krt_lock_cwnd/,
<cf/krt_advmss/, <cf/krt_lock_advmss/, <cf/krt_reordering/, <cf/krt_lock_reordering/,
<cf/krt_hoplimit/, <cf/krt_lock_hoplimit/, <cf/krt_rto_min/, <cf/krt_lock_rto_min/,
<cf/krt_initcwnd/, <cf/krt_initrwnd/, <cf/krt_quickack/,
<cf/krt_feature_ecn/, <cf/krt_feature_allfrag/
<sect1>Example
<p>A simple configuration can look this way:
@ -2447,7 +2609,7 @@ protocol ospf &lt;name&gt; {
This option specifies whether OSPF is allowed to generate ECMP
(equal-cost multipath) routes. Such routes are used when there are
several directions to the destination, each with the same (computed)
cost. This option also allows to specify a limit on maximal number of
cost. This option also allows to specify a limit on maximum number of
nexthops in one route. By default, ECMP is disabled. If enabled,
default value of the limit is 16.
@ -2579,8 +2741,8 @@ protocol ospf &lt;name&gt; {
updates. Default value is 5.
<tag>priority <M>num</M></tag>
On every multiple access network (e.g., the Ethernet) Designed Router
and Backup Designed router are elected. These routers have some special
On every multiple access network (e.g., the Ethernet) Designated Router
and Backup Designated router are elected. These routers have some special
functions in the flooding process. Higher priority increases preferences
in this election. Routers with priority 0 are not eligible. Default
value is 1.
@ -3215,16 +3377,14 @@ one). After some time, the distance reaches infinity (that's 15 in RIP) and all
routers know that network is unreachable. RIP tries to minimize situations where
counting to infinity is necessary, because it is slow. Due to infinity being 16,
you can't use RIP on networks where maximal distance is higher than 15
hosts. You can read more about RIP at
<HTMLURL URL="http://www.ietf.org/html.charters/rip-charter.html"
name="http://www.ietf.org/html.charters/rip-charter.html">. Both IPv4
(RFC 1723 <htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc1723.txt">) and IPv6
(RFC 2080 <htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc2080.txt">) versions
of RIP are supported by BIRD, historical RIPv1
(RFC 1058 <htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc1058.txt">) is not
currently supported. RIPv4 MD5 authentication
(RFC 2082 <htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc2082.txt">) is
supported.
hosts.
<p>BIRD supports RIPv1
(RFC 1058<htmlurl url="http://www.rfc-editor.org/rfc/rfc1058.txt">),
RIPv2 (RFC 2453<htmlurl url="http://www.rfc-editor.org/rfc/rfc2453.txt">),
RIPng (RFC 2080<htmlurl url="http://www.rfc-editor.org/rfc/rfc2080.txt">),
and RIP cryptographic authentication (SHA-1 not implemented)
(RFC 4822<htmlurl url="http://www.rfc-editor.org/rfc/rfc4822.txt">).
<p>RIP is a very simple protocol, and it has a lot of shortcomings. Slow
convergence, big network load and inability to handle larger networks makes it
@ -3232,39 +3392,161 @@ pretty much obsolete. It is still usable on very small networks.
<sect1>Configuration
<p>In addition to options common for all to other protocols, RIP supports the
following ones:
<p>RIP configuration consists mainly of common protocol options and interface
definitions, most RIP options are interface specific.
<code>
protocol rip [&lt;name&gt;] {
infinity &lt;number&gt;;
ecmp &lt;switch&gt; [limit &lt;number&gt;];
interface &lt;interface pattern&gt; {
metric &lt;number&gt;;
mode multicast|broadcast;
passive &lt;switch&gt;;
address &lt;ip&gt;;
port &lt;number&gt;;
version 1|2;
split horizon &lt;switch&gt;;
poison reverse &lt;switch&gt;;
check zero &lt;switch&gt;;
update time &lt;number&gt;;
timeout time &lt;number&gt;;
garbage time &lt;number&gt;;
ecmp weight &lt;number&gt;;
ttl security &lt;switch&gt;; | tx only;
tx class|dscp &lt;number&gt;;
tx priority &lt;number&gt;;
rx buffer &lt;number&gt;;
tx length &lt;number&gt;;
check link &lt;switch&gt;;
authentication none|plaintext|cryptographic;
password "&lt;text&gt;";
password "&lt;text&gt;" {
id &lt;num&gt;;
generate from "&lt;date&gt;";
generate to "&lt;date&gt;";
accept from "&lt;date&gt;";
accept to "&lt;date&gt;";
};
};
}
</code>
<descrip>
<tag>authentication none|plaintext|md5</tag>
Selects authentication method to be used. <cf/none/ means that packets
are not authenticated at all, <cf/plaintext/ means that a plaintext
password is embedded into each packet, and <cf/md5/ means that packets
are authenticated using a MD5 cryptographic hash. If you set
authentication to not-none, it is a good idea to add <cf>password</cf>
section. Default: none.
<tag>infinity <M>number</M></tag>
Selects the distance of infinity. Bigger values will make
protocol convergence even slower. The default value is 16.
<tag>honor always|neighbor|never</tag>
Specifies when should requests for dumping routing table be honored.
(Always, when sent from a host on a directly connected network or
never.) Routing table updates are honored only from neighbors, that is
not configurable. Default: never.
<tag>ecmp <M>switch</M> [limit <M>number</M>]</tag>
This option specifies whether RIP is allowed to generate ECMP
(equal-cost multipath) routes. Such routes are used when there are
several directions to the destination, each with the same (computed)
cost. This option also allows to specify a limit on maximum number of
nexthops in one route. By default, ECMP is disabled. If enabled,
default value of the limit is 16.
<tag>interface <m/pattern [, ...]/ { <m/options/ }</tag>
Interface definitions specify a set of interfaces on which the
protocol is activated and contain interface specific options.
See <ref id="dsc-iface" name="interface"> common options for
detailed description.
</descrip>
<p>There are some options that can be specified per-interface:
<p>Interface specific options:
<descrip>
<tag>metric <m/num/</tag>
This option specifies the metric of the interface. Valid
This option specifies the metric of the interface. When a route is
received from the interface, its metric is increased by this value
before further processing. Valid values are 1-255, but values higher
than infinity has no further meaning. Default: 1.
<tag>mode multicast|broadcast|quiet|nolisten|version1</tag>
This option selects the mode for RIP to use on the interface. If nothing
is specified, RIP runs in multicast mode. <cf/version1/ is currently
equivalent to <cf/broadcast/, and it makes RIP talk to a broadcast
address even through multicast mode is possible. <cf/quiet/ option means
that RIP will not transmit any periodic messages to this interface and
<cf/nolisten/ means that RIP will send to this interface butnot listen
to it.
<tag>mode multicast|broadcast</tag>
This option selects the mode for RIP to use on the interface. The
default is multicast mode for RIPv2 and broadcast mode for RIPv1.
RIPng always uses the multicast mode.
<tag>passive <m/switch/</tag>
Passive interfaces receive routing updates but do not transmit any
messages. Default: no.
<tag>address <m/ip/</tag>
This option specifies a destination address used for multicast or
broadcast messages, the default is the official RIP (224.0.0.9) or RIPng
(ff02::9) multicast address, or an appropriate broadcast address in the
broadcast mode.
<tag>port <m/number/</tag>
This option selects an UDP port to operate on, the default is the
official RIP (520) or RIPng (521) port.
<tag>version 1|2</tag>
This option selects the version of RIP used on the interface. For RIPv1,
automatic subnet aggregation is not implemented, only classful network
routes and host routes are propagated. Note that BIRD allows RIPv1 to be
configured with features that are defined for RIPv2 only, like
authentication or using multicast sockets. The default is RIPv2 for IPv4
RIP, the option is not supported for RIPng, as no further versions are
defined.
<tag>version only <m/switch/</tag>
Regardless of RIP version configured for the interface, BIRD accepts
incoming packets of any RIP version. This option restrict accepted
packets to the configured version. Default: no.
<tag>split horizon <m/switch/</tag>
Split horizon is a scheme for preventing routing loops. When split
horizon is active, routes are not regularly propagated back to the
interface from which they were received. They are either not propagated
back at all (plain split horizon) or propagated back with an infinity
metric (split horizon with poisoned reverse). Therefore, other routers
on the interface will not consider the router as a part of an
independent path to the destination of the route. Default: yes.
<tag>poison reverse <m/switch/</tag>
When split horizon is active, this option specifies whether the poisoned
reverse variant (propagating routes back with an infinity metric) is
used. The poisoned reverse has some advantages in faster convergence,
but uses more network traffic. Default: yes.
<tag>check zero <m/switch/</tag>
Received RIPv1 packets with non-zero values in reserved fields should
be discarded. This option specifies whether the check is performed or
such packets are just processed as usual. Default: yes.
<tag>update time <m/number/</tag>
Specifies the number of seconds between periodic updates. A lower number
will mean faster convergence but bigger network load. Default: 30.
<tag>timeout time <m/number/</tag>
Specifies the time interval (in seconds) between the last received route
announcement and the route expiration. After that, the network is
considered unreachable, but still is propagated with infinity distance.
Default: 180.
<tag>garbage time <m/number/</tag>
Specifies the time interval (in seconds) between the route expiration
and the removal of the unreachable network entry. The garbage interval,
when a route with infinity metric is propagated, is used for both
internal (after expiration) and external (after withdrawal) routes.
Default: 120.
<tag>ecmp weight <m/number/</tag>
When ECMP (multipath) routes are allowed, this value specifies a
relative weight used for nexthops going through the iface. Valid
values are 1-256. Default value is 1.
<tag>authentication none|plaintext|cryptographic</tag>
Selects authentication method to be used. <cf/none/ means that packets
are not authenticated at all, <cf/plaintext/ means that a plaintext
password is embedded into each packet, and <cf/cryptographic/ means that
packets are authenticated using a MD5 cryptographic hash. If you set
authentication to not-none, it is a good idea to add <cf>password</cf>
section. Default: none.
<tag>password "<m/text/"</tag>
Specifies a password used for authentication. See <ref id="dsc-pass"
name="password"> common option for detailed description.
<tag>ttl security [<m/switch/ | tx only]</tag>
TTL security is a feature that protects routing protocols from remote
@ -3280,43 +3562,31 @@ following ones:
compatibility with neighbors regardless of whether they use ttl
security.
Note that for RIPng, TTL security is a standard behavior (required by
RFC 2080), but BIRD uses <cf/tx only/ by default, for compatibility with
older versions. For IPv4 RIP, default value is no.
For RIPng, TTL security is a standard behavior (required by RFC 2080)
and therefore default value is yes. For IPv4 RIP, default value is no.
<tag>tx class|dscp|priority <m/num/</tag>
<tag>tx class|dscp|priority <m/number/</tag>
These options specify the ToS/DiffServ/Traffic class/Priority of the
outgoing RIP packets. See <ref id="dsc-prio" name="tx class"> common
option for detailed description.
</descrip>
<p>The following options generally override behavior specified in RFC. If you
use any of these options, BIRD will no longer be RFC-compliant, which means it
will not be able to talk to anything other than equally configured BIRD. I have
warned you.
<tag>rx buffer <m/number/</tag>
This option specifies the size of buffers used for packet processing.
The buffer size should be bigger than maximal size of received packets.
The default value is 532 for IPv4 RIP and interface MTU value for RIPng.
<descrip>
<tag>port <M>number</M></tag>
Selects IP port to operate on, default 520. (This is useful when testing
BIRD, if you set this to an address &gt;1024, you will not need to run
bird with UID==0).
<tag>tx length <m/number/</tag>
This option specifies the maximum length of generated RIP packets. To
avoid IP fragmentation, it should not exceed the interface MTU value.
The default value is 532 for IPv4 RIP and interface MTU value for RIPng.
<tag>infinity <M>number</M></tag>
Selects the value of infinity, default is 16. Bigger values will make
protocol convergence even slower.
<tag>period <M>number</M></tag>
Specifies the number of seconds between periodic updates. Default is 30
seconds. A lower number will mean faster convergence but bigger network
load. Do not use values lower than 12.
<tag>timeout time <M>number</M></tag>
Specifies how old route has to be to be considered unreachable.
Default is 4*<cf/period/.
<tag>garbage time <M>number</M></tag>
Specifies how old route has to be to be discarded. Default is
10*<cf/period/.
<tag>check link <m/switch/</tag>
If set, the hardware link state (as reported by OS) is taken into
consideration. When the link disappears (e.g. an ethernet cable is
unplugged), neighbors are immediately considered unreachable and all
routes received from them are withdrawn. It is possible that some
hardware drivers or platforms do not implement this feature. Default:
no.
</descrip>
<sect1>Attributes
@ -3327,27 +3597,26 @@ warned you.
<tag>int <cf/rip_metric/</tag>
RIP metric of the route (ranging from 0 to <cf/infinity/). When routes
from different RIP instances are available and all of them have the same
preference, BIRD prefers the route with lowest <cf/rip_metric/. When
importing a non-RIP route, the metric defaults to 5.
preference, BIRD prefers the route with lowest <cf/rip_metric/. When a
non-RIP route is exported to RIP, the default metric is 1.
<tag>int <cf/rip_tag/</tag>
RIP route tag: a 16-bit number which can be used to carry additional
information with the route (for example, an originating AS number in
case of external routes). When importing a non-RIP route, the tag
defaults to 0.
case of external routes). When a non-RIP route is exported to RIP, the
default tag is 0.
</descrip>
<sect1>Example
<p><code>
protocol rip MyRIP_test {
protocol rip {
debug all;
port 1520;
period 12;
garbage time 60;
interface "eth0" { metric 3; mode multicast; };
interface "eth*" { metric 2; mode broadcast; };
honor neighbor;
authentication none;
import filter { print "importing"; accept; };
export filter { print "exporting"; accept; };
@ -3369,7 +3638,7 @@ default route to prevent routing loops).
packets to a neighboring router, multipath routes specifying several (possibly
weighted) neighboring routers, device routes specifying forwarding to hosts on a
directly connected network, recursive routes computing their nexthops by doing
route table lookups for a given IP and special routes (sink, blackhole etc.)
route table lookups for a given IP, and special routes (sink, blackhole etc.)
which specify a special action to be done instead of forwarding the packet.
<p>When the particular destination is not available (the interface is down or
@ -3377,16 +3646,36 @@ the next hop of the route is not a neighbor at the moment), Static just
uninstalls the route from the table it is connected to and adds it again as soon
as the destination becomes adjacent again.
<p>The Static protocol does not have many configuration options. The definition
of the protocol contains mainly a list of static routes:
<p>There are three classes of definitions in Static protocol configuration --
global options, static route definitions, and per-route options. Usually, the
definition of the protocol contains mainly a list of static routes.
<p>Global options:
<descrip>
<tag>check link <m/switch/</tag>
If set, hardware link states of network interfaces are taken into
consideration. When link disappears (e.g. ethernet cable is unplugged),
static routes directing to that interface are removed. It is possible
that some hardware drivers or platforms do not implement this feature.
Default: off.
<tag>igp table <m/name/</tag>
Specifies a table that is used for route table lookups of recursive
routes. Default: the same table as the protocol is connected to.
</descrip>
<p>Route definitions (each may also contain a block of per-route options):
<descrip>
<tag>route <m/prefix/ via <m/ip/</tag>
Static route through a neighboring router.
Static route through a neighboring router. For link-local next hops,
interface can be specified as a part of the address (e.g.,
<cf/via fe80::1234%eth0/).
<tag>route <m/prefix/ multipath via <m/ip/ [weight <m/num/] [via ...]</tag>
<tag>route <m/prefix/ multipath via <m/ip/ [weight <m/num/] [bfd <m/switch/] [via ...]</tag>
Static multipath route. Contains several nexthops (gateways), possibly
with their weights.
with their weights.
<tag>route <m/prefix/ via <m/"interface"/</tag>
Static device route through an interface to hosts on a directly
@ -3400,17 +3689,33 @@ of the protocol contains mainly a list of static routes:
Special routes specifying to silently drop the packet, return it as
unreachable or return it as administratively prohibited. First two
targets are also known as <cf/drop/ and <cf/reject/.
</descrip>
<tag>check link <m/switch/</tag>
If set, hardware link states of network interfaces are taken into
consideration. When link disappears (e.g. ethernet cable is unplugged),
static routes directing to that interface are removed. It is possible
that some hardware drivers or platforms do not implement this feature.
Default: off.
<p>Per-route options:
<tag>igp table <m/name/</tag>
Specifies a table that is used for route table lookups of recursive
routes. Default: the same table as the protocol is connected to.
<descrip>
<tag>bfd <m/switch/</tag>
The Static protocol could use BFD protocol for next hop liveness
detection. If enabled, a BFD session to the route next hop is created
and the static route is BFD-controlled -- the static route is announced
only if the next hop liveness is confirmed by BFD. If the BFD session
fails, the static route is removed. Note that this is a bit different
compared to other protocols, which may use BFD as an advisory mechanism
for fast failure detection but ignores it if a BFD session is not even
established.
This option can be used for static routes with a direct next hop, or
also for for individual next hops in a static multipath route (see
above). Note that BFD protocol also has to be configured, see
<ref id="sect-bfd" name="BFD"> section for details. Default value is no.
<tag><m/filter expression/</tag>
This is a special option that allows filter expressions to be configured
on per-route basis. Can be used multiple times. These expressions are
evaluated when the route is originated, similarly to the import filter
of the static protocol. This is especially useful for configuring route
attributes, e.g., <cf/ospf_metric1 = 100;/ for a route that will be
exported to the OSPF protocol.
</descrip>
<p>Static routes have no specific attributes.
@ -3419,14 +3724,23 @@ of the protocol contains mainly a list of static routes:
<p><code>
protocol static {
table testable; # Connect to a non-default routing table
table testable; # Connect to a non-default routing table
check link; # Advertise routes only if link is up
route 0.0.0.0/0 via 198.51.100.130; # Default route
route 10.0.0.0/8 multipath # Multipath route
route 10.0.0.0/8 multipath # Multipath route
via 198.51.100.10 weight 2
via 198.51.100.20
via 198.51.100.20 bfd # BFD-controlled next hop
via 192.0.2.1;
route 203.0.113.0/24 unreachable; # Sink route
route 10.2.0.0/24 via "arc0"; # Secondary network
route 10.2.0.0/24 via "arc0"; # Secondary network
route 192.168.10.0/24 via 198.51.100.100 {
ospf_metric1 = 20; # Set extended attribute
}
route 192.168.10.0/24 via 198.51.100.100 {
ospf_metric2 = 100; # Set extended attribute
ospf_tag = 2; # Set extended attribute
bfd; # BFD-controlled route
}
}
</code>

View File

@ -55,6 +55,11 @@ Reply codes of BIRD command-line interface
1018 Show memory
1019 Show ROA list
1020 Show BFD sessions
1021 Show RIP interface
1022 Show RIP neighbors
1023 Show Babel interfaces
1024 Show Babel neighbors
1025 Show Babel entries
8000 Reply too long
8001 Route not found

View File

@ -283,7 +283,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
LEN,
DEFINED,
ADD, DELETE, CONTAINS, RESET,
PREPEND, FIRST, LAST, MATCH,
PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
ROA_CHECK,
EMPTY,
FILTER, WHERE, EVAL)
@ -752,6 +752,7 @@ term:
| term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; }
| term '.' FIRST { $$ = f_new_inst(); $$->code = P('a','f'); $$->a1.p = $1; }
| term '.' LAST { $$ = f_new_inst(); $$->code = P('a','l'); $$->a1.p = $1; }
| term '.' LAST_NONAGGREGATED { $$ = f_new_inst(); $$->code = P('a','L'); $$->a1.p = $1; }
/* Communities */
/* This causes one shift/reduce conflict

View File

@ -462,7 +462,7 @@ static int f_flags;
static inline void f_rte_cow(void)
{
*f_rte = rte_cow(*f_rte);
*f_rte = rte_cow(*f_rte);
}
/*
@ -471,26 +471,22 @@ static inline void f_rte_cow(void)
static void
f_rta_cow(void)
{
if ((*f_rte)->attrs->aflags & RTAF_CACHED) {
if (!rta_is_cached((*f_rte)->attrs))
return;
/* Prepare to modify rte */
f_rte_cow();
/* Prepare to modify rte */
f_rte_cow();
/* Store old rta to free it later */
f_old_rta = (*f_rte)->attrs;
/* Store old rta to free it later, it stores reference from rte_cow() */
f_old_rta = (*f_rte)->attrs;
/*
* Alloc new rta, do shallow copy and update rte. Fields eattrs
* and nexthops of rta are shared with f_old_rta (they will be
* copied when the cached rta will be obtained at the end of
* f_run()), also the lock of hostentry is inherited (we suppose
* hostentry is not changed by filters).
*/
rta *ra = lp_alloc(f_pool, sizeof(rta));
memcpy(ra, f_old_rta, sizeof(rta));
ra->aflags = 0;
(*f_rte)->attrs = ra;
}
/*
* Get shallow copy of rta. Fields eattrs and nexthops of rta are shared
* with f_old_rta (they will be copied when the cached rta will be obtained
* at the end of f_run()), also the lock of hostentry is inherited (we
* suppose hostentry is not changed by filters).
*/
(*f_rte)->attrs = rta_do_cow((*f_rte)->attrs, f_pool);
}
static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
@ -516,6 +512,9 @@ static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
#define ACCESS_RTE \
do { if (!f_rte) runtime("No route to access"); } while (0)
#define BITFIELD_MASK(what) \
(1u << (what->a2.i >> 24))
/**
* interpret
* @what: filter to interpret
@ -526,7 +525,7 @@ static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
* Each instruction has 4 fields: code (which is instruction code),
* aux (which is extension to instruction code, typically type),
* arg1 and arg2 - arguments. Depending on instruction, arguments
* are either integers, or pointers to instruction trees. Common
* are either integers, or pointers to instruction trees. Common
* instructions like +, that have two expressions as arguments use
* TWOARGS macro to get both of them evaluated.
*
@ -585,7 +584,7 @@ interpret(struct f_inst *what)
default: runtime( "Usage of unknown type" );
}
break;
case '&':
case '|':
ARG(v1, a1.p);
@ -625,7 +624,7 @@ interpret(struct f_inst *what)
if (v1.type == T_INT) {
ipv4_used = 0; key = v1.val.i;
}
}
else if (v1.type == T_QUAD) {
ipv4_used = 1; key = v1.val.i;
}
@ -724,7 +723,7 @@ interpret(struct f_inst *what)
#endif
runtime( "Assigning to variable of incompatible type" );
}
*vp = v2;
*vp = v2;
break;
/* some constants have value in a2, some in *a1.p, strange. */
@ -864,12 +863,14 @@ interpret(struct f_inst *what)
ACCESS_RTE;
{
eattr *e = NULL;
u16 code = what->a2.i;
if (!(f_flags & FF_FORCE_TMPATTR))
e = ea_find( (*f_rte)->attrs->eattrs, what->a2.i );
if (!e)
e = ea_find( (*f_tmp_attrs), what->a2.i );
e = ea_find((*f_rte)->attrs->eattrs, code);
if (!e)
e = ea_find((*f_tmp_attrs), code);
if ((!e) && (f_flags & FF_FORCE_TMPATTR))
e = ea_find( (*f_rte)->attrs->eattrs, what->a2.i );
e = ea_find((*f_rte)->attrs->eattrs, code);
if (!e) {
/* A special case: undefined int_set looks like empty int_set */
@ -878,8 +879,9 @@ interpret(struct f_inst *what)
res.val.ad = adata_empty(f_pool, 0);
break;
}
/* The same special case for ec_set */
else if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_EC_SET) {
if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_EC_SET) {
res.type = T_ECLIST;
res.val.ad = adata_empty(f_pool, 0);
break;
@ -912,6 +914,10 @@ interpret(struct f_inst *what)
res.type = T_PATH;
res.val.ad = e->u.ptr;
break;
case EAF_TYPE_BITFIELD:
res.type = T_BOOL;
res.val.i = !!(e->u.data & BITFIELD_MASK(what));
break;
case EAF_TYPE_INT_SET:
res.type = T_CLIST;
res.val.ad = e->u.ptr;
@ -933,13 +939,15 @@ interpret(struct f_inst *what)
ONEARG;
{
struct ea_list *l = lp_alloc(f_pool, sizeof(struct ea_list) + sizeof(eattr));
u16 code = what->a2.i;
l->next = NULL;
l->flags = EALF_SORTED;
l->count = 1;
l->attrs[0].id = what->a2.i;
l->attrs[0].id = code;
l->attrs[0].flags = 0;
l->attrs[0].type = what->aux | EAF_ORIGINATED;
switch (what->aux & EAF_TYPE_MASK) {
case EAF_TYPE_INT:
if (v1.type != T_INT)
@ -978,6 +986,26 @@ interpret(struct f_inst *what)
runtime( "Setting path attribute to non-path value" );
l->attrs[0].u.ptr = v1.val.ad;
break;
case EAF_TYPE_BITFIELD:
if (v1.type != T_BOOL)
runtime( "Setting bit in bitfield attribute to non-bool value" );
{
/* First, we have to find the old value */
eattr *e = NULL;
if (!(f_flags & FF_FORCE_TMPATTR))
e = ea_find((*f_rte)->attrs->eattrs, code);
if (!e)
e = ea_find((*f_tmp_attrs), code);
if ((!e) && (f_flags & FF_FORCE_TMPATTR))
e = ea_find((*f_rte)->attrs->eattrs, code);
u32 data = e ? e->u.data : 0;
if (v1.val.i)
l->attrs[0].u.data = data | BITFIELD_MASK(what);
else
l->attrs[0].u.data = data & ~BITFIELD_MASK(what);;
}
break;
case EAF_TYPE_INT_SET:
if (v1.type != T_CLIST)
runtime( "Setting clist attribute to non-clist value" );
@ -1063,6 +1091,14 @@ interpret(struct f_inst *what)
res.type = T_INT;
res.val.i = as;
break;
case P('a','L'): /* Get last ASN from non-aggregated part of AS PATH */
ONEARG;
if (v1.type != T_PATH)
runtime( "AS path expected" );
res.type = T_INT;
res.val.i = as_path_get_last_nonaggregated(v1.val.ad);
break;
case 'r':
ONEARG;
res = v1;
@ -1073,7 +1109,7 @@ interpret(struct f_inst *what)
res = interpret(what->a2.p);
if (res.type == T_RETURN)
return res;
res.type &= ~T_RETURN;
res.type &= ~T_RETURN;
break;
case P('c','v'): /* Clear local variables */
for (sym = what->a1.p; sym != NULL; sym = sym->aux2)
@ -1090,7 +1126,7 @@ interpret(struct f_inst *what)
debug( "No else statement?\n");
break;
}
}
}
/* It is actually possible to have t->data NULL */
res = interpret(t->data);
@ -1184,10 +1220,10 @@ interpret(struct f_inst *what)
runtime("Can't add set");
else if (!arg_set)
res.val.ad = int_set_add(f_pool, v1.val.ad, n);
else
else
res.val.ad = int_set_union(f_pool, v1.val.ad, v2.val.ad);
break;
case 'd':
if (!arg_set)
res.val.ad = int_set_del(f_pool, v1.val.ad, n);
@ -1209,7 +1245,7 @@ interpret(struct f_inst *what)
{
/* Extended community list */
int arg_set = 0;
/* v2.val is either EC or EC-set */
if ((v2.type == T_SET) && eclist_set_type(v2.val.t))
arg_set = 1;
@ -1226,10 +1262,10 @@ interpret(struct f_inst *what)
runtime("Can't add set");
else if (!arg_set)
res.val.ad = ec_set_add(f_pool, v1.val.ad, v2.val.ec);
else
else
res.val.ad = ec_set_union(f_pool, v1.val.ad, v2.val.ad);
break;
case 'd':
if (!arg_set)
res.val.ad = ec_set_del(f_pool, v1.val.ad, v2.val.ec);
@ -1353,7 +1389,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
}
break;
case 'c':
case 'c':
switch (f1->aux) {
case T_PREFIX_SET:
@ -1381,7 +1417,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
return 0;
break;
case 'V':
case 'V':
if (strcmp((char *) f1->a2.p, (char *) f2->a2.p))
return 0;
break;
@ -1399,12 +1435,12 @@ i_same(struct f_inst *f1, struct f_inst *f2)
case 'r': ONEARG; break;
case P('c','p'): ONEARG; break;
case P('c','a'): /* Call rewriting trickery to avoid exponential behaviour */
ONEARG;
ONEARG;
if (!i_same(f1->a2.p, f2->a2.p))
return 0;
return 0;
f2->a2.p = f1->a2.p;
break;
case P('c','v'): break; /* internal instruction */
case P('c','v'): break; /* internal instruction */
case P('S','W'): ONEARG; if (!same_tree(f1->a2.p, f2->a2.p)) return 0; break;
case P('i','M'): TWOARGS; break;
case P('A','p'): TWOARGS; break;
@ -1414,7 +1450,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
case P('R','C'):
TWOARGS;
/* Does not really make sense - ROA check resuls may change anyway */
if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name,
if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name,
((struct f_inst_roa_check *) f2)->rtc->name))
return 0;
break;
@ -1499,6 +1535,30 @@ f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struc
return res.val.i;
}
/* TODO: perhaps we could integrate f_eval(), f_eval_rte() and f_run() */
struct f_val
f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool)
{
struct ea_list *tmp_attrs = NULL;
f_rte = rte;
f_old_rta = NULL;
f_tmp_attrs = &tmp_attrs;
f_pool = tmp_pool;
f_flags = 0;
LOG_BUFFER_INIT(f_buf);
/* Note that in this function we assume that rte->attrs is private / uncached */
struct f_val res = interpret(expr);
/* Hack to include EAF_TEMP attributes to the main list */
(*rte)->attrs->eattrs = ea_append(tmp_attrs, (*rte)->attrs->eattrs);
return res;
}
struct f_val
f_eval(struct f_inst *expr, struct linpool *tmp_pool)
{

View File

@ -107,6 +107,7 @@ struct ea_list;
struct rte;
int f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struct linpool *tmp_pool, int flags);
struct f_val f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool);
struct f_val f_eval(struct f_inst *expr, struct linpool *tmp_pool);
uint f_eval_int(struct f_inst *expr);
u32 f_eval_asn(struct f_inst *expr);

View File

@ -1,3 +1,9 @@
sha256.c
sha256.h
sha512.c
sha512.h
sha1.c
sha1.h
birdlib.h
bitops.c
bitops.h

View File

@ -30,7 +30,9 @@
#define MAX(a,b) MAX_(a,b)
#endif
#define U64(c) UINT64_C(c)
#define ABS(a) ((a)>=0 ? (a) : -(a))
#define DELTA(a,b) (((a)>=(b))?(a)-(b):(b)-(a))
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
@ -58,6 +60,7 @@
#define NORET __attribute__((noreturn))
#define UNUSED __attribute__((unused))
#define PACKED __attribute__((packed))
/* Microsecond time */

View File

@ -17,7 +17,7 @@
* representation consists of @n ones followed by zeroes.
*/
u32
u32_mkmask(unsigned n)
u32_mkmask(uint n)
{
return n ? ~((1 << (32 - n)) - 1) : 0;
}

View File

@ -18,12 +18,13 @@
* u32_masklen Inverse operation to u32_mkmask, -1 if not a bitmask.
*/
u32 u32_mkmask(unsigned n);
u32 u32_mkmask(uint n);
int u32_masklen(u32 x);
u32 u32_log2(u32 v);
static inline u32 u32_hash(u32 v) { return v * 2902958171u; }
#endif
static inline u8 u32_popcount(u32 v) { return __builtin_popcount(v); }
#endif

View File

@ -28,7 +28,7 @@ add32(u32 sum, u32 x)
}
static u16
ipsum_calc_block(u32 *buf, unsigned len, u16 isum)
ipsum_calc_block(u32 *buf, uint len, u16 isum)
{
/*
* A few simple facts about the IP checksum (see RFC 1071 for detailed
@ -57,7 +57,7 @@ ipsum_calc_block(u32 *buf, unsigned len, u16 isum)
}
static u16
ipsum_calc(void *frag, unsigned len, va_list args)
ipsum_calc(void *frag, uint len, va_list args)
{
u16 sum = 0;
@ -67,7 +67,7 @@ ipsum_calc(void *frag, unsigned len, va_list args)
frag = va_arg(args, void *);
if (!frag)
break;
len = va_arg(args, unsigned);
len = va_arg(args, uint);
}
return sum;
}
@ -87,7 +87,7 @@ ipsum_calc(void *frag, unsigned len, va_list args)
* Result: 1 if the checksum is correct, 0 else.
*/
int
ipsum_verify(void *frag, unsigned len, ...)
ipsum_verify(void *frag, uint len, ...)
{
va_list args;
u16 sum;
@ -110,7 +110,7 @@ ipsum_verify(void *frag, unsigned len, ...)
* up checksum calculation as much as possible.
*/
u16
ipsum_calculate(void *frag, unsigned len, ...)
ipsum_calculate(void *frag, uint len, ...)
{
va_list args;
u16 sum;

View File

@ -14,7 +14,7 @@
* fragments finished by NULL pointer.
*/
int ipsum_verify(void *frag, unsigned len, ...);
u16 ipsum_calculate(void *frag, unsigned len, ...);
int ipsum_verify(void *frag, uint len, ...);
u16 ipsum_calculate(void *frag, uint len, ...);
#endif

196
lib/fletcher16.h Normal file
View File

@ -0,0 +1,196 @@
/*
* BIRD Library -- Fletcher-16 checksum
*
* (c) 2015 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
/**
* DOC: Fletcher-16 checksum
*
* Fletcher-16 checksum is a position-dependent checksum algorithm used for
* error-detection e.g. in OSPF LSAs.
*
* To generate Fletcher-16 checksum, zero the checksum field in data, initialize
* the context by fletcher16_init(), process the data by fletcher16_update(),
* compute the checksum value by fletcher16_final() and store it to the checksum
* field in data by put_u16() (or other means involving htons() conversion).
*
* To verify Fletcher-16 checksum, initialize the context by fletcher16_init(),
* process the data by fletcher16_update(), compute a passing checksum by
* fletcher16_compute() and check if it is zero.
*/
#ifndef _BIRD_FLETCHER16_H_
#define _BIRD_FLETCHER16_H_
#include "nest/bird.h"
struct fletcher16_context
{
int c0, c1;
};
/**
* fletcher16_init - initialize Fletcher-16 context
* @ctx: the context
*/
static inline void
fletcher16_init(struct fletcher16_context *ctx)
{
ctx->c0 = ctx->c1 = 0;
}
/**
* fletcher16_update - process data to Fletcher-16 context
* @ctx: the context
* @buf: data buffer
* @len: data length
*
* fletcher16_update() reads data from the buffer @buf and updates passing sums
* in the context @ctx. It may be used multiple times for multiple blocks of
* checksummed data.
*/
static inline void
fletcher16_update(struct fletcher16_context *ctx, const u8* buf, int len)
{
/*
* The Fletcher-16 sum is essentially a sequence of
* ctx->c1 += ctx->c0 += *buf++, modulo 255.
*
* In the inner loop, we eliminate modulo operation and we do some loop
* unrolling. MODX is the maximal number of steps that can be done without
* modulo before overflow, see RFC 1008 for details. We use a bit smaller
* value to cover for initial steps due to loop unrolling.
*/
#define MODX 4096
int blen, i;
blen = len % 4;
len -= blen;
for (i = 0; i < blen; i++)
ctx->c1 += ctx->c0 += *buf++;
do {
blen = MIN(len, MODX);
len -= blen;
for (i = 0; i < blen; i += 4)
{
ctx->c1 += ctx->c0 += *buf++;
ctx->c1 += ctx->c0 += *buf++;
ctx->c1 += ctx->c0 += *buf++;
ctx->c1 += ctx->c0 += *buf++;
}
ctx->c0 %= 255;
ctx->c1 %= 255;
} while (len);
}
/**
* fletcher16_update_n32 - process data to Fletcher-16 context, with endianity adjustment
* @ctx: the context
* @buf: data buffer
* @len: data length
*
* fletcher16_update_n32() works like fletcher16_update(), except it applies
* 32-bit host/network endianity swap to the data before they are processed.
* I.e., it assumes that the data is a sequence of u32 that must be converted by
* ntohl() or htonl() before processing. The @buf need not to be aligned, but
* its length (@len) must be multiple of 4. Note that on big endian systems the
* host endianity is the same as the network endianity, therefore there is no
* endianity swap.
*/
static inline void
fletcher16_update_n32(struct fletcher16_context *ctx, const u8* buf, int len)
{
/* See fletcher16_update() for details */
int blen, i;
do {
blen = MIN(len, MODX);
len -= blen;
for (i = 0; i < blen; i += 4)
{
#ifdef CPU_BIG_ENDIAN
ctx->c1 += ctx->c0 += *buf++;
ctx->c1 += ctx->c0 += *buf++;
ctx->c1 += ctx->c0 += *buf++;
ctx->c1 += ctx->c0 += *buf++;
#else
ctx->c1 += ctx->c0 += buf[3];
ctx->c1 += ctx->c0 += buf[2];
ctx->c1 += ctx->c0 += buf[1];
ctx->c1 += ctx->c0 += buf[0];
buf += 4;
#endif
}
ctx->c0 %= 255;
ctx->c1 %= 255;
} while (len);
}
/**
* fletcher16_final - compute final Fletcher-16 checksum value
* @ctx: the context
* @len: total data length
* @pos: offset in data where the checksum will be stored
*
* fletcher16_final() computes the final checksum value and returns it.
* The caller is responsible for storing it in the appropriate position.
* The checksum value depends on @len and @pos, but only their difference
* (i.e. the offset from the end) is significant.
*
* The checksum value is represented as u16, although it is defined as two
* consecutive bytes. We treat them as one u16 in big endian / network order.
* I.e., the returned value is in the form that would be returned by get_u16()
* from the checksum field in the data buffer, therefore the caller should use
* put_u16() or an explicit host-to-network conversion when storing it to the
* checksum field in the data buffer.
*
* Note that the returned checksum value is always nonzero.
*/
static inline u16
fletcher16_final(struct fletcher16_context *ctx, int len, int pos)
{
int x = ((len - pos - 1) * ctx->c0 - ctx->c1) % 255;
if (x <= 0)
x += 255;
int y = 510 - ctx->c0 - x;
if (y > 255)
y -= 255;
return (x << 8) | y;
}
/**
* fletcher16_compute - compute Fletcher-16 sum for verification
* @ctx: the context
*
* fletcher16_compute() returns a passing Fletcher-16 sum for processed data.
* If the data contains the proper Fletcher-16 checksum value, the returned
* value is zero.
*/
static inline u16
fletcher16_compute(struct fletcher16_context *ctx)
{
return (ctx->c0 << 8) | ctx->c1;
}
#endif

View File

@ -233,7 +233,7 @@ ip6_ntop(ip6_addr a, char *b)
}
int
ip4_pton(char *a, ip4_addr *o)
ip4_pton(const char *a, ip4_addr *o)
{
int i;
unsigned long int l;
@ -258,11 +258,11 @@ ip4_pton(char *a, ip4_addr *o)
}
int
ip6_pton(char *a, ip6_addr *o)
ip6_pton(const char *a, ip6_addr *o)
{
u16 words[8];
int i, j, k, l, hfil;
char *start;
const char *start;
if (a[0] == ':') /* Leading :: */
{
@ -364,7 +364,9 @@ ip4_class_mask(ip4_addr ad)
{
u32 m, a = _I(ad);
if (a < 0x80000000)
if (a == 0x00000000)
m = 0x00000000;
else if (a < 0x80000000)
m = 0xff000000;
else if (a < 0xc0000000)
m = 0xffff0000;

View File

@ -15,14 +15,18 @@
#include "lib/unaligned.h"
#define IP4_ALL_NODES ipa_build4(224, 0, 0, 1)
#define IP4_ALL_ROUTERS ipa_build4(224, 0, 0, 2)
#define IP4_OSPF_ALL_ROUTERS ipa_build4(224, 0, 0, 5)
#define IP4_OSPF_DES_ROUTERS ipa_build4(224, 0, 0, 6)
#define IP4_RIP_ROUTERS ipa_build4(224, 0, 0, 9)
#define IP6_ALL_NODES ipa_build6(0xFF020000, 0, 0, 1)
#define IP6_ALL_ROUTERS ipa_build6(0xFF020000, 0, 0, 2)
#define IP6_OSPF_ALL_ROUTERS ipa_build6(0xFF020000, 0, 0, 5)
#define IP6_OSPF_DES_ROUTERS ipa_build6(0xFF020000, 0, 0, 6)
#define IP6_RIP_ROUTERS ipa_build6(0xFF020000, 0, 0, 9)
#define IP6_BABEL_ROUTERS ipa_build6(0xFF020000, 0, 0, 0x00010006)
#define IP4_NONE _MI4(0)
#define IP6_NONE _MI6(0,0,0,0)
@ -32,6 +36,10 @@
#define IP_PREC_INTERNET_CONTROL 0xc0
#define IP4_HEADER_LENGTH 20
#define IP6_HEADER_LENGTH 40
#define UDP_HEADER_LENGTH 8
#ifdef IPV6
#define MAX_PREFIX_LENGTH 128
@ -446,8 +454,8 @@ static inline char * ip4_ntox(ip4_addr a, char *b)
static inline char * ip6_ntox(ip6_addr a, char *b)
{ return b + bsprintf(b, "%08x.%08x.%08x.%08x", _I0(a), _I1(a), _I2(a), _I3(a)); }
int ip4_pton(char *a, ip4_addr *o);
int ip6_pton(char *a, ip6_addr *o);
int ip4_pton(const char *a, ip4_addr *o);
int ip6_pton(const char *a, ip6_addr *o);
// XXXX these functions must be redesigned or removed
#ifdef IPV6
@ -471,11 +479,11 @@ int ip6_pton(char *a, ip6_addr *o);
#define ipa_in_net(x,n,p) (ipa_zero(ipa_and(ipa_xor((n),(x)),ipa_mkmask(p))))
#define net_in_net(n1,l1,n2,l2) (((l1) >= (l2)) && (ipa_zero(ipa_and(ipa_xor((n1),(n2)),ipa_mkmask(l2)))))
char *ip_scope_text(unsigned);
char *ip_scope_text(uint);
struct prefix {
ip_addr addr;
unsigned int len;
uint len;
};

View File

@ -41,7 +41,7 @@ add_tail(list *l, node *n)
{
node *z = l->tail;
n->next = (node *) &l->null;
n->next = &l->tail_node;
n->prev = z;
z->next = n;
l->tail = n;
@ -60,7 +60,7 @@ add_head(list *l, node *n)
node *z = l->head;
n->next = z;
n->prev = (node *) &l->head;
n->prev = &l->head_node;
z->prev = n;
l->head = n;
}
@ -88,7 +88,7 @@ insert_node(node *n, node *after)
* rem_node - remove a node from a list
* @n: node to be removed
*
* Removes a node @n from the list it's linked in.
* Removes a node @n from the list it's linked in. Afterwards, node @n is cleared.
*/
LIST_INLINE void
rem_node(node *n)
@ -96,23 +96,6 @@ rem_node(node *n)
node *z = n->prev;
node *x = n->next;
z->next = x;
x->prev = z;
}
/**
* rem2_node - remove a node from a list, with cleanup
* @n: node to be removed
*
* Removes a node @n from the list it's linked in and resets its pointers to NULL.
* Useful if you want to distinguish between linked and unlinked nodes.
*/
LIST_INLINE void
rem2_node(node *n)
{
node *z = n->prev;
node *x = n->next;
z->next = x;
x->prev = z;
n->next = NULL;
@ -150,9 +133,9 @@ replace_node(node *old, node *new)
LIST_INLINE void
init_list(list *l)
{
l->head = (node *) &l->null;
l->head = &l->tail_node;
l->null = NULL;
l->tail = (node *) &l->head;
l->tail = &l->head_node;
}
/**
@ -172,6 +155,6 @@ add_tail_list(list *to, list *l)
p->next = q;
q->prev = p;
q = l->tail;
q->next = (node *) &to->null;
q->next = &to->tail_node;
to->tail = q;
}

View File

@ -26,10 +26,23 @@ typedef struct node {
struct node *next, *prev;
} node;
typedef struct list { /* In fact two overlayed nodes */
struct node *head, *null, *tail;
typedef union list { /* In fact two overlayed nodes */
struct { /* Head node */
struct node head_node;
void *head_padding;
};
struct { /* Tail node */
void *tail_padding;
struct node tail_node;
};
struct { /* Split to separate pointers */
struct node *head;
struct node *null;
struct node *tail;
};
} list;
#define NODE (node *)
#define HEAD(list) ((void *)((list).head))
#define TAIL(list) ((void *)((list).tail))
@ -61,7 +74,6 @@ typedef struct list { /* In fact two overlayed nodes */
void add_tail(list *, node *);
void add_head(list *, node *);
void rem_node(node *);
void rem2_node(node *);
void add_tail_list(list *, list *);
void init_list(list *);
void insert_node(node *, node *);

418
lib/md5.c
View File

@ -1,154 +1,159 @@
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
* BIRD Library -- MD5 Hash Function and HMAC-MD5 Function
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
* (c) 2015 CZ.NIC z.s.p.o.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
* The code was written by Colin Plumb in 1993, no copyright is claimed.
*
* Adapted for BIRD by Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
/*
* Adapted for BIRD by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
*/
#include "nest/bird.h"
#include "lib/string.h"
#include "md5.h"
#include "lib/md5.h"
#ifdef CPU_LITTLE_ENDIAN
#define byteReverse(buf, len) /* Nothing */
#else
void byteReverse(unsigned char *buf, unsigned longs);
void byteReverse(byte *buf, uint longs);
/*
* Note: this code is harmless on little-endian machines.
*/
void byteReverse(unsigned char *buf, unsigned longs)
void byteReverse(byte *buf, uint longs)
{
u32 t;
do {
t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
((unsigned) buf[1] << 8 | buf[0]);
*(u32 *) buf = t;
buf += 4;
} while (--longs);
u32 t;
do {
t = (u32) ((uint) buf[3] << 8 | buf[2]) << 16 |
((uint) buf[1] << 8 | buf[0]);
*(u32 *) buf = t;
buf += 4;
} while (--longs);
}
#endif
static void md5_transform(u32 buf[4], u32 const in[16]);
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void MD5Init(struct MD5Context *ctx)
void
md5_init(struct md5_context *ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
void
md5_update(struct md5_context *ctx, const byte *buf, uint len)
{
u32 t;
u32 t;
/* Update bitcount */
/* Update bitcount */
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((u32) len << 3)) < t)
ctx->bits[1]++; /* Carry from low to high */
ctx->bits[1] += len >> 29;
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((u32) len << 3)) < t)
ctx->bits[1]++; /* Carry from low to high */
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
/* Handle any leading odd-sized chunks */
/* Handle any leading odd-sized chunks */
if (t)
{
byte *p = (byte *) ctx->in + t;
if (t) {
unsigned char *p = (unsigned char *) ctx->in + t;
t = 64 - t;
if (len < t) {
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (u32 *) ctx->in);
buf += t;
len -= t;
t = 64 - t;
if (len < t)
{
memcpy(p, buf, len);
return;
}
/* Process data in 64-byte chunks */
memcpy(p, buf, t);
byteReverse(ctx->in, 16);
md5_transform(ctx->buf, (u32 *) ctx->in);
buf += t;
len -= t;
}
while (len >= 64) {
memcpy(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (u32 *) ctx->in);
buf += 64;
len -= 64;
}
/* Process data in 64-byte chunks */
while (len >= 64)
{
memcpy(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
md5_transform(ctx->buf, (u32 *) ctx->in);
buf += 64;
len -= 64;
}
/* Handle any remaining bytes of data. */
memcpy(ctx->in, buf, len);
/* Handle any remaining bytes of data. */
memcpy(ctx->in, buf, len);
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
byte *
md5_final(struct md5_context *ctx)
{
unsigned count;
unsigned char *p;
uint count;
byte *p;
/* Compute number of bytes mod 64 */
count = (ctx->bits[0] >> 3) & 0x3F;
/* Compute number of bytes mod 64 */
count = (ctx->bits[0] >> 3) & 0x3F;
/* Set the first char of padding to 0x80. This is safe since there is
/* Set the first char of padding to 0x80. This is safe since there is
always at least one byte free */
p = ctx->in + count;
*p++ = 0x80;
p = ctx->in + count;
*p++ = 0x80;
/* Bytes of padding needed to make 64 bytes */
count = 64 - 1 - count;
/* Bytes of padding needed to make 64 bytes */
count = 64 - 1 - count;
/* Pad out to 56 mod 64 */
if (count < 8) {
/* Two lots of padding: Pad the first block to 64 bytes */
memset(p, 0, count);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (u32 *) ctx->in);
/* Pad out to 56 mod 64 */
if (count < 8)
{
/* Two lots of padding: Pad the first block to 64 bytes */
memset(p, 0, count);
byteReverse(ctx->in, 16);
md5_transform(ctx->buf, (u32 *) ctx->in);
/* Now fill the next block with 56 bytes */
memset(ctx->in, 0, 56);
} else {
/* Pad block to 56 bytes */
memset(p, 0, count - 8);
}
byteReverse(ctx->in, 14);
/* Now fill the next block with 56 bytes */
memset(ctx->in, 0, 56);
}
else
{
/* Pad block to 56 bytes */
memset(p, 0, count - 8);
}
byteReverse(ctx->in, 14);
/* Append length in bits and transform */
((u32 *) ctx->in)[14] = ctx->bits[0];
((u32 *) ctx->in)[15] = ctx->bits[1];
/* Append length in bits and transform */
((u32 *) ctx->in)[14] = ctx->bits[0];
((u32 *) ctx->in)[15] = ctx->bits[1];
MD5Transform(ctx->buf, (u32 *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
memset((char *) ctx, 0, sizeof(ctx)); /* In case it's sensitive */
md5_transform(ctx->buf, (u32 *) ctx->in);
byteReverse((byte *) ctx->buf, 4);
return (byte*) ctx->buf;
}
/* I am a hard paranoid */
void
md5_erase_ctx(struct md5_context *ctx)
{
memset((char *) ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
}
/* The four core functions - F1 is optimized somewhat */
@ -161,92 +166,157 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
void MD5Transform(u32 buf[4], u32 const in[16])
void
md5_transform(u32 buf[4], u32 const in[16])
{
register u32 a, b, c, d;
register u32 a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
/*
* MD5-HMAC
*/
static void
md5_hash_buffer(byte *outbuf, const byte *buffer, size_t length)
{
struct md5_context hd_tmp;
md5_init(&hd_tmp);
md5_update(&hd_tmp, buffer, length);
memcpy(outbuf, md5_final(&hd_tmp), MD5_SIZE);
}
void
md5_hmac_init(struct md5_hmac_context *ctx, const byte *key, size_t keylen)
{
byte keybuf[MD5_BLOCK_SIZE], buf[MD5_BLOCK_SIZE];
/* Hash the key if necessary */
if (keylen <= MD5_BLOCK_SIZE)
{
memcpy(keybuf, key, keylen);
bzero(keybuf + keylen, MD5_BLOCK_SIZE - keylen);
}
else
{
md5_hash_buffer(keybuf, key, keylen);
bzero(keybuf + MD5_SIZE, MD5_BLOCK_SIZE - MD5_SIZE);
}
/* Initialize the inner digest */
md5_init(&ctx->ictx);
int i;
for (i = 0; i < MD5_BLOCK_SIZE; i++)
buf[i] = keybuf[i] ^ 0x36;
md5_update(&ctx->ictx, buf, MD5_BLOCK_SIZE);
/* Initialize the outer digest */
md5_init(&ctx->octx);
for (i = 0; i < MD5_BLOCK_SIZE; i++)
buf[i] = keybuf[i] ^ 0x5c;
md5_update(&ctx->octx, buf, MD5_BLOCK_SIZE);
}
void
md5_hmac_update(struct md5_hmac_context *ctx, const byte *buf, size_t buflen)
{
/* Just update the inner digest */
md5_update(&ctx->ictx, buf, buflen);
}
byte *
md5_hmac_final(struct md5_hmac_context *ctx)
{
/* Finish the inner digest */
byte *isha = md5_final(&ctx->ictx);
/* Finish the outer digest */
md5_update(&ctx->octx, isha, MD5_SIZE);
return md5_final(&ctx->octx);
}

View File

@ -1,16 +1,47 @@
#ifndef MD5_H
#define MD5_H
/*
* BIRD Library -- MD5 Hash Function and HMAC-MD5 Function
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Adapted for BIRD by Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
struct MD5Context {
u32 buf[4];
u32 bits[2];
unsigned char in[64];
#ifndef _BIRD_MD5_H_
#define _BIRD_MD5_H_
#include "nest/bird.h"
#define MD5_SIZE 16
#define MD5_HEX_SIZE 33
#define MD5_BLOCK_SIZE 64
struct md5_context {
u32 buf[4];
u32 bits[2];
byte in[64];
};
void MD5Init(struct MD5Context *context);
void MD5Update(struct MD5Context *context, unsigned char const *buf,
unsigned len);
void MD5Final(unsigned char digest[16], struct MD5Context *context);
void MD5Transform(u32 buf[4], u32 const in[16]);
void md5_init(struct md5_context *ctx);
void md5_update(struct md5_context *ctx, const byte *buf, uint len);
byte *md5_final(struct md5_context *ctx);
#endif /* !MD5_H */
/*
* HMAC-MD5
*/
struct md5_hmac_context {
struct md5_context ictx;
struct md5_context octx;
};
void md5_hmac_init(struct md5_hmac_context *ctx, const byte *key, size_t keylen);
void md5_hmac_update(struct md5_hmac_context *ctx, const byte *buf, size_t buflen);
byte *md5_hmac_final(struct md5_hmac_context *ctx);
#endif /* _BIRD_MD5_H_ */

View File

@ -27,7 +27,7 @@
struct lp_chunk {
struct lp_chunk *next;
unsigned int size;
uint size;
uintptr_t data_align[0];
byte data[0];
};
@ -37,7 +37,7 @@ struct linpool {
byte *ptr, *end;
struct lp_chunk *first, *current, **plast; /* Normal (reusable) chunks */
struct lp_chunk *first_large; /* Large chunks */
unsigned chunk_size, threshold, total, total_large;
uint chunk_size, threshold, total, total_large;
};
static void lp_free(resource *);
@ -64,7 +64,7 @@ static struct resclass lp_class = {
* @blk.
*/
linpool
*lp_new(pool *p, unsigned blk)
*lp_new(pool *p, uint blk)
{
linpool *m = ralloc(p, &lp_class);
m->plast = &m->first;
@ -88,7 +88,7 @@ linpool
* size chunk, an "overflow" chunk is created for it instead.
*/
void *
lp_alloc(linpool *m, unsigned size)
lp_alloc(linpool *m, uint size)
{
byte *a = (byte *) BIRD_ALIGN((unsigned long) m->ptr, CPU_STRUCT_ALIGN);
byte *e = a + size;
@ -146,7 +146,7 @@ lp_alloc(linpool *m, unsigned size)
* how to allocate strings without any space overhead.
*/
void *
lp_allocu(linpool *m, unsigned size)
lp_allocu(linpool *m, uint size)
{
byte *a = m->ptr;
byte *e = a + size;
@ -168,7 +168,7 @@ lp_allocu(linpool *m, unsigned size)
* clears the allocated memory block.
*/
void *
lp_allocz(linpool *m, unsigned size)
lp_allocz(linpool *m, uint size)
{
void *z = lp_alloc(m, size);

View File

@ -16,7 +16,7 @@
#endif
int
MATCH_FUNC_NAME(byte *p, byte *s)
MATCH_FUNC_NAME(const byte *p, const byte *s)
{
while (*p)
{

View File

@ -124,6 +124,7 @@ static char * number(char * str, long num, int base, int size, int precision,
* width is automatically replaced by standard IP address width which
* depends on whether we use IPv4 or IPv6; |%#I| gives hexadecimal format),
* |%R| for Router / Network ID (u32 value printed as IPv4 address)
* |%lR| for 64bit Router / Network ID (u64 value printed as eight :-separated octets)
* and |%m| resp. |%M| for error messages (uses strerror() to translate @errno code to
* message text). On the other hand, it doesn't support floating
* point numbers.
@ -137,9 +138,10 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
unsigned long num;
int i, base;
u32 x;
u64 X;
char *str, *start;
const char *s;
char ipbuf[STD_ADDRESS_P_LENGTH+1];
char ipbuf[MAX(STD_ADDRESS_P_LENGTH,ROUTER_ID_64_LENGTH)+1];
struct iface *iface;
int flags; /* flags to number() */
@ -215,7 +217,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
if (!(flags & LEFT))
while (--field_width > 0)
*str++ = ' ';
*str++ = (unsigned char) va_arg(args, int);
*str++ = (byte) va_arg(args, int);
while (--field_width > 0)
*str++ = ' ';
continue;
@ -309,12 +311,27 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
/* Router/Network ID - essentially IPv4 address in u32 value */
case 'R':
x = va_arg(args, u32);
bsprintf(ipbuf, "%d.%d.%d.%d",
((x >> 24) & 0xff),
((x >> 16) & 0xff),
((x >> 8) & 0xff),
(x & 0xff));
if(qualifier == 'l') {
X = va_arg(args, u64);
bsprintf(ipbuf, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
((X >> 56) & 0xff),
((X >> 48) & 0xff),
((X >> 40) & 0xff),
((X >> 32) & 0xff),
((X >> 24) & 0xff),
((X >> 16) & 0xff),
((X >> 8) & 0xff),
(X & 0xff));
}
else
{
x = va_arg(args, u32);
bsprintf(ipbuf, "%d.%d.%d.%d",
((x >> 24) & 0xff),
((x >> 16) & 0xff),
((x >> 8) & 0xff),
(x & 0xff));
}
s = ipbuf;
goto str;
@ -355,7 +372,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
} else if (flags & SIGN)
num = va_arg(args, int);
else
num = va_arg(args, unsigned int);
num = va_arg(args, uint);
str = number(str, num, base, field_width, precision, flags, size);
if (!str)
return -1;

View File

@ -163,6 +163,7 @@ rfree(void *res)
if (r->n.next)
rem_node(&r->n);
r->class->free(r);
r->class = NULL;
xfree(r);
}
@ -383,16 +384,9 @@ mb_allocz(pool *p, unsigned size)
void *
mb_realloc(void *m, unsigned size)
{
struct mblock *ob = NULL;
struct mblock *b = SKIP_BACK(struct mblock, data, m);
if (m)
{
ob = SKIP_BACK(struct mblock, data, m);
if (ob->r.n.next)
rem_node(&ob->r.n);
}
struct mblock *b = xrealloc(ob, sizeof(struct mblock) + size);
b = xrealloc(b, sizeof(struct mblock) + size);
replace_node(&b->r.n, &b->r.n);
b->size = size;
return b->data;

348
lib/sha1.c Normal file
View File

@ -0,0 +1,348 @@
/*
* BIRD Library -- SHA-1 Hash Function (FIPS 180-1, RFC 3174) and HMAC-SHA-1
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Based on the code from libucw-6.4
* (c) 2008--2009 Martin Mares <mj@ucw.cz>
*
* Based on the code from libgcrypt-1.2.3, which is
* (c) 1998, 2001, 2002, 2003 Free Software Foundation, Inc.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "lib/sha1.h"
#include "lib/unaligned.h"
void
sha1_init(struct sha1_context *ctx)
{
ctx->h0 = 0x67452301;
ctx->h1 = 0xefcdab89;
ctx->h2 = 0x98badcfe;
ctx->h3 = 0x10325476;
ctx->h4 = 0xc3d2e1f0;
ctx->nblocks = 0;
ctx->count = 0;
}
/*
* Transform the message X which consists of 16 32-bit-words
*/
static void
sha1_transform(struct sha1_context *ctx, const byte *data)
{
u32 a,b,c,d,e,tm;
u32 x[16];
/* Get values from the chaining vars. */
a = ctx->h0;
b = ctx->h1;
c = ctx->h2;
d = ctx->h3;
e = ctx->h4;
#ifdef CPU_BIG_ENDIAN
memcpy(x, data, 64);
#else
int i;
for (i = 0; i < 16; i++)
x[i] = get_u32(data+4*i);
#endif
#define K1 0x5A827999L
#define K2 0x6ED9EBA1L
#define K3 0x8F1BBCDCL
#define K4 0xCA62C1D6L
#define F1(x,y,z) ( z ^ ( x & ( y ^ z ) ) )
#define F2(x,y,z) ( x ^ y ^ z )
#define F3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) )
#define F4(x,y,z) ( x ^ y ^ z )
#define M(i) (tm = x[i&0x0f] ^ x[(i-14)&0x0f] ^ x[(i-8)&0x0f] ^ x[(i-3)&0x0f], (x[i&0x0f] = ROL(tm, 1)))
/* Bitwise rotation of an unsigned int to the left **/
#define ROL(x, bits) (((x) << (bits)) | ((uint)(x) >> (sizeof(uint)*8 - (bits))))
#define R(a, b, c, d, e, f, k, m) \
do \
{ \
e += ROL(a, 5) + f(b, c, d) + k + m; \
b = ROL(b, 30); \
} while(0)
R( a, b, c, d, e, F1, K1, x[ 0] );
R( e, a, b, c, d, F1, K1, x[ 1] );
R( d, e, a, b, c, F1, K1, x[ 2] );
R( c, d, e, a, b, F1, K1, x[ 3] );
R( b, c, d, e, a, F1, K1, x[ 4] );
R( a, b, c, d, e, F1, K1, x[ 5] );
R( e, a, b, c, d, F1, K1, x[ 6] );
R( d, e, a, b, c, F1, K1, x[ 7] );
R( c, d, e, a, b, F1, K1, x[ 8] );
R( b, c, d, e, a, F1, K1, x[ 9] );
R( a, b, c, d, e, F1, K1, x[10] );
R( e, a, b, c, d, F1, K1, x[11] );
R( d, e, a, b, c, F1, K1, x[12] );
R( c, d, e, a, b, F1, K1, x[13] );
R( b, c, d, e, a, F1, K1, x[14] );
R( a, b, c, d, e, F1, K1, x[15] );
R( e, a, b, c, d, F1, K1, M(16) );
R( d, e, a, b, c, F1, K1, M(17) );
R( c, d, e, a, b, F1, K1, M(18) );
R( b, c, d, e, a, F1, K1, M(19) );
R( a, b, c, d, e, F2, K2, M(20) );
R( e, a, b, c, d, F2, K2, M(21) );
R( d, e, a, b, c, F2, K2, M(22) );
R( c, d, e, a, b, F2, K2, M(23) );
R( b, c, d, e, a, F2, K2, M(24) );
R( a, b, c, d, e, F2, K2, M(25) );
R( e, a, b, c, d, F2, K2, M(26) );
R( d, e, a, b, c, F2, K2, M(27) );
R( c, d, e, a, b, F2, K2, M(28) );
R( b, c, d, e, a, F2, K2, M(29) );
R( a, b, c, d, e, F2, K2, M(30) );
R( e, a, b, c, d, F2, K2, M(31) );
R( d, e, a, b, c, F2, K2, M(32) );
R( c, d, e, a, b, F2, K2, M(33) );
R( b, c, d, e, a, F2, K2, M(34) );
R( a, b, c, d, e, F2, K2, M(35) );
R( e, a, b, c, d, F2, K2, M(36) );
R( d, e, a, b, c, F2, K2, M(37) );
R( c, d, e, a, b, F2, K2, M(38) );
R( b, c, d, e, a, F2, K2, M(39) );
R( a, b, c, d, e, F3, K3, M(40) );
R( e, a, b, c, d, F3, K3, M(41) );
R( d, e, a, b, c, F3, K3, M(42) );
R( c, d, e, a, b, F3, K3, M(43) );
R( b, c, d, e, a, F3, K3, M(44) );
R( a, b, c, d, e, F3, K3, M(45) );
R( e, a, b, c, d, F3, K3, M(46) );
R( d, e, a, b, c, F3, K3, M(47) );
R( c, d, e, a, b, F3, K3, M(48) );
R( b, c, d, e, a, F3, K3, M(49) );
R( a, b, c, d, e, F3, K3, M(50) );
R( e, a, b, c, d, F3, K3, M(51) );
R( d, e, a, b, c, F3, K3, M(52) );
R( c, d, e, a, b, F3, K3, M(53) );
R( b, c, d, e, a, F3, K3, M(54) );
R( a, b, c, d, e, F3, K3, M(55) );
R( e, a, b, c, d, F3, K3, M(56) );
R( d, e, a, b, c, F3, K3, M(57) );
R( c, d, e, a, b, F3, K3, M(58) );
R( b, c, d, e, a, F3, K3, M(59) );
R( a, b, c, d, e, F4, K4, M(60) );
R( e, a, b, c, d, F4, K4, M(61) );
R( d, e, a, b, c, F4, K4, M(62) );
R( c, d, e, a, b, F4, K4, M(63) );
R( b, c, d, e, a, F4, K4, M(64) );
R( a, b, c, d, e, F4, K4, M(65) );
R( e, a, b, c, d, F4, K4, M(66) );
R( d, e, a, b, c, F4, K4, M(67) );
R( c, d, e, a, b, F4, K4, M(68) );
R( b, c, d, e, a, F4, K4, M(69) );
R( a, b, c, d, e, F4, K4, M(70) );
R( e, a, b, c, d, F4, K4, M(71) );
R( d, e, a, b, c, F4, K4, M(72) );
R( c, d, e, a, b, F4, K4, M(73) );
R( b, c, d, e, a, F4, K4, M(74) );
R( a, b, c, d, e, F4, K4, M(75) );
R( e, a, b, c, d, F4, K4, M(76) );
R( d, e, a, b, c, F4, K4, M(77) );
R( c, d, e, a, b, F4, K4, M(78) );
R( b, c, d, e, a, F4, K4, M(79) );
/* Update chaining vars. */
ctx->h0 += a;
ctx->h1 += b;
ctx->h2 += c;
ctx->h3 += d;
ctx->h4 += e;
}
/*
* Update the message digest with the contents of BUF with length LEN.
*/
void
sha1_update(struct sha1_context *ctx, const byte *buf, uint len)
{
if (ctx->count)
{
/* Fill rest of internal buffer */
for (; len && ctx->count < SHA1_BLOCK_SIZE; len--)
ctx->buf[ctx->count++] = *buf++;
if (ctx->count < SHA1_BLOCK_SIZE)
return;
/* Process data from internal buffer */
sha1_transform(ctx, ctx->buf);
ctx->nblocks++;
ctx->count = 0;
}
if (!len)
return;
/* Process data from input buffer */
while (len >= SHA1_BLOCK_SIZE)
{
sha1_transform(ctx, buf);
ctx->nblocks++;
buf += SHA1_BLOCK_SIZE;
len -= SHA1_BLOCK_SIZE;
}
/* Copy remaining data to internal buffer */
memcpy(ctx->buf, buf, len);
ctx->count = len;
}
/*
* The routine final terminates the computation and returns the digest. The
* handle is prepared for a new cycle, but adding bytes to the handle will the
* destroy the returned buffer.
*
* Returns: 20 bytes representing the digest.
*/
byte *
sha1_final(struct sha1_context *ctx)
{
u32 t, msb, lsb;
sha1_update(ctx, NULL, 0); /* flush */
t = ctx->nblocks;
/* multiply by 64 to make a byte count */
lsb = t << 6;
msb = t >> 26;
/* add the count */
t = lsb;
if ((lsb += ctx->count) < t)
msb++;
/* multiply by 8 to make a bit count */
t = lsb;
lsb <<= 3;
msb <<= 3;
msb |= t >> 29;
if (ctx->count < 56)
{
/* enough room */
ctx->buf[ctx->count++] = 0x80; /* pad */
while (ctx->count < 56)
ctx->buf[ctx->count++] = 0; /* pad */
}
else
{
/* need one extra block */
ctx->buf[ctx->count++] = 0x80; /* pad character */
while (ctx->count < 64)
ctx->buf[ctx->count++] = 0;
sha1_update(ctx, NULL, 0); /* flush */
memset(ctx->buf, 0, 56); /* fill next block with zeroes */
}
/* append the 64 bit count */
ctx->buf[56] = msb >> 24;
ctx->buf[57] = msb >> 16;
ctx->buf[58] = msb >> 8;
ctx->buf[59] = msb;
ctx->buf[60] = lsb >> 24;
ctx->buf[61] = lsb >> 16;
ctx->buf[62] = lsb >> 8;
ctx->buf[63] = lsb;
sha1_transform(ctx, ctx->buf);
byte *p = ctx->buf;
#define X(a) do { put_u32(p, ctx->h##a); p += 4; } while(0)
X(0);
X(1);
X(2);
X(3);
X(4);
#undef X
return ctx->buf;
}
/*
* SHA1-HMAC
*/
/*
* Shortcut function which puts the hash value of the supplied buffer
* into outbuf which must have a size of 20 bytes.
*/
void
sha1_hash_buffer(byte *outbuf, const byte *buffer, uint length)
{
struct sha1_context ctx;
sha1_init(&ctx);
sha1_update(&ctx, buffer, length);
memcpy(outbuf, sha1_final(&ctx), SHA1_SIZE);
}
void
sha1_hmac_init(struct sha1_hmac_context *ctx, const byte *key, uint keylen)
{
byte keybuf[SHA1_BLOCK_SIZE], buf[SHA1_BLOCK_SIZE];
/* Hash the key if necessary */
if (keylen <= SHA1_BLOCK_SIZE)
{
memcpy(keybuf, key, keylen);
memset(keybuf + keylen, 0, SHA1_BLOCK_SIZE - keylen);
}
else
{
sha1_hash_buffer(keybuf, key, keylen);
memset(keybuf + SHA1_SIZE, 0, SHA1_BLOCK_SIZE - SHA1_SIZE);
}
/* Initialize the inner digest */
sha1_init(&ctx->ictx);
int i;
for (i = 0; i < SHA1_BLOCK_SIZE; i++)
buf[i] = keybuf[i] ^ 0x36;
sha1_update(&ctx->ictx, buf, SHA1_BLOCK_SIZE);
/* Initialize the outer digest */
sha1_init(&ctx->octx);
for (i = 0; i < SHA1_BLOCK_SIZE; i++)
buf[i] = keybuf[i] ^ 0x5c;
sha1_update(&ctx->octx, buf, SHA1_BLOCK_SIZE);
}
void
sha1_hmac_update(struct sha1_hmac_context *ctx, const byte *data, uint datalen)
{
/* Just update the inner digest */
sha1_update(&ctx->ictx, data, datalen);
}
byte *
sha1_hmac_final(struct sha1_hmac_context *ctx)
{
/* Finish the inner digest */
byte *isha = sha1_final(&ctx->ictx);
/* Finish the outer digest */
sha1_update(&ctx->octx, isha, SHA1_SIZE);
return sha1_final(&ctx->octx);
}
void
sha1_hmac(byte *outbuf, const byte *key, uint keylen, const byte *data, uint datalen)
{
struct sha1_hmac_context ctx;
sha1_hmac_init(&ctx, key, keylen);
sha1_hmac_update(&ctx, data, datalen);
memcpy(outbuf, sha1_hmac_final(&ctx), SHA1_SIZE);
}

86
lib/sha1.h Normal file
View File

@ -0,0 +1,86 @@
/*
* BIRD Library -- SHA-1 Hash Function (FIPS 180-1, RFC 3174) and HMAC-SHA-1
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Based on the code from libucw-6.4
* (c) 2008--2009 Martin Mares <mj@ucw.cz>
*
* Based on the code from libgcrypt-1.2.3, which is
* (c) 1998, 2001, 2002, 2003 Free Software Foundation, Inc.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_SHA1_H_
#define _BIRD_SHA1_H_
#include "nest/bird.h"
#define SHA1_SIZE 20 /* Size of the SHA1 hash in its binary representation */
#define SHA1_HEX_SIZE 41 /* Buffer length for a string containing SHA1 in hexadecimal format. */
#define SHA1_BLOCK_SIZE 64 /* SHA1 splits input to blocks of this size. */
/*
* Internal SHA1 state.
* You should use it just as an opaque handle only.
*/
struct sha1_context {
u32 h0, h1, h2, h3, h4;
byte buf[SHA1_BLOCK_SIZE];
uint nblocks;
uint count;
};
void sha1_init(struct sha1_context *ctx); /* Initialize new algorithm run in the @ctx context. **/
/*
* Push another @len bytes of data pointed to by @buf onto the SHA1 hash
* currently in @ctx. You can call this any times you want on the same hash (and
* you do not need to reinitialize it by @sha1_init()). It has the same effect
* as concatenating all the data together and passing them at once.
*/
void sha1_update(struct sha1_context *ctx, const byte *buf, uint len);
/*
* No more @sha1_update() calls will be done. This terminates the hash and
* returns a pointer to it.
*
* Note that the pointer points into data in the @ctx context. If it ceases to
* exist, the pointer becomes invalid.
*/
byte *sha1_final(struct sha1_context *ctx);
/*
* A convenience one-shot function for SHA1 hash. It is equivalent to this
* snippet of code:
*
* sha1_context ctx;
* sha1_init(&ctx);
* sha1_update(&ctx, buffer, length);
* memcpy(outbuf, sha1_final(&ctx), SHA1_SIZE);
*/
void sha1_hash_buffer(byte *outbuf, const byte *buffer, uint length);
/*
* SHA1 HMAC message authentication. If you provide @key and @data, the result
* will be stored in @outbuf.
*/
void sha1_hmac(byte *outbuf, const byte *key, uint keylen, const byte *data, uint datalen);
/*
* The HMAC also exists in a stream version in a way analogous to the plain
* SHA1. Pass this as a context.
*/
struct sha1_hmac_context {
struct sha1_context ictx;
struct sha1_context octx;
};
void sha1_hmac_init(struct sha1_hmac_context *ctx, const byte *key, uint keylen); /* Initialize HMAC with context @ctx and the given key. See sha1_init(). */
void sha1_hmac_update(struct sha1_hmac_context *ctx, const byte *data, uint datalen); /* Hash another @datalen bytes of data. See sha1_update(). */
byte *sha1_hmac_final(struct sha1_hmac_context *ctx); /* Terminate the HMAC and return a pointer to the allocated hash. See sha1_final(). */
#endif /* _BIRD_SHA1_H_ */

449
lib/sha256.c Normal file
View File

@ -0,0 +1,449 @@
/*
* BIRD Library -- SHA-256 and SHA-224 Hash Functions,
* HMAC-SHA-256 and HMAC-SHA-224 Functions
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Based on the code from libgcrypt-1.6.0, which is
* (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "lib/sha256.h"
#include "lib/unaligned.h"
// #define SHA256_UNROLLED
void
sha256_init(struct sha256_context *ctx)
{
ctx->h0 = 0x6a09e667;
ctx->h1 = 0xbb67ae85;
ctx->h2 = 0x3c6ef372;
ctx->h3 = 0xa54ff53a;
ctx->h4 = 0x510e527f;
ctx->h5 = 0x9b05688c;
ctx->h6 = 0x1f83d9ab;
ctx->h7 = 0x5be0cd19;
ctx->nblocks = 0;
ctx->count = 0;
}
void
sha224_init(struct sha224_context *ctx)
{
ctx->h0 = 0xc1059ed8;
ctx->h1 = 0x367cd507;
ctx->h2 = 0x3070dd17;
ctx->h3 = 0xf70e5939;
ctx->h4 = 0xffc00b31;
ctx->h5 = 0x68581511;
ctx->h6 = 0x64f98fa7;
ctx->h7 = 0xbefa4fa4;
ctx->nblocks = 0;
ctx->count = 0;
}
/* (4.2) same as SHA-1's F1. */
static inline u32
f1(u32 x, u32 y, u32 z)
{
return (z ^ (x & (y ^ z)));
}
/* (4.3) same as SHA-1's F3 */
static inline u32
f3(u32 x, u32 y, u32 z)
{
return ((x & y) | (z & (x|y)));
}
/* Bitwise rotation of an uint to the right */
static inline u32 ror(u32 x, int n)
{
return ((x >> (n&(32-1))) | (x << ((32-n)&(32-1))));
}
/* (4.4) */
static inline u32
sum0(u32 x)
{
return (ror(x, 2) ^ ror(x, 13) ^ ror(x, 22));
}
/* (4.5) */
static inline u32
sum1(u32 x)
{
return (ror(x, 6) ^ ror(x, 11) ^ ror(x, 25));
}
/*
Transform the message X which consists of 16 32-bit-words. See FIPS
180-2 for details. */
#define S0(x) (ror((x), 7) ^ ror((x), 18) ^ ((x) >> 3)) /* (4.6) */
#define S1(x) (ror((x), 17) ^ ror((x), 19) ^ ((x) >> 10)) /* (4.7) */
#define R(a,b,c,d,e,f,g,h,k,w) \
do \
{ \
t1 = (h) + sum1((e)) + f1((e),(f),(g)) + (k) + (w); \
t2 = sum0((a)) + f3((a),(b),(c)); \
h = g; \
g = f; \
f = e; \
e = d + t1; \
d = c; \
c = b; \
b = a; \
a = t1 + t2; \
} while (0)
/*
The SHA-256 core: Transform the message X which consists of 16
32-bit-words. See FIPS 180-2 for details.
*/
static uint
sha256_transform(struct sha256_context *ctx, const byte *data)
{
static const u32 K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
u32 a,b,c,d,e,f,g,h,t1,t2;
u32 w[64];
int i;
a = ctx->h0;
b = ctx->h1;
c = ctx->h2;
d = ctx->h3;
e = ctx->h4;
f = ctx->h5;
g = ctx->h6;
h = ctx->h7;
for (i = 0; i < 16; i++)
w[i] = get_u32(data + i * 4);
for (; i < 64; i++)
w[i] = S1(w[i-2]) + w[i-7] + S0(w[i-15]) + w[i-16];
for (i = 0; i < 64;)
{
#ifndef SHA256_UNROLLED
R(a,b,c,d,e,f,g,h,K[i],w[i]);
i++;
#else /* Unrolled */
t1 = h + sum1(e) + f1(e, f, g) + K[i] + w[i];
t2 = sum0(a) + f3(a, b, c);
d += t1;
h = t1 + t2;
t1 = g + sum1(d) + f1(d, e, f) + K[i+1] + w[i+1];
t2 = sum0(h) + f3(h, a, b);
c += t1;
g = t1 + t2;
t1 = f + sum1(c) + f1(c, d, e) + K[i+2] + w[i+2];
t2 = sum0(g) + f3(g, h, a);
b += t1;
f = t1 + t2;
t1 = e + sum1(b) + f1(b, c, d) + K[i+3] + w[i+3];
t2 = sum0(f) + f3(f, g, h);
a += t1;
e = t1 + t2;
t1 = d + sum1(a) + f1(a, b, c) + K[i+4] + w[i+4];
t2 = sum0(e) + f3(e, f, g);
h += t1;
d = t1 + t2;
t1 = c + sum1(h) + f1(h, a, b) + K[i+5] + w[i+5];
t2 = sum0(d) + f3(d, e, f);
g += t1;
c = t1 + t2;
t1 = b + sum1(g) + f1(g, h, a) + K[i+6] + w[i+6];
t2 = sum0(c) + f3(c, d, e);
f += t1;
b = t1 + t2;
t1 = a + sum1(f) + f1(f, g, h) + K[i+7] + w[i+7];
t2 = sum0(b) + f3(b, c, d);
e += t1;
a = t1 + t2;
i += 8;
#endif
}
ctx->h0 += a;
ctx->h1 += b;
ctx->h2 += c;
ctx->h3 += d;
ctx->h4 += e;
ctx->h5 += f;
ctx->h6 += g;
ctx->h7 += h;
return /*burn_stack*/ 74*4+32;
}
#undef S0
#undef S1
#undef R
/* Common function to write a chunk of data to the transform function
of a hash algorithm. Note that the use of the term "block" does
not imply a fixed size block. Note that we explicitly allow to use
this function after the context has been finalized; the result does
not have any meaning but writing after finalize is sometimes
helpful to mitigate timing attacks. */
void
sha256_update(struct sha256_context *ctx, const byte *buf, size_t len)
{
if (ctx->count)
{
/* Fill rest of internal buffer */
for (; len && ctx->count < SHA256_BLOCK_SIZE; len--)
ctx->buf[ctx->count++] = *buf++;
if (ctx->count < SHA256_BLOCK_SIZE)
return;
/* Process data from internal buffer */
sha256_transform(ctx, ctx->buf);
ctx->nblocks++;
ctx->count = 0;
}
if (!len)
return;
/* Process data from input buffer */
while (len >= SHA256_BLOCK_SIZE)
{
sha256_transform(ctx, buf);
ctx->nblocks++;
buf += SHA256_BLOCK_SIZE;
len -= SHA256_BLOCK_SIZE;
}
/* Copy remaining data to internal buffer */
memcpy(ctx->buf, buf, len);
ctx->count = len;
}
/*
* The routine finally terminates the computation and returns the digest. The
* handle is prepared for a new cycle, but adding bytes to the handle will the
* destroy the returned buffer.
*
* Returns: 32 bytes with the message the digest. 28 bytes for SHA-224.
*/
byte *
sha256_final(struct sha256_context *ctx)
{
u32 t, th, msb, lsb;
sha256_update(ctx, NULL, 0); /* flush */
t = ctx->nblocks;
th = 0;
/* multiply by 64 to make a byte count */
lsb = t << 6;
msb = (th << 6) | (t >> 26);
/* add the count */
t = lsb;
if ((lsb += ctx->count) < t)
msb++;
/* multiply by 8 to make a bit count */
t = lsb;
lsb <<= 3;
msb <<= 3;
msb |= t >> 29;
if (ctx->count < 56)
{
/* enough room */
ctx->buf[ctx->count++] = 0x80; /* pad */
while (ctx->count < 56)
ctx->buf[ctx->count++] = 0; /* pad */
}
else
{
/* need one extra block */
ctx->buf[ctx->count++] = 0x80; /* pad character */
while (ctx->count < 64)
ctx->buf[ctx->count++] = 0;
sha256_update(ctx, NULL, 0); /* flush */;
memset(ctx->buf, 0, 56 ); /* fill next block with zeroes */
}
/* append the 64 bit count */
put_u32(ctx->buf + 56, msb);
put_u32(ctx->buf + 60, lsb);
sha256_transform(ctx, ctx->buf);
byte *p = ctx->buf;
#define X(a) do { put_u32(p, ctx->h##a); p += 4; } while(0)
X(0);
X(1);
X(2);
X(3);
X(4);
X(5);
X(6);
X(7);
#undef X
return ctx->buf;
}
/*
* SHA256-HMAC
*/
static void
sha256_hash_buffer(byte *outbuf, const byte *buffer, size_t length)
{
struct sha256_context ctx;
sha256_init(&ctx);
sha256_update(&ctx, buffer, length);
memcpy(outbuf, sha256_final(&ctx), SHA256_SIZE);
}
void
sha256_hmac_init(struct sha256_hmac_context *ctx, const byte *key, size_t keylen)
{
byte keybuf[SHA256_BLOCK_SIZE], buf[SHA256_BLOCK_SIZE];
/* Hash the key if necessary */
if (keylen <= SHA256_BLOCK_SIZE)
{
memcpy(keybuf, key, keylen);
memset(keybuf + keylen, 0, SHA256_BLOCK_SIZE - keylen);
}
else
{
sha256_hash_buffer(keybuf, key, keylen);
memset(keybuf + SHA256_SIZE, 0, SHA256_BLOCK_SIZE - SHA256_SIZE);
}
/* Initialize the inner digest */
sha256_init(&ctx->ictx);
int i;
for (i = 0; i < SHA256_BLOCK_SIZE; i++)
buf[i] = keybuf[i] ^ 0x36;
sha256_update(&ctx->ictx, buf, SHA256_BLOCK_SIZE);
/* Initialize the outer digest */
sha256_init(&ctx->octx);
for (i = 0; i < SHA256_BLOCK_SIZE; i++)
buf[i] = keybuf[i] ^ 0x5c;
sha256_update(&ctx->octx, buf, SHA256_BLOCK_SIZE);
}
void
sha256_hmac_update(struct sha256_hmac_context *ctx, const byte *buf, size_t buflen)
{
/* Just update the inner digest */
sha256_update(&ctx->ictx, buf, buflen);
}
byte *
sha256_hmac_final(struct sha256_hmac_context *ctx)
{
/* Finish the inner digest */
byte *isha = sha256_final(&ctx->ictx);
/* Finish the outer digest */
sha256_update(&ctx->octx, isha, SHA256_SIZE);
return sha256_final(&ctx->octx);
}
/*
* SHA224-HMAC
*/
static void
sha224_hash_buffer(byte *outbuf, const byte *buffer, size_t length)
{
struct sha224_context ctx;
sha224_init(&ctx);
sha224_update(&ctx, buffer, length);
memcpy(outbuf, sha224_final(&ctx), SHA224_SIZE);
}
void
sha224_hmac_init(struct sha224_hmac_context *ctx, const byte *key, size_t keylen)
{
byte keybuf[SHA224_BLOCK_SIZE], buf[SHA224_BLOCK_SIZE];
/* Hash the key if necessary */
if (keylen <= SHA224_BLOCK_SIZE)
{
memcpy(keybuf, key, keylen);
memset(keybuf + keylen, 0, SHA224_BLOCK_SIZE - keylen);
}
else
{
sha224_hash_buffer(keybuf, key, keylen);
memset(keybuf + SHA224_SIZE, 0, SHA224_BLOCK_SIZE - SHA224_SIZE);
}
/* Initialize the inner digest */
sha224_init(&ctx->ictx);
int i;
for (i = 0; i < SHA224_BLOCK_SIZE; i++)
buf[i] = keybuf[i] ^ 0x36;
sha224_update(&ctx->ictx, buf, SHA224_BLOCK_SIZE);
/* Initialize the outer digest */
sha224_init(&ctx->octx);
for (i = 0; i < SHA224_BLOCK_SIZE; i++)
buf[i] = keybuf[i] ^ 0x5c;
sha224_update(&ctx->octx, buf, SHA224_BLOCK_SIZE);
}
void
sha224_hmac_update(struct sha224_hmac_context *ctx, const byte *buf, size_t buflen)
{
/* Just update the inner digest */
sha256_update(&ctx->ictx, buf, buflen);
}
byte *
sha224_hmac_final(struct sha224_hmac_context *ctx)
{
/* Finish the inner digest */
byte *isha = sha224_final(&ctx->ictx);
/* Finish the outer digest */
sha224_update(&ctx->octx, isha, SHA224_SIZE);
return sha224_final(&ctx->octx);
}

73
lib/sha256.h Normal file
View File

@ -0,0 +1,73 @@
/*
* BIRD Library -- SHA-256 and SHA-224 Hash Functions,
* HMAC-SHA-256 and HMAC-SHA-224 Functions
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Based on the code from libgcrypt-1.6.0, which is
* (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_SHA256_H_
#define _BIRD_SHA256_H_
#include "nest/bird.h"
#define SHA224_SIZE 28
#define SHA224_HEX_SIZE 57
#define SHA224_BLOCK_SIZE 64
#define SHA256_SIZE 32
#define SHA256_HEX_SIZE 65
#define SHA256_BLOCK_SIZE 64
struct sha256_context {
u32 h0, h1, h2, h3, h4, h5, h6, h7;
byte buf[SHA256_BLOCK_SIZE];
uint nblocks;
uint count;
};
#define sha224_context sha256_context
void sha256_init(struct sha256_context *ctx);
void sha224_init(struct sha224_context *ctx);
void sha256_update(struct sha256_context *ctx, const byte *buf, size_t len);
static inline void sha224_update(struct sha224_context *ctx, const byte *buf, size_t len)
{ sha256_update(ctx, buf, len); }
byte *sha256_final(struct sha256_context *ctx);
static inline byte *sha224_final(struct sha224_context *ctx)
{ return sha256_final(ctx); }
/*
* HMAC-SHA256, HMAC-SHA224
*/
struct sha256_hmac_context
{
struct sha256_context ictx;
struct sha256_context octx;
};
#define sha224_hmac_context sha256_hmac_context
void sha256_hmac_init(struct sha256_hmac_context *ctx, const byte *key, size_t keylen);
void sha224_hmac_init(struct sha224_hmac_context *ctx, const byte *key, size_t keylen);
void sha256_hmac_update(struct sha256_hmac_context *ctx, const byte *buf, size_t buflen);
void sha224_hmac_update(struct sha224_hmac_context *ctx, const byte *buf, size_t buflen);
byte *sha256_hmac_final(struct sha256_hmac_context *ctx);
byte *sha224_hmac_final(struct sha224_hmac_context *ctx);
#endif /* _BIRD_SHA256_H_ */

620
lib/sha512.c Normal file
View File

@ -0,0 +1,620 @@
/*
* BIRD Library -- SHA-512 and SHA-384 Hash Functions,
* HMAC-SHA-512 and HMAC-SHA-384 Functions
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Based on the code from libgcrypt-1.6.0, which is
* (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "lib/sha512.h"
#include "lib/unaligned.h"
// #define SHA512_UNROLLED
void
sha512_init(struct sha512_context *ctx)
{
ctx->h0 = U64(0x6a09e667f3bcc908);
ctx->h1 = U64(0xbb67ae8584caa73b);
ctx->h2 = U64(0x3c6ef372fe94f82b);
ctx->h3 = U64(0xa54ff53a5f1d36f1);
ctx->h4 = U64(0x510e527fade682d1);
ctx->h5 = U64(0x9b05688c2b3e6c1f);
ctx->h6 = U64(0x1f83d9abfb41bd6b);
ctx->h7 = U64(0x5be0cd19137e2179);
ctx->nblocks = 0;
ctx->count = 0;
}
void
sha384_init(struct sha384_context *ctx)
{
ctx->h0 = U64(0xcbbb9d5dc1059ed8);
ctx->h1 = U64(0x629a292a367cd507);
ctx->h2 = U64(0x9159015a3070dd17);
ctx->h3 = U64(0x152fecd8f70e5939);
ctx->h4 = U64(0x67332667ffc00b31);
ctx->h5 = U64(0x8eb44a8768581511);
ctx->h6 = U64(0xdb0c2e0d64f98fa7);
ctx->h7 = U64(0x47b5481dbefa4fa4);
ctx->nblocks = 0;
ctx->count = 0;
}
static inline u64
ROTR(u64 x, u64 n)
{
return ((x >> n) | (x << (64 - n)));
}
static inline u64
Ch(u64 x, u64 y, u64 z)
{
return ((x & y) ^ ( ~x & z));
}
static inline u64
Maj(u64 x, u64 y, u64 z)
{
return ((x & y) ^ (x & z) ^ (y & z));
}
static inline u64
sum0(u64 x)
{
return (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39));
}
static inline u64
sum1(u64 x)
{
return (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41));
}
static const u64 k[] =
{
U64(0x428a2f98d728ae22), U64(0x7137449123ef65cd),
U64(0xb5c0fbcfec4d3b2f), U64(0xe9b5dba58189dbbc),
U64(0x3956c25bf348b538), U64(0x59f111f1b605d019),
U64(0x923f82a4af194f9b), U64(0xab1c5ed5da6d8118),
U64(0xd807aa98a3030242), U64(0x12835b0145706fbe),
U64(0x243185be4ee4b28c), U64(0x550c7dc3d5ffb4e2),
U64(0x72be5d74f27b896f), U64(0x80deb1fe3b1696b1),
U64(0x9bdc06a725c71235), U64(0xc19bf174cf692694),
U64(0xe49b69c19ef14ad2), U64(0xefbe4786384f25e3),
U64(0x0fc19dc68b8cd5b5), U64(0x240ca1cc77ac9c65),
U64(0x2de92c6f592b0275), U64(0x4a7484aa6ea6e483),
U64(0x5cb0a9dcbd41fbd4), U64(0x76f988da831153b5),
U64(0x983e5152ee66dfab), U64(0xa831c66d2db43210),
U64(0xb00327c898fb213f), U64(0xbf597fc7beef0ee4),
U64(0xc6e00bf33da88fc2), U64(0xd5a79147930aa725),
U64(0x06ca6351e003826f), U64(0x142929670a0e6e70),
U64(0x27b70a8546d22ffc), U64(0x2e1b21385c26c926),
U64(0x4d2c6dfc5ac42aed), U64(0x53380d139d95b3df),
U64(0x650a73548baf63de), U64(0x766a0abb3c77b2a8),
U64(0x81c2c92e47edaee6), U64(0x92722c851482353b),
U64(0xa2bfe8a14cf10364), U64(0xa81a664bbc423001),
U64(0xc24b8b70d0f89791), U64(0xc76c51a30654be30),
U64(0xd192e819d6ef5218), U64(0xd69906245565a910),
U64(0xf40e35855771202a), U64(0x106aa07032bbd1b8),
U64(0x19a4c116b8d2d0c8), U64(0x1e376c085141ab53),
U64(0x2748774cdf8eeb99), U64(0x34b0bcb5e19b48a8),
U64(0x391c0cb3c5c95a63), U64(0x4ed8aa4ae3418acb),
U64(0x5b9cca4f7763e373), U64(0x682e6ff3d6b2b8a3),
U64(0x748f82ee5defb2fc), U64(0x78a5636f43172f60),
U64(0x84c87814a1f0ab72), U64(0x8cc702081a6439ec),
U64(0x90befffa23631e28), U64(0xa4506cebde82bde9),
U64(0xbef9a3f7b2c67915), U64(0xc67178f2e372532b),
U64(0xca273eceea26619c), U64(0xd186b8c721c0c207),
U64(0xeada7dd6cde0eb1e), U64(0xf57d4f7fee6ed178),
U64(0x06f067aa72176fba), U64(0x0a637dc5a2c898a6),
U64(0x113f9804bef90dae), U64(0x1b710b35131c471b),
U64(0x28db77f523047d84), U64(0x32caab7b40c72493),
U64(0x3c9ebe0a15c9bebc), U64(0x431d67c49c100d4c),
U64(0x4cc5d4becb3e42b6), U64(0x597f299cfc657e2a),
U64(0x5fcb6fab3ad6faec), U64(0x6c44198c4a475817)
};
/*
* Transform the message W which consists of 16 64-bit-words
*/
static uint
sha512_transform(struct sha512_context *ctx, const byte *data)
{
u64 a, b, c, d, e, f, g, h;
u64 w[16];
uint t;
/* get values from the chaining vars */
a = ctx->h0;
b = ctx->h1;
c = ctx->h2;
d = ctx->h3;
e = ctx->h4;
f = ctx->h5;
g = ctx->h6;
h = ctx->h7;
for (t = 0; t < 16; t++)
w[t] = get_u64(data + t * 8);
#define S0(x) (ROTR((x),1) ^ ROTR((x),8) ^ ((x)>>7))
#define S1(x) (ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6))
for (t = 0; t < 80 - 16; )
{
u64 t1, t2;
/* Performance on a AMD Athlon(tm) Dual Core Processor 4050e
with gcc 4.3.3 using gcry_md_hash_buffer of each 10000 bytes
initialized to 0,1,2,3...255,0,... and 1000 iterations:
Not unrolled with macros: 440ms
Unrolled with macros: 350ms
Unrolled with inline: 330ms
*/
#ifndef SHA512_UNROLLED
t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[t%16];
w[t%16] += S1(w[(t - 2)%16]) + w[(t - 7)%16] + S0(w[(t - 15)%16]);
t2 = sum0(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
t++;
#else /* Unrolled */
t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[0];
w[0] += S1(w[14]) + w[9] + S0(w[1]);
t2 = sum0(a) + Maj(a, b, c);
d += t1;
h = t1 + t2;
t1 = g + sum1(d) + Ch(d, e, f) + k[t+1] + w[1];
w[1] += S1(w[15]) + w[10] + S0(w[2]);
t2 = sum0(h) + Maj(h, a, b);
c += t1;
g = t1 + t2;
t1 = f + sum1(c) + Ch(c, d, e) + k[t+2] + w[2];
w[2] += S1(w[0]) + w[11] + S0(w[3]);
t2 = sum0(g) + Maj(g, h, a);
b += t1;
f = t1 + t2;
t1 = e + sum1(b) + Ch(b, c, d) + k[t+3] + w[3];
w[3] += S1(w[1]) + w[12] + S0(w[4]);
t2 = sum0(f) + Maj(f, g, h);
a += t1;
e = t1 + t2;
t1 = d + sum1(a) + Ch(a, b, c) + k[t+4] + w[4];
w[4] += S1(w[2]) + w[13] + S0(w[5]);
t2 = sum0(e) + Maj(e, f, g);
h += t1;
d = t1 + t2;
t1 = c + sum1(h) + Ch(h, a, b) + k[t+5] + w[5];
w[5] += S1(w[3]) + w[14] + S0(w[6]);
t2 = sum0(d) + Maj(d, e, f);
g += t1;
c = t1 + t2;
t1 = b + sum1(g) + Ch(g, h, a) + k[t+6] + w[6];
w[6] += S1(w[4]) + w[15] + S0(w[7]);
t2 = sum0(c) + Maj(c, d, e);
f += t1;
b = t1 + t2;
t1 = a + sum1(f) + Ch(f, g, h) + k[t+7] + w[7];
w[7] += S1(w[5]) + w[0] + S0(w[8]);
t2 = sum0(b) + Maj(b, c, d);
e += t1;
a = t1 + t2;
t1 = h + sum1(e) + Ch(e, f, g) + k[t+8] + w[8];
w[8] += S1(w[6]) + w[1] + S0(w[9]);
t2 = sum0(a) + Maj(a, b, c);
d += t1;
h = t1 + t2;
t1 = g + sum1(d) + Ch(d, e, f) + k[t+9] + w[9];
w[9] += S1(w[7]) + w[2] + S0(w[10]);
t2 = sum0(h) + Maj(h, a, b);
c += t1;
g = t1 + t2;
t1 = f + sum1(c) + Ch(c, d, e) + k[t+10] + w[10];
w[10] += S1(w[8]) + w[3] + S0(w[11]);
t2 = sum0(g) + Maj(g, h, a);
b += t1;
f = t1 + t2;
t1 = e + sum1(b) + Ch(b, c, d) + k[t+11] + w[11];
w[11] += S1(w[9]) + w[4] + S0(w[12]);
t2 = sum0(f) + Maj(f, g, h);
a += t1;
e = t1 + t2;
t1 = d + sum1(a) + Ch(a, b, c) + k[t+12] + w[12];
w[12] += S1(w[10]) + w[5] + S0(w[13]);
t2 = sum0(e) + Maj(e, f, g);
h += t1;
d = t1 + t2;
t1 = c + sum1(h) + Ch(h, a, b) + k[t+13] + w[13];
w[13] += S1(w[11]) + w[6] + S0(w[14]);
t2 = sum0(d) + Maj(d, e, f);
g += t1;
c = t1 + t2;
t1 = b + sum1(g) + Ch(g, h, a) + k[t+14] + w[14];
w[14] += S1(w[12]) + w[7] + S0(w[15]);
t2 = sum0(c) + Maj(c, d, e);
f += t1;
b = t1 + t2;
t1 = a + sum1(f) + Ch(f, g, h) + k[t+15] + w[15];
w[15] += S1(w[13]) + w[8] + S0(w[0]);
t2 = sum0(b) + Maj(b, c, d);
e += t1;
a = t1 + t2;
t += 16;
#endif
}
for (; t < 80; )
{
u64 t1, t2;
#ifndef SHA512_UNROLLED
t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[t%16];
t2 = sum0(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
t++;
#else /* Unrolled */
t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[0];
t2 = sum0(a) + Maj(a, b, c);
d += t1;
h = t1 + t2;
t1 = g + sum1(d) + Ch(d, e, f) + k[t+1] + w[1];
t2 = sum0(h) + Maj(h, a, b);
c += t1;
g = t1 + t2;
t1 = f + sum1(c) + Ch(c, d, e) + k[t+2] + w[2];
t2 = sum0(g) + Maj(g, h, a);
b += t1;
f = t1 + t2;
t1 = e + sum1(b) + Ch(b, c, d) + k[t+3] + w[3];
t2 = sum0(f) + Maj(f, g, h);
a += t1;
e = t1 + t2;
t1 = d + sum1(a) + Ch(a, b, c) + k[t+4] + w[4];
t2 = sum0(e) + Maj(e, f, g);
h += t1;
d = t1 + t2;
t1 = c + sum1(h) + Ch(h, a, b) + k[t+5] + w[5];
t2 = sum0(d) + Maj(d, e, f);
g += t1;
c = t1 + t2;
t1 = b + sum1(g) + Ch(g, h, a) + k[t+6] + w[6];
t2 = sum0(c) + Maj(c, d, e);
f += t1;
b = t1 + t2;
t1 = a + sum1(f) + Ch(f, g, h) + k[t+7] + w[7];
t2 = sum0(b) + Maj(b, c, d);
e += t1;
a = t1 + t2;
t1 = h + sum1(e) + Ch(e, f, g) + k[t+8] + w[8];
t2 = sum0(a) + Maj(a, b, c);
d += t1;
h = t1 + t2;
t1 = g + sum1(d) + Ch(d, e, f) + k[t+9] + w[9];
t2 = sum0(h) + Maj(h, a, b);
c += t1;
g = t1 + t2;
t1 = f + sum1(c) + Ch(c, d, e) + k[t+10] + w[10];
t2 = sum0(g) + Maj(g, h, a);
b += t1;
f = t1 + t2;
t1 = e + sum1(b) + Ch(b, c, d) + k[t+11] + w[11];
t2 = sum0(f) + Maj(f, g, h);
a += t1;
e = t1 + t2;
t1 = d + sum1(a) + Ch(a, b, c) + k[t+12] + w[12];
t2 = sum0(e) + Maj(e, f, g);
h += t1;
d = t1 + t2;
t1 = c + sum1(h) + Ch(h, a, b) + k[t+13] + w[13];
t2 = sum0(d) + Maj(d, e, f);
g += t1;
c = t1 + t2;
t1 = b + sum1(g) + Ch(g, h, a) + k[t+14] + w[14];
t2 = sum0(c) + Maj(c, d, e);
f += t1;
b = t1 + t2;
t1 = a + sum1(f) + Ch(f, g, h) + k[t+15] + w[15];
t2 = sum0(b) + Maj(b, c, d);
e += t1;
a = t1 + t2;
t += 16;
#endif
}
/* Update chaining vars. */
ctx->h0 += a;
ctx->h1 += b;
ctx->h2 += c;
ctx->h3 += d;
ctx->h4 += e;
ctx->h5 += f;
ctx->h6 += g;
ctx->h7 += h;
return /* burn_stack */ (8 + 16) * sizeof(u64) + sizeof(u32) + 3 * sizeof(void*);
}
void
sha512_update(struct sha512_context *ctx, const byte *buf, size_t len)
{
if (ctx->count)
{
/* Fill rest of internal buffer */
for (; len && ctx->count < SHA512_BLOCK_SIZE; len--)
ctx->buf[ctx->count++] = *buf++;
if (ctx->count < SHA512_BLOCK_SIZE)
return;
/* Process data from internal buffer */
sha512_transform(ctx, ctx->buf);
ctx->nblocks++;
ctx->count = 0;
}
if (!len)
return;
/* Process data from input buffer */
while (len >= SHA512_BLOCK_SIZE)
{
sha512_transform(ctx, buf);
ctx->nblocks++;
buf += SHA512_BLOCK_SIZE;
len -= SHA512_BLOCK_SIZE;
}
/* Copy remaining data to internal buffer */
memcpy(ctx->buf, buf, len);
ctx->count = len;
}
/*
* The routine final terminates the computation and returns the digest. The
* handle is prepared for a new cycle, but adding bytes to the handle will the
* destroy the returned buffer.
*
* Returns: 64 bytes representing the digest. When used for sha384, we take the
* first 48 of those bytes.
*/
byte *
sha512_final(struct sha512_context *ctx)
{
u64 t, th, msb, lsb;
sha512_update(ctx, NULL, 0); /* flush */
t = ctx->nblocks;
th = 0;
/* multiply by 128 to make a byte count */
lsb = t << 7;
msb = (th << 7) | (t >> 57);
/* add the count */
t = lsb;
if ((lsb += ctx->count) < t)
msb++;
/* multiply by 8 to make a bit count */
t = lsb;
lsb <<= 3;
msb <<= 3;
msb |= t >> 61;
if (ctx->count < 112)
{
/* enough room */
ctx->buf[ctx->count++] = 0x80; /* pad */
while(ctx->count < 112)
ctx->buf[ctx->count++] = 0; /* pad */
}
else
{
/* need one extra block */
ctx->buf[ctx->count++] = 0x80; /* pad character */
while(ctx->count < 128)
ctx->buf[ctx->count++] = 0;
sha512_update(ctx, NULL, 0); /* flush */
memset(ctx->buf, 0, 112); /* fill next block with zeroes */
}
/* append the 128 bit count */
put_u64(ctx->buf + 112, msb);
put_u64(ctx->buf + 120, lsb);
sha512_transform(ctx, ctx->buf);
byte *p = ctx->buf;
#define X(a) do { put_u64(p, ctx->h##a); p += 8; } while(0)
X(0);
X(1);
X(2);
X(3);
X(4);
X(5);
X(6);
X(7);
#undef X
return ctx->buf;
}
/*
* SHA512-HMAC
*/
static void
sha512_hash_buffer(byte *outbuf, const byte *buffer, size_t length)
{
struct sha512_context ctx;
sha512_init(&ctx);
sha512_update(&ctx, buffer, length);
memcpy(outbuf, sha512_final(&ctx), SHA512_SIZE);
}
void
sha512_hmac_init(struct sha512_hmac_context *ctx, const byte *key, size_t keylen)
{
byte keybuf[SHA512_BLOCK_SIZE], buf[SHA512_BLOCK_SIZE];
/* Hash the key if necessary */
if (keylen <= SHA512_BLOCK_SIZE)
{
memcpy(keybuf, key, keylen);
memset(keybuf + keylen, 0, SHA512_BLOCK_SIZE - keylen);
}
else
{
sha512_hash_buffer(keybuf, key, keylen);
memset(keybuf + SHA512_SIZE, 0, SHA512_BLOCK_SIZE - SHA512_SIZE);
}
/* Initialize the inner digest */
sha512_init(&ctx->ictx);
int i;
for (i = 0; i < SHA512_BLOCK_SIZE; i++)
buf[i] = keybuf[i] ^ 0x36;
sha512_update(&ctx->ictx, buf, SHA512_BLOCK_SIZE);
/* Initialize the outer digest */
sha512_init(&ctx->octx);
for (i = 0; i < SHA512_BLOCK_SIZE; i++)
buf[i] = keybuf[i] ^ 0x5c;
sha512_update(&ctx->octx, buf, SHA512_BLOCK_SIZE);
}
void
sha512_hmac_update(struct sha512_hmac_context *ctx, const byte *buf, size_t buflen)
{
/* Just update the inner digest */
sha512_update(&ctx->ictx, buf, buflen);
}
byte *
sha512_hmac_final(struct sha512_hmac_context *ctx)
{
/* Finish the inner digest */
byte *isha = sha512_final(&ctx->ictx);
/* Finish the outer digest */
sha512_update(&ctx->octx, isha, SHA512_SIZE);
return sha512_final(&ctx->octx);
}
/*
* SHA384-HMAC
*/
static void
sha384_hash_buffer(byte *outbuf, const byte *buffer, size_t length)
{
struct sha384_context ctx;
sha384_init(&ctx);
sha384_update(&ctx, buffer, length);
memcpy(outbuf, sha384_final(&ctx), SHA384_SIZE);
}
void
sha384_hmac_init(struct sha384_hmac_context *ctx, const byte *key, size_t keylen)
{
byte keybuf[SHA384_BLOCK_SIZE], buf[SHA384_BLOCK_SIZE];
/* Hash the key if necessary */
if (keylen <= SHA384_BLOCK_SIZE)
{
memcpy(keybuf, key, keylen);
memset(keybuf + keylen, 0, SHA384_BLOCK_SIZE - keylen);
}
else
{
sha384_hash_buffer(keybuf, key, keylen);
memset(keybuf + SHA384_SIZE, 0, SHA384_BLOCK_SIZE - SHA384_SIZE);
}
/* Initialize the inner digest */
sha384_init(&ctx->ictx);
int i;
for (i = 0; i < SHA384_BLOCK_SIZE; i++)
buf[i] = keybuf[i] ^ 0x36;
sha384_update(&ctx->ictx, buf, SHA384_BLOCK_SIZE);
/* Initialize the outer digest */
sha384_init(&ctx->octx);
for (i = 0; i < SHA384_BLOCK_SIZE; i++)
buf[i] = keybuf[i] ^ 0x5c;
sha384_update(&ctx->octx, buf, SHA384_BLOCK_SIZE);
}
void
sha384_hmac_update(struct sha384_hmac_context *ctx, const byte *buf, size_t buflen)
{
/* Just update the inner digest */
sha384_update(&ctx->ictx, buf, buflen);
}
byte *
sha384_hmac_final(struct sha384_hmac_context *ctx)
{
/* Finish the inner digest */
byte *isha = sha384_final(&ctx->ictx);
/* Finish the outer digest */
sha384_update(&ctx->octx, isha, SHA384_SIZE);
return sha384_final(&ctx->octx);
}

73
lib/sha512.h Normal file
View File

@ -0,0 +1,73 @@
/*
* BIRD Library -- SHA-512 and SHA-384 Hash Functions,
* HMAC-SHA-512 and HMAC-SHA-384 Functions
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Based on the code from libgcrypt-1.6.0, which is
* (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_SHA512_H_
#define _BIRD_SHA512_H_
#include "nest/bird.h"
#define SHA384_SIZE 48
#define SHA384_HEX_SIZE 97
#define SHA384_BLOCK_SIZE 128
#define SHA512_SIZE 64
#define SHA512_HEX_SIZE 129
#define SHA512_BLOCK_SIZE 128
struct sha512_context {
u64 h0, h1, h2, h3, h4, h5, h6, h7;
byte buf[SHA512_BLOCK_SIZE];
uint nblocks;
uint count;
};
#define sha384_context sha512_context
void sha512_init(struct sha512_context *ctx);
void sha384_init(struct sha384_context *ctx);
void sha512_update(struct sha512_context *ctx, const byte *buf, size_t len);
static inline void sha384_update(struct sha384_context *ctx, const byte *buf, size_t len)
{ sha512_update(ctx, buf, len); }
byte *sha512_final(struct sha512_context *ctx);
static inline byte *sha384_final(struct sha384_context *ctx)
{ return sha512_final(ctx); }
/*
* HMAC-SHA512, HMAC-SHA384
*/
struct sha512_hmac_context
{
struct sha512_context ictx;
struct sha512_context octx;
};
#define sha384_hmac_context sha512_hmac_context
void sha512_hmac_init(struct sha512_hmac_context *ctx, const byte *key, size_t keylen);
void sha384_hmac_init(struct sha384_hmac_context *ctx, const byte *key, size_t keylen);
void sha512_hmac_update(struct sha512_hmac_context *ctx, const byte *buf, size_t buflen);
void sha384_hmac_update(struct sha384_hmac_context *ctx, const byte *buf, size_t buflen);
byte *sha512_hmac_final(struct sha512_hmac_context *ctx);
byte *sha384_hmac_final(struct sha384_hmac_context *ctx);
#endif /* _BIRD_SHA512_H_ */

View File

@ -51,7 +51,7 @@ static size_t slab_memsize(resource *r);
struct slab {
resource r;
unsigned size;
uint size;
list objs;
};
@ -71,7 +71,7 @@ struct sl_obj {
};
slab *
sl_new(pool *p, unsigned size)
sl_new(pool *p, uint size)
{
slab *s = ralloc(p, &sl_class);
s->size = size;
@ -144,7 +144,7 @@ slab_memsize(resource *r)
struct slab {
resource r;
unsigned obj_size, head_size, objs_per_slab, num_empty_heads, data_size;
uint obj_size, head_size, objs_per_slab, num_empty_heads, data_size;
list empty_heads, partial_heads, full_heads;
};
@ -185,10 +185,10 @@ struct sl_alignment { /* Magic structure for testing of alignment */
* objects of size @size can be allocated.
*/
slab *
sl_new(pool *p, unsigned size)
sl_new(pool *p, uint size)
{
slab *s = ralloc(p, &sl_class);
unsigned int align = sizeof(struct sl_alignment);
uint align = sizeof(struct sl_alignment);
if (align < sizeof(int))
align = sizeof(int);
s->data_size = size;
@ -214,7 +214,7 @@ sl_new_head(slab *s)
struct sl_head *h = xmalloc(SLAB_SIZE);
struct sl_obj *o = (struct sl_obj *)((byte *)h+s->head_size);
struct sl_obj *no;
unsigned int n = s->objs_per_slab;
uint n = s->objs_per_slab;
h->first_free = o;
h->num_full = 0;

View File

@ -20,7 +20,7 @@ typedef struct birdsock {
int type; /* Socket type */
void *data; /* User data */
ip_addr saddr, daddr; /* IPA_NONE = unspecified */
unsigned sport, dport; /* 0 = unspecified (for IP: protocol type) */
uint sport, dport; /* 0 = unspecified (for IP: protocol type) */
int tos; /* TOS / traffic class, -1 = default */
int priority; /* Local socket priority, -1 = default */
int ttl; /* Time To Live, -1 = default */
@ -28,20 +28,21 @@ typedef struct birdsock {
struct iface *iface; /* Interface; specify this for broad/multicast sockets */
byte *rbuf, *rpos; /* NULL=allocate automatically */
unsigned rbsize;
uint fast_rx; /* RX has higher priority in event loop */
uint rbsize;
int (*rx_hook)(struct birdsock *, int size); /* NULL=receiving turned off, returns 1 to clear rx buffer */
byte *tbuf, *tpos; /* NULL=allocate automatically */
byte *ttx; /* Internal */
unsigned tbsize;
uint tbsize;
void (*tx_hook)(struct birdsock *);
void (*err_hook)(struct birdsock *, int); /* errno or zero if EOF */
/* Information about received datagrams (UDP, RAW), valid in rx_hook */
ip_addr faddr, laddr; /* src (From) and dst (Local) address of the datagram */
unsigned fport; /* src port of the datagram */
unsigned lifindex; /* local interface that received the datagram */
uint fport; /* src port of the datagram */
uint lifindex; /* local interface that received the datagram */
/* laddr and lifindex are valid only if SKF_LADDR_RX flag is set to request it */
int af; /* Address family (AF_INET, AF_INET6 or 0 for non-IP) of fd */
@ -59,8 +60,8 @@ sock *sock_new(pool *); /* Allocate new socket */
int sk_open(sock *); /* Open socket */
int sk_rx_ready(sock *s);
int sk_send(sock *, unsigned len); /* Send data, <0=err, >0=ok, 0=sleep */
int sk_send_to(sock *, unsigned len, ip_addr to, unsigned port); /* sk_send to given destination */
int sk_send(sock *, uint len); /* Send data, <0=err, >0=ok, 0=sleep */
int sk_send_to(sock *, uint len, ip_addr to, uint port); /* sk_send to given destination */
void sk_reallocate(sock *); /* Free and allocate tbuf & rbuf */
void sk_set_rbsize(sock *s, uint val); /* Resize RX buffer */
void sk_set_tbsize(sock *s, uint val); /* Resize TX buffer, keeping content */
@ -86,7 +87,7 @@ int sk_leave_group(sock *s, ip_addr maddr); /* Leave multicast group on sk iface
int sk_setup_broadcast(sock *s);
int sk_set_ttl(sock *s, int ttl); /* Set transmit TTL for given socket */
int sk_set_min_ttl(sock *s, int ttl); /* Set minimal accepted TTL for given socket */
int sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd);
int sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, struct iface *ifa, char *passwd, int setkey);
int sk_set_ipv6_checksum(sock *s, int offset);
int sk_set_icmp6_filter(sock *s, int p1, int p2);
void sk_log_error(sock *s, const char *p);

View File

@ -22,6 +22,14 @@ int buffer_vprint(buffer *buf, const char *fmt, va_list args);
int buffer_print(buffer *buf, const char *fmt, ...);
void buffer_puts(buffer *buf, const char *str);
int patmatch(byte *pat, byte *str);
int patmatch(const byte *pat, const byte *str);
static inline char *xbasename(const char *str)
{
char *s = strrchr(str, '/');
return s ? s+1 : (char *) str;
}
#define ROUTER_ID_64_LENGTH 23
#endif

View File

@ -19,11 +19,8 @@
#include "lib/string.h"
/* XXX need ifdef to intsead use bsd's #include <sys/endian.h> */
#include <endian.h>
static inline u16
get_u16(void *p)
get_u16(const void *p)
{
u16 x;
memcpy(&x, p, 2);
@ -31,7 +28,7 @@ get_u16(void *p)
}
static inline u32
get_u32(void *p)
get_u32(const void *p)
{
u32 x;
memcpy(&x, p, 4);
@ -39,11 +36,12 @@ get_u32(void *p)
}
static inline u64
get_u64(void *p)
get_u64(const void *p)
{
u64 x;
memcpy(&x, p, 8);
return be64toh(x);
u32 xh, xl;
memcpy(&xh, p, 4);
memcpy(&xl, p+4, 4);
return (((u64) ntohl(xh)) << 32) | ntohl(xl);
}
static inline void
@ -63,8 +61,11 @@ put_u32(void *p, u32 x)
static inline void
put_u64(void *p, u64 x)
{
x = htobe64(x);
memcpy(p, &x, 8);
u32 xh, xl;
xh = htonl(x >> 32);
xl = htonl((u32) x);
memcpy(p, &xh, 4);
memcpy(p+4, &xl, 4);
}
#endif

View File

@ -24,7 +24,7 @@
* Wherever possible, please use the memory resources instead.
*/
void *
xmalloc(unsigned size)
xmalloc(uint size)
{
void *p = malloc(size);
if (p)
@ -44,7 +44,7 @@ xmalloc(unsigned size)
* Wherever possible, please use the memory resources instead.
*/
void *
xrealloc(void *ptr, unsigned size)
xrealloc(void *ptr, uint size)
{
void *p = realloc(ptr, size);
if (p)

View File

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

View File

@ -23,7 +23,7 @@ int h[65536];
* = ((1-1/k)^k)^a which we can approximate by e^-a.
*/
unsigned int hf(unsigned int n)
uint hf(uint n)
{
#if 0
n = (n ^ (n >> 16)) & 0xffff;
@ -58,7 +58,7 @@ main(int argc, char **argv)
while (max--)
{
unsigned int i, e;
uint i, e;
if (scanf("%x/%d", &i, &e) != 2)
if (feof(stdin))
break;

View File

@ -124,7 +124,7 @@ as_path_convert_to_new(struct adata *path, byte *dst, int req_as)
}
void
as_path_format(struct adata *path, byte *buf, unsigned int size)
as_path_format(struct adata *path, byte *buf, uint size)
{
byte *p = path->data;
byte *e = p + path->length;
@ -220,7 +220,7 @@ as_path_get_last(struct adata *path, u32 *orig_as)
p += BS * len;
}
break;
default: bug("as_path_get_first: Invalid path segment");
default: bug("Invalid path segment");
}
}
@ -229,6 +229,35 @@ as_path_get_last(struct adata *path, u32 *orig_as)
return found;
}
u32
as_path_get_last_nonaggregated(struct adata *path)
{
u8 *p = path->data;
u8 *q = p+path->length;
u32 res = 0;
int len;
while (p<q)
{
switch (*p++)
{
case AS_PATH_SET:
return res;
case AS_PATH_SEQUENCE:
if (len = *p++)
res = get_as(p + BS * (len - 1));
p += BS * len;
break;
default: bug("Invalid path segment");
}
}
return res;
}
int
as_path_get_first(struct adata *path, u32 *last_as)
{

View File

@ -32,7 +32,7 @@
* the buffer to indicate truncation.
*/
int
int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int size)
int_set_format(struct adata *set, int way, int from, byte *buf, uint size)
{
u32 *z = (u32 *) set->data;
byte *end = buf + size - 24;
@ -113,7 +113,7 @@ ec_format(byte *buf, u64 ec)
}
int
ec_set_format(struct adata *set, int from, byte *buf, unsigned int size)
ec_set_format(struct adata *set, int from, byte *buf, uint size)
{
u32 *z = int_set_get_data(set);
byte *end = buf + size - 24;

View File

@ -30,11 +30,12 @@ struct f_tree;
struct adata *as_path_prepend(struct linpool *pool, struct adata *olda, u32 as);
int as_path_convert_to_old(struct adata *path, byte *dst, int *new_used);
int as_path_convert_to_new(struct adata *path, byte *dst, int req_as);
void as_path_format(struct adata *path, byte *buf, unsigned int size);
void as_path_format(struct adata *path, byte *buf, uint size);
int as_path_getlen(struct adata *path);
int as_path_getlen_int(struct adata *path, int bs);
int as_path_get_first(struct adata *path, u32 *orig_as);
int as_path_get_last(struct adata *path, u32 *last_as);
u32 as_path_get_last_nonaggregated(struct adata *path);
int as_path_contains(struct adata *path, u32 as, int min);
int as_path_match_set(struct adata *path, struct f_tree *set);
struct adata *as_path_filter(struct linpool *pool, struct adata *path, struct f_tree *set, u32 key, int pos);
@ -95,9 +96,9 @@ static inline u64 ec_ip4(u64 kind, u64 key, u64 val)
static inline u64 ec_generic(u64 key, u64 val)
{ return (key << 32) | val; }
int int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int size);
int int_set_format(struct adata *set, int way, int from, byte *buf, uint size);
int ec_format(byte *buf, u64 ec);
int ec_set_format(struct adata *set, int from, byte *buf, unsigned int size);
int ec_set_format(struct adata *set, int from, byte *buf, uint size);
int int_set_contains(struct adata *list, u32 val);
int ec_set_contains(struct adata *list, u64 val);
struct adata *int_set_add(struct linpool *pool, struct adata *list, u32 val);

View File

@ -32,6 +32,12 @@ struct bfd_request {
};
#define BFD_STATE_ADMIN_DOWN 0
#define BFD_STATE_DOWN 1
#define BFD_STATE_INIT 2
#define BFD_STATE_UP 3
#ifdef CONFIG_BFD
struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, void (*hook)(struct bfd_request *), void *data);

View File

@ -163,7 +163,7 @@ static void
cli_copy_message(cli *c)
{
byte *p, *q;
unsigned int cnt = 2;
uint cnt = 2;
if (c->ring_overflow)
{
@ -230,12 +230,12 @@ cli_written(cli *c)
static byte *cli_rh_pos;
static unsigned int cli_rh_len;
static uint cli_rh_len;
static int cli_rh_trick_flag;
struct cli *this_cli;
static int
cli_cmd_read_hook(byte *buf, unsigned int max, UNUSED int fd)
cli_cmd_read_hook(byte *buf, uint max, UNUSED int fd)
{
if (!cli_rh_trick_flag)
{
@ -330,7 +330,7 @@ static list cli_log_hooks;
static int cli_log_inited;
void
cli_set_log_echo(cli *c, unsigned int mask, unsigned int size)
cli_set_log_echo(cli *c, uint mask, uint size)
{
if (c->ring_buf)
{
@ -351,7 +351,7 @@ cli_set_log_echo(cli *c, unsigned int mask, unsigned int size)
}
void
cli_echo(unsigned int class, byte *msg)
cli_echo(uint class, byte *msg)
{
unsigned len, free, i, l;
cli *c;

View File

@ -40,10 +40,10 @@ typedef struct cli {
struct linpool *parser_pool; /* Pool used during parsing */
byte *ring_buf; /* Ring buffer for asynchronous messages */
byte *ring_end, *ring_read, *ring_write; /* Pointers to the ring buffer */
unsigned int ring_overflow; /* Counter of ring overflows */
unsigned int log_mask; /* Mask of allowed message levels */
unsigned int log_threshold; /* When free < log_threshold, store only important messages */
unsigned int async_msg_size; /* Total size of async messages queued in tx_buf */
uint ring_overflow; /* Counter of ring overflows */
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;
extern pool *cli_pool;
@ -55,7 +55,7 @@ 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_log_echo(cli *, unsigned int mask, unsigned int size);
void cli_set_log_echo(cli *, uint mask, uint size);
/* Functions provided to sysdep layer */
@ -64,7 +64,7 @@ void cli_init(void);
void cli_free(cli *);
void cli_kick(cli *);
void cli_written(cli *);
void cli_echo(unsigned int class, byte *msg);
void cli_echo(uint class, byte *msg);
static inline int cli_access_restricted(void)
{

View File

@ -94,6 +94,7 @@ rtrid:
idval:
NUM { $$ = $1; }
| '(' term ')' { $$ = f_eval_int($2); }
| RTRID
| IPA {
#ifndef IPV6
@ -102,6 +103,16 @@ idval:
cf_error("Router IDs must be entered as hexadecimal numbers or IPv4 addresses in IPv6 version");
#endif
}
| SYM {
if ($1->class == (SYM_CONSTANT | T_INT) || $1->class == (SYM_CONSTANT | T_QUAD))
$$ = SYM_VAL($1).i;
#ifndef IPV6
else if ($1->class == (SYM_CONSTANT | T_IP))
$$ = ipa_to_u32(SYM_VAL($1).px.ip);
#endif
else
cf_error("Number or IPv4 address constant expected");
}
;
@ -183,16 +194,18 @@ proto_name:
}
| FROM SYM {
struct symbol *s = cf_default_name(this_proto->protocol->template, &this_proto->protocol->name_counter);
s->class = this_proto->class;
s->def = this_proto;
this_proto->name = s->name;
if (($2->class != SYM_TEMPLATE) && ($2->class != SYM_PROTO)) cf_error("Template or protocol name expected");
proto_copy_config(this_proto, $2->def);
}
| SYM FROM SYM {
if (($3->class != SYM_TEMPLATE) && ($3->class != SYM_PROTO)) cf_error("Template or protocol name expected");
cf_define_symbol($1, this_proto->class, this_proto);
this_proto->name = $1->name;
if (($3->class != SYM_TEMPLATE) && ($3->class != SYM_PROTO)) cf_error("Template or protocol name expected");
proto_copy_config(this_proto, $3->def);
}
;
@ -322,6 +335,7 @@ dev_proto:
dev_proto_start proto_name '{'
| dev_proto proto_item ';'
| dev_proto dev_iface_patt ';'
| dev_proto CHECK LINK bool ';' { DIRECT_CFG->check_link = $4; }
;
dev_iface_init:

View File

@ -49,7 +49,7 @@
static slab *neigh_slab;
static list sticky_neigh_list, neigh_hash_table[NEIGH_HASH_SIZE];
static inline unsigned int
static inline uint
neigh_hash(struct proto *p, ip_addr *a)
{
return (p->hash_key ^ ipa_hash(*a)) & (NEIGH_HASH_SIZE-1);
@ -126,7 +126,7 @@ neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags)
{
neighbor *n;
int class, scope = -1;
unsigned int h = neigh_hash(p, a);
uint h = neigh_hash(p, a);
struct iface *i;
struct ifa *addr;

View File

@ -51,3 +51,18 @@ password_find_by_id(list *l, int id)
return NULL;
}
struct password_item *
password_find_by_value(list *l, char *pass, uint size)
{
struct password_item *pi;
if (!l)
return NULL;
WALK_LIST(pi, *l)
if (password_verify(pi, pass, size) && (pi->accfrom <= now_real) && (now_real < pi->accto))
return pi;
return NULL;
}

View File

@ -11,8 +11,6 @@
#define PASSWORD_H
#include "lib/timer.h"
#define MD5_AUTH_SIZE 16
struct password_item {
node n;
char *password;
@ -24,6 +22,7 @@ extern struct password_item *last_password_item;
struct password_item *password_find(list *l, int first_fit);
struct password_item *password_find_by_id(list *l, int id);
struct password_item *password_find_by_value(list *l, char *pass, uint size);
static inline int password_verify(struct password_item *p1, char *p2, uint size)
{

View File

@ -521,7 +521,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
WALK_LIST(oc, old->protos)
{
p = oc->proto;
sym = cf_find_symbol(oc->name);
sym = cf_find_symbol(new, oc->name);
if (sym && sym->class == SYM_PROTO && !new->shutdown)
{
/* Found match, let's check if we can smoothly switch to new configuration */
@ -919,6 +919,9 @@ protos_build(void)
proto_build(&proto_bfd);
bfd_init_all();
#endif
#ifdef CONFIG_BABEL
proto_build(&proto_babel);
#endif
proto_pool = rp_new(&root_pool, "Protocols");
proto_flush_event = ev_new(proto_pool);
@ -1260,6 +1263,7 @@ proto_want_export_down(struct proto *p)
rt_feed_baby_abort(p);
p->export_state = ES_DOWN;
p->stats.exp_routes = 0;
proto_unlink_ahooks(p);
}
@ -1488,7 +1492,7 @@ proto_show_basic_info(struct proto *p)
}
void
proto_cmd_show(struct proto *p, unsigned int verbose, int cnt)
proto_cmd_show(struct proto *p, uint verbose, int cnt)
{
byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE];
@ -1524,7 +1528,7 @@ proto_cmd_show(struct proto *p, unsigned int verbose, int cnt)
}
void
proto_cmd_disable(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED)
proto_cmd_disable(struct proto *p, uint arg UNUSED, int cnt UNUSED)
{
if (p->disabled)
{
@ -1540,7 +1544,7 @@ proto_cmd_disable(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED)
}
void
proto_cmd_enable(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED)
proto_cmd_enable(struct proto *p, uint arg UNUSED, int cnt UNUSED)
{
if (!p->disabled)
{
@ -1555,7 +1559,7 @@ proto_cmd_enable(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED)
}
void
proto_cmd_restart(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED)
proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED)
{
if (p->disabled)
{
@ -1573,7 +1577,7 @@ proto_cmd_restart(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED)
}
void
proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED)
proto_cmd_reload(struct proto *p, uint dir, int cnt UNUSED)
{
if (p->disabled)
{
@ -1615,19 +1619,19 @@ proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED)
}
void
proto_cmd_debug(struct proto *p, unsigned int mask, int cnt UNUSED)
proto_cmd_debug(struct proto *p, uint mask, int cnt UNUSED)
{
p->debug = mask;
}
void
proto_cmd_mrtdump(struct proto *p, unsigned int mask, int cnt UNUSED)
proto_cmd_mrtdump(struct proto *p, uint mask, int cnt UNUSED)
{
p->mrtdump = mask;
}
static void
proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, unsigned int, int), unsigned int arg)
proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int), uint arg)
{
if (s->class != SYM_PROTO)
{
@ -1640,7 +1644,7 @@ proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, unsigned i
}
static void
proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, unsigned int, int), unsigned int arg)
proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uint, int), uint arg)
{
int cnt = 0;
@ -1660,8 +1664,8 @@ proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, unsigned int, int)
}
void
proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, unsigned int, int),
int restricted, unsigned int arg)
proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uint, int),
int restricted, uint arg)
{
if (restricted && cli_access_restricted())
return;

View File

@ -76,7 +76,7 @@ void protos_dump_all(void);
extern struct protocol
proto_device, proto_radv, proto_rip, proto_static,
proto_ospf, proto_pipe, proto_bgp, proto_bgpsec, proto_bfd;
proto_ospf, proto_pipe, proto_bgp, proto_bgpsec, proto_bfd, proto_babel;
/*
* Routing Protocol Instance
@ -158,6 +158,7 @@ struct proto {
byte gr_wait; /* Route export to protocol is postponed until graceful restart */
byte down_sched; /* Shutdown is scheduled for later (PDS_*) */
byte down_code; /* Reason for shutdown (PDC_* codes) */
byte merge_limit; /* Maximal number of nexthops for RA_MERGED */
u32 hash_key; /* Random key used for hashing of neighbors */
bird_clock_t last_state_change; /* Time of last state transition */
char *last_state_name_announced; /* Last state name we've announced to the user */
@ -200,6 +201,7 @@ struct proto {
* rte_recalculate Called at the beginning of the best route selection
* rte_better Compare two rte's and decide which one is better (1=first, 0=second).
* rte_same Compare two rte's and decide whether they are identical (1=yes, 0=no).
* rte_mergable Compare two rte's and decide whether they could be merged (1=yes, 0=no).
* rte_insert Called whenever a rte is inserted to a routing table.
* rte_remove Called whenever a rte is removed from the routing table.
*/
@ -207,6 +209,7 @@ struct proto {
int (*rte_recalculate)(struct rtable *, struct network *, struct rte *, struct rte *, struct rte *);
int (*rte_better)(struct rte *, struct rte *);
int (*rte_same)(struct rte *, struct rte *);
int (*rte_mergable)(struct rte *, struct rte *);
void (*rte_insert)(struct network *, struct rte *);
void (*rte_remove)(struct network *, struct rte *);
@ -261,15 +264,15 @@ void proto_graceful_restart_unlock(struct proto *p);
void proto_show_limit(struct proto_limit *l, const char *dsc);
void proto_show_basic_info(struct proto *p);
void proto_cmd_show(struct proto *, unsigned int, int);
void proto_cmd_disable(struct proto *, unsigned int, int);
void proto_cmd_enable(struct proto *, unsigned int, int);
void proto_cmd_restart(struct proto *, unsigned int, int);
void proto_cmd_reload(struct proto *, unsigned int, int);
void proto_cmd_debug(struct proto *, unsigned int, int);
void proto_cmd_mrtdump(struct proto *, unsigned int, int);
void proto_cmd_show(struct proto *, uint, int);
void proto_cmd_disable(struct proto *, uint, int);
void proto_cmd_enable(struct proto *, uint, int);
void proto_cmd_restart(struct proto *, uint, int);
void proto_cmd_reload(struct proto *, uint, int);
void proto_cmd_debug(struct proto *, uint, int);
void proto_cmd_mrtdump(struct proto *, uint, int);
void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, unsigned int, int), int restricted, unsigned int arg);
void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uint, int), int restricted, uint arg);
struct proto *proto_get_named(struct symbol *, struct protocol *);
#define CMD_RELOAD 0

View File

@ -47,7 +47,7 @@ struct fib_iterator { /* See lib/slists.h for an explanation */
byte efef; /* 0xff to distinguish between iterator and node */
byte pad[3];
struct fib_node *node; /* Or NULL if freshly merged */
unsigned int hash;
uint hash;
};
typedef void (*fib_init_func)(struct fib_node *);
@ -56,11 +56,11 @@ struct fib {
pool *fib_pool; /* Pool holding all our data */
slab *fib_slab; /* Slab holding all fib nodes */
struct fib_node **hash_table; /* Node hash table */
unsigned int hash_size; /* Number of hash table entries (a power of two) */
unsigned int hash_order; /* Binary logarithm of hash_size */
unsigned int hash_shift; /* 16 - hash_log */
unsigned int entries; /* Number of entries */
unsigned int entries_min, entries_max;/* Entry count limits (else start rehashing) */
uint hash_size; /* Number of hash table entries (a power of two) */
uint hash_order; /* Binary logarithm of hash_size */
uint hash_shift; /* 16 - hash_log */
uint entries; /* Number of entries */
uint entries_min, entries_max; /* Entry count limits (else start rehashing) */
fib_init_func init; /* Constructor */
};
@ -75,10 +75,12 @@ void fib_check(struct fib *); /* Consistency check for debugging */
void fit_init(struct fib_iterator *, struct fib *); /* Internal functions, don't call */
struct fib_node *fit_get(struct fib *, struct fib_iterator *);
void fit_put(struct fib_iterator *, struct fib_node *);
void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos);
#define FIB_WALK(fib, z) do { \
struct fib_node *z, **ff = (fib)->hash_table; \
unsigned int count = (fib)->hash_size; \
uint count = (fib)->hash_size; \
while (count--) \
for(z = *ff++; z; z=z->next)
@ -88,8 +90,8 @@ void fit_put(struct fib_iterator *, struct fib_node *);
#define FIB_ITERATE_START(fib, it, z) do { \
struct fib_node *z = fit_get(fib, it); \
unsigned int count = (fib)->hash_size; \
unsigned int hpos = (it)->hash; \
uint count = (fib)->hash_size; \
uint hpos = (it)->hash; \
for(;;) { \
if (!z) \
{ \
@ -103,6 +105,11 @@ void fit_put(struct fib_iterator *, struct fib_node *);
#define FIB_ITERATE_PUT(it, z) fit_put(it, z)
#define FIB_ITERATE_PUT_NEXT(it, fib, z) fit_put_next(fib, it, z, hpos)
#define FIB_ITERATE_UNLINK(it, fib) fit_get(fib, it)
/*
* Master Routing Tables. Generally speaking, each of them contains a FIB
* with each entry pointing to a list of route entries representing routes
@ -174,7 +181,7 @@ struct hostentry {
ip_addr addr; /* IP address of host, part of key */
ip_addr link; /* (link-local) IP address of host, used as gw
if host is directly attached */
struct rtable *tab; /* Dependent table, part of key*/
struct rtable *tab; /* Dependent table, part of key */
struct hostentry *next; /* Next in hash chain */
unsigned hash_key; /* Hash key */
unsigned uc; /* Use count */
@ -196,10 +203,9 @@ typedef struct rte {
union { /* Protocol-dependent data (metrics etc.) */
#ifdef CONFIG_RIP
struct {
node garbage; /* List for garbage collection */
byte metric; /* RIP metric */
struct iface *from; /* Incoming iface */
u8 metric; /* RIP metric */
u16 tag; /* External route tag */
struct rip_entry *entry;
} rip;
#endif
#ifdef CONFIG_OSPF
@ -213,12 +219,18 @@ typedef struct rte {
struct {
u8 suppressed; /* Used for deterministic MED comparison */
} bgp;
#endif
#ifdef CONFIG_BABEL
struct {
u16 metric; /* Babel metric */
u64 router_id; /* Babel router id */
} babel;
#endif
struct { /* Routes generated by krt sync (both temporary and inherited ones) */
s8 src; /* Alleged route source (see krt.h) */
u8 proto; /* Kernel source protocol ID */
u8 type; /* Kernel route type */
u8 seen; /* Seen during last scan */
u8 best; /* Best route in network, propagated to core */
u32 metric; /* Kernel metric */
} krt;
} u;
@ -240,6 +252,7 @@ static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED);
#define RA_OPTIMAL 1 /* Announcement of optimal route change */
#define RA_ACCEPTED 2 /* Announcement of first accepted route */
#define RA_ANY 3 /* Announcement of any route change */
#define RA_MERGED 4 /* Announcement of optimal route merged with next ones */
/* Return value of import_control() callback */
#define RIC_ACCEPT 1 /* Accepted by protocol */
@ -263,12 +276,14 @@ void rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *s
static inline void rte_update(struct proto *p, net *net, rte *new) { rte_update2(p->main_ahook, net, new, p->main_source); }
void rte_discard(rtable *tab, rte *old);
int rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter *filter);
rte *rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, struct ea_list **tmpa, int silent);
void rt_refresh_begin(rtable *t, struct announce_hook *ah);
void rt_refresh_end(rtable *t, struct announce_hook *ah);
void rte_dump(rte *);
void rte_free(rte *);
rte *rte_do_cow(rte *);
static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r) : r; }
rte *rte_cow_rta(rte *r, linpool *lp);
void rt_dump(rtable *);
void rt_dump_all(void);
int rt_feed_baby(struct proto *p);
@ -320,7 +335,7 @@ struct mpnh {
ip_addr gw; /* Next hop */
struct iface *iface; /* Outgoing interface */
struct mpnh *next;
unsigned char weight;
byte weight;
};
struct rte_src {
@ -365,6 +380,7 @@ typedef struct rta {
#define RTS_OSPF_EXT2 10 /* OSPF external route type 2 */
#define RTS_BGP 11 /* BGP route */
#define RTS_PIPE 12 /* Inter-table wormhole */
#define RTS_BABEL 13 /* Babel route */
#define RTC_UNICAST 0
#define RTC_BROADCAST 1
@ -388,6 +404,12 @@ typedef struct rta {
#define IGP_METRIC_UNKNOWN 0x80000000 /* Default igp_metric used when no other
protocol-specific metric is availabe */
/* Route has regular, reachable nexthop (i.e. not RTD_UNREACHABLE and like) */
static inline int rte_is_reachable(rte *r)
{ uint d = r->attrs->dest; return (d == RTD_ROUTER) || (d == RTD_DEVICE) || (d == RTD_MULTIPATH); }
/*
* Extended Route Attributes
*/
@ -407,8 +429,9 @@ typedef struct eattr {
#define EAP_RIP 2 /* RIP */
#define EAP_OSPF 3 /* OSPF */
#define EAP_KRT 4 /* Kernel route attributes */
#define EAP_BGPSEC 5 /* BGPSEC attributes */
#define EAP_MAX 6
#define EAP_BABEL 5 /* Babel attributes */
#define EAP_BGPSEC 6 /* BGPSEC attributes */
#define EAP_MAX 7
#define EA_CODE(proto,id) (((proto) << 8) | (id))
#define EA_PROTO(ea) ((ea) >> 8)
@ -418,13 +441,15 @@ typedef struct eattr {
#define EA_CODE_MASK 0xffff
#define EA_ALLOW_UNDEF 0x10000 /* ea_find: allow EAF_TYPE_UNDEF */
#define EA_BIT(n) ((n) << 24) /* Used in bitfield accessors */
#define EAF_TYPE_MASK 0x0f /* Mask with this to get type */
#define EAF_TYPE_INT 0x01 /* 32-bit signed integer number */
#define EAF_TYPE_INT 0x01 /* 32-bit unsigned integer number */
#define EAF_TYPE_OPAQUE 0x02 /* Opaque byte string (not filterable) */
#define EAF_TYPE_IP_ADDRESS 0x04 /* IP address */
#define EAF_TYPE_ROUTER_ID 0x05 /* Router ID (IPv4 address) */
#define EAF_TYPE_AS_PATH 0x06 /* BGP AS path (encoding per RFC 1771:4.3) */
#define EAF_TYPE_BITFIELD 0x09 /* 32-bit embedded bitfield */
#define EAF_TYPE_INT_SET 0x0a /* Set of u32's (e.g., a community list) */
#define EAF_TYPE_EC_SET 0x0e /* Set of pairs of u32's - ext. community list */
#define EAF_TYPE_UNDEF 0x0f /* `force undefined' entry */
@ -434,7 +459,7 @@ typedef struct eattr {
#define EAF_TEMP 0x80 /* A temporary attribute (the one stored in the tmp attr list) */
struct adata {
unsigned int length; /* Length of data */
uint length; /* Length of data */
byte data[0];
};
@ -460,20 +485,28 @@ static inline void rt_lock_source(struct rte_src *src) { src->uc++; }
static inline void rt_unlock_source(struct rte_src *src) { src->uc--; }
void rt_prune_sources(void);
struct ea_walk_state {
ea_list *eattrs; /* Ccurrent ea_list, initially set by caller */
eattr *ea; /* Current eattr, initially NULL */
u32 visited[4]; /* Bitfield, limiting max to 128 */
};
eattr *ea_find(ea_list *, unsigned ea);
eattr *ea_walk(struct ea_walk_state *s, uint id, uint max);
int ea_get_int(ea_list *, unsigned ea, int def);
void ea_dump(ea_list *);
void ea_sort(ea_list *); /* Sort entries in all sub-lists */
unsigned ea_scan(ea_list *); /* How many bytes do we need for merged ea_list */
void ea_merge(ea_list *from, ea_list *to); /* Merge sub-lists to allocated buffer */
int ea_same(ea_list *x, ea_list *y); /* Test whether two ea_lists are identical */
unsigned int ea_hash(ea_list *e); /* Calculate 16-bit hash value */
uint ea_hash(ea_list *e); /* Calculate 16-bit hash value */
ea_list *ea_append(ea_list *to, ea_list *what);
void ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max);
int mpnh__same(struct mpnh *x, struct mpnh *y); /* Compare multipath nexthops */
static inline int mpnh_same(struct mpnh *x, struct mpnh *y)
{ return (x == y) || mpnh__same(x, y); }
struct mpnh *mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp);
void rta_init(void);
rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */
@ -481,25 +514,33 @@ static inline int rta_is_cached(rta *r) { return r->aflags & RTAF_CACHED; }
static inline rta *rta_clone(rta *r) { r->uc++; return r; }
void rta__free(rta *r);
static inline void rta_free(rta *r) { if (r && !--r->uc) rta__free(r); }
rta *rta_do_cow(rta *o, linpool *lp);
static inline rta * rta_cow(rta *r, linpool *lp) { return rta_is_cached(r) ? rta_do_cow(r, lp) : r; }
void rta_dump(rta *);
void rta_dump_all(void);
void rta_show(struct cli *, rta *, ea_list *);
void rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr *gw, ip_addr *ll);
/*
* rta_set_recursive_next_hop() acquires hostentry from hostcache and
* fills rta->hostentry field. New hostentry has zero use
* count. Cached rta locks its hostentry (increases its use count),
* uncached rta does not lock it. Hostentry with zero use count is
* removed asynchronously during host cache update, therefore it is
* safe to hold such hostentry temorarily. Hostentry holds a lock for
* a 'source' rta, mainly to share multipath nexthops. There is no
* need to hold a lock for hostentry->dep table, because that table
* contains routes responsible for that hostentry, and therefore is
* non-empty if given hostentry has non-zero use count. The protocol
* responsible for routes with recursive next hops should also hold a
* lock for a table governing that routes (argument tab to
* rta_set_recursive_next_hop()).
* rta_set_recursive_next_hop() acquires hostentry from hostcache and fills
* rta->hostentry field. New hostentry has zero use count. Cached rta locks its
* hostentry (increases its use count), uncached rta does not lock it. Hostentry
* with zero use count is removed asynchronously during host cache update,
* therefore it is safe to hold such hostentry temorarily. Hostentry holds a
* lock for a 'source' rta, mainly to share multipath nexthops.
*
* There is no need to hold a lock for hostentry->dep table, because that table
* contains routes responsible for that hostentry, and therefore is non-empty if
* given hostentry has non-zero use count. If the hostentry has zero use count,
* the entry is removed before dep is referenced.
*
* The protocol responsible for routes with recursive next hops should hold a
* lock for a 'source' table governing that routes (argument tab to
* rta_set_recursive_next_hop()), because its routes reference hostentries
* (through rta) related to the governing table. When all such routes are
* removed, rtas are immediately removed achieving zero uc. Then the 'source'
* table lock could be immediately released, although hostentries may still
* exist - they will be freed together with the 'source' table.
*/
static inline void rt_lock_hostentry(struct hostentry *he) { if (he) he->uc++; }
@ -515,6 +556,7 @@ extern struct protocol *attr_class_to_protocol[EAP_MAX];
#define DEF_PREF_DIRECT 240 /* Directly connected */
#define DEF_PREF_STATIC 200 /* Static route */
#define DEF_PREF_OSPF 150 /* OSPF intra-area, inter-area and type 1 external routes */
#define DEF_PREF_BABEL 130 /* Babel */
#define DEF_PREF_RIP 120 /* RIP */
#define DEF_PREF_BGP 100 /* BGP */
#define DEF_PREF_PIPE 70 /* Routes piped from other tables */

View File

@ -98,7 +98,7 @@ rte_src_init(void)
HASH_INIT(src_hash, rta_pool, RSH_INIT_ORDER);
}
static inline int u32_cto(unsigned int x) { return ffs(~x) - 1; }
static inline int u32_cto(uint x) { return ffs(~x) - 1; }
static inline u32
rte_src_alloc_id(void)
@ -167,7 +167,7 @@ rt_get_source(struct proto *p, u32 id)
src->private_id = id;
src->global_id = rte_src_alloc_id();
src->uc = 0;
HASH_INSERT2(src_hash, RSH, rta_pool, src);
return src;
@ -195,10 +195,10 @@ rt_prune_sources(void)
* Multipath Next Hop
*/
static inline unsigned int
static inline uint
mpnh_hash(struct mpnh *x)
{
unsigned int h = 0;
uint h = 0;
for (; x; x = x->next)
h ^= ipa_hash(x->gw);
@ -215,6 +215,94 @@ mpnh__same(struct mpnh *x, struct mpnh *y)
return x == y;
}
static int
mpnh_compare_node(struct mpnh *x, struct mpnh *y)
{
int r;
if (!x)
return 1;
if (!y)
return -1;
r = ((int) y->weight) - ((int) x->weight);
if (r)
return r;
r = ipa_compare(x->gw, y->gw);
if (r)
return r;
return ((int) x->iface->index) - ((int) y->iface->index);
}
static inline struct mpnh *
mpnh_copy_node(const struct mpnh *src, linpool *lp)
{
struct mpnh *n = lp_alloc(lp, sizeof(struct mpnh));
n->gw = src->gw;
n->iface = src->iface;
n->next = NULL;
n->weight = src->weight;
return n;
}
/**
* mpnh_merge - merge nexthop lists
* @x: list 1
* @y: list 2
* @rx: reusability of list @x
* @ry: reusability of list @y
* @max: max number of nexthops
* @lp: linpool for allocating nexthops
*
* The mpnh_merge() function takes two nexthop lists @x and @y and merges them,
* eliminating possible duplicates. The input lists must be sorted and the
* result is sorted too. The number of nexthops in result is limited by @max.
* New nodes are allocated from linpool @lp.
*
* The arguments @rx and @ry specify whether corresponding input lists may be
* consumed by the function (i.e. their nodes reused in the resulting list), in
* that case the caller should not access these lists after that. To eliminate
* issues with deallocation of these lists, the caller should use some form of
* bulk deallocation (e.g. stack or linpool) to free these nodes when the
* resulting list is no longer needed. When reusability is not set, the
* corresponding lists are not modified nor linked from the resulting list.
*/
struct mpnh *
mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp)
{
struct mpnh *root = NULL;
struct mpnh **n = &root;
while ((x || y) && max--)
{
int cmp = mpnh_compare_node(x, y);
if (cmp < 0)
{
*n = rx ? x : mpnh_copy_node(x, lp);
x = x->next;
}
else if (cmp > 0)
{
*n = ry ? y : mpnh_copy_node(y, lp);
y = y->next;
}
else
{
*n = rx ? x : (ry ? y : mpnh_copy_node(x, lp));
x = x->next;
y = y->next;
}
n = &((*n)->next);
}
*n = NULL;
return root;
}
static struct mpnh *
mpnh_copy(struct mpnh *o)
{
@ -307,6 +395,82 @@ ea_find(ea_list *e, unsigned id)
return a;
}
/**
* ea_walk - walk through extended attributes
* @s: walk state structure
* @id: start of attribute ID interval
* @max: length of attribute ID interval
*
* Given an extended attribute list, ea_walk() walks through the list looking
* for first occurrences of attributes with ID in specified interval from @id to
* (@id + @max - 1), returning pointers to found &eattr structures, storing its
* walk state in @s for subsequent calls.
* The function ea_walk() is supposed to be called in a loop, with initially
* zeroed walk state structure @s with filled the initial extended attribute
* list, returning one found attribute in each call or %NULL when no other
* attribute exists. The extended attribute list or the arguments should not be
* modified between calls. The maximum value of @max is 128.
*/
eattr *
ea_walk(struct ea_walk_state *s, uint id, uint max)
{
ea_list *e = s->eattrs;
eattr *a = s->ea;
eattr *a_max;
max = id + max;
if (a)
goto step;
for (; e; e = e->next)
{
if (e->flags & EALF_BISECT)
{
int l, r, m;
l = 0;
r = e->count - 1;
while (l < r)
{
m = (l+r) / 2;
if (e->attrs[m].id < id)
l = m + 1;
else
r = m;
}
a = e->attrs + l;
}
else
a = e->attrs;
step:
a_max = e->attrs + e->count;
for (; a < a_max; a++)
if ((a->id >= id) && (a->id < max))
{
int n = a->id - id;
if (BIT32_TEST(s->visited, n))
continue;
BIT32_SET(s->visited, n);
if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
continue;
s->eattrs = e;
s->ea = a;
return a;
}
else if (e->flags & EALF_BISECT)
break;
}
return NULL;
}
/**
* ea_get_int - fetch an integer attribute
* @e: attribute list
@ -559,12 +723,38 @@ get_generic_attr(eattr *a, byte **buf, int buflen UNUSED)
*buf += bsprintf(*buf, "igp_metric");
return GA_NAME;
}
return GA_UNKNOWN;
}
void
ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max)
{
byte *bound = buf + bufsize - 32;
u32 data = a->u.data;
int i;
for (i = min; i < max; i++)
if ((data & (1u << i)) && names[i])
{
if (buf > bound)
{
strcpy(buf, " ...");
return;
}
buf += bsprintf(buf, " %s", names[i]);
data &= ~(1u << i);
}
if (data)
bsprintf(buf, " %08x", data);
return;
}
static inline void
opaque_format(struct adata *ad, byte *buf, unsigned int size)
opaque_format(struct adata *ad, byte *buf, uint size)
{
byte *bound = buf + size - 10;
int i;
@ -639,7 +829,7 @@ ea_show(struct cli *c, eattr *e)
}
else if (EA_PROTO(e->id))
pos += bsprintf(pos, "%02x.", EA_PROTO(e->id));
else
else
status = get_generic_attr(e, &pos, end - pos);
if (status < GA_NAME)
@ -665,6 +855,9 @@ ea_show(struct cli *c, eattr *e)
case EAF_TYPE_AS_PATH:
as_path_format(ad, pos, end - pos);
break;
case EAF_TYPE_BITFIELD:
bsprintf(pos, "%08x", e->u.data);
break;
case EAF_TYPE_INT_SET:
ea_show_int_set(c, ad, 1, pos, buf, end);
return;
@ -733,7 +926,7 @@ ea_dump(ea_list *e)
* ea_hash() takes an extended attribute list and calculated a hopefully
* uniformly distributed hash value from its contents.
*/
inline unsigned int
inline uint
ea_hash(ea_list *e)
{
u32 h = 0;
@ -795,10 +988,10 @@ ea_append(ea_list *to, ea_list *what)
* rta's
*/
static unsigned int rta_cache_count;
static unsigned int rta_cache_size = 32;
static unsigned int rta_cache_limit;
static unsigned int rta_cache_mask;
static uint rta_cache_count;
static uint rta_cache_size = 32;
static uint rta_cache_limit;
static uint rta_cache_mask;
static rta **rta_hash_table;
static void
@ -812,7 +1005,7 @@ rta_alloc_hash(void)
rta_cache_mask = rta_cache_size - 1;
}
static inline unsigned int
static inline uint
rta_hash(rta *a)
{
return (((uint) (uintptr_t) a->src) ^ ipa_hash(a->gw) ^
@ -852,7 +1045,7 @@ rta_copy(rta *o)
static inline void
rta_insert(rta *r)
{
unsigned int h = r->hash_key & rta_cache_mask;
uint h = r->hash_key & rta_cache_mask;
r->next = rta_hash_table[h];
if (r->next)
r->next->pprev = &r->next;
@ -863,8 +1056,8 @@ rta_insert(rta *r)
static void
rta_rehash(void)
{
unsigned int ohs = rta_cache_size;
unsigned int h;
uint ohs = rta_cache_size;
uint h;
rta *r, *n;
rta **oht = rta_hash_table;
@ -897,7 +1090,7 @@ rta *
rta_lookup(rta *o)
{
rta *r;
unsigned int h;
uint h;
ASSERT(!(o->aflags & RTAF_CACHED));
if (o->eattrs)
@ -945,6 +1138,16 @@ rta__free(rta *a)
sl_free(rta_slab, a);
}
rta *
rta_do_cow(rta *o, linpool *lp)
{
rta *r = lp_alloc(lp, sizeof(rta));
memcpy(r, o, sizeof(rta));
r->aflags = 0;
r->uc = 0;
return r;
}
/**
* rta_dump - dump route attributes
* @a: attribute structure to dump
@ -988,7 +1191,7 @@ void
rta_dump_all(void)
{
rta *a;
unsigned int h;
uint h;
debug("Route attribute cache (%d entries, rehash at %d):\n", rta_cache_count, rta_cache_limit);
for(h=0; h<rta_cache_size; h++)

View File

@ -51,7 +51,10 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
DBG("dev_if_notify: device shutdown: prefix not found\n");
return;
}
rte_update(p, n, NULL);
/* Use iface ID as local source ID */
struct rte_src *src = rt_get_source(p, ad->iface->index);
rte_update2(p->main_ahook, n, NULL, src);
}
else if (c & IF_CHANGE_UP)
{
@ -61,8 +64,14 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
DBG("dev_if_notify: %s:%I going up\n", ad->iface->name, ad->ip);
if (P->check_link && !(ad->iface->flags & IF_LINK_UP))
return;
/* Use iface ID as local source ID */
struct rte_src *src = rt_get_source(p, ad->iface->index);
rta a0 = {
.src = p->main_source,
.src = src,
.source = RTS_DEVICE,
.scope = SCOPE_UNIVERSE,
.cast = RTC_UNICAST,
@ -75,15 +84,35 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
e = rte_get_temp(a);
e->net = n;
e->pflags = 0;
rte_update(p, n, e);
rte_update2(p->main_ahook, n, e, src);
}
}
static void
dev_if_notify(struct proto *p, uint c, struct iface *iface)
{
struct rt_dev_config *cf = (void *) p->cf;
if (c & (IF_CHANGE_UP | IF_CHANGE_DOWN))
return;
if ((c & IF_CHANGE_LINK) && cf->check_link)
{
uint ac = (iface->flags & IF_LINK_UP) ? IF_CHANGE_UP : IF_CHANGE_DOWN;
struct ifa *a;
WALK_LIST(a, iface->addrs)
dev_ifa_notify(p, ac, a);
}
}
static struct proto *
dev_init(struct proto_config *c)
{
struct proto *p = proto_new(c, sizeof(struct proto));
p->if_notify = dev_if_notify;
p->ifa_notify = dev_ifa_notify;
return p;
}
@ -94,7 +123,8 @@ dev_reconfigure(struct proto *p, struct proto_config *new)
struct rt_dev_config *o = (struct rt_dev_config *) p->cf;
struct rt_dev_config *n = (struct rt_dev_config *) new;
return iface_patts_equal(&o->iface_list, &n->iface_list, NULL);
return iface_patts_equal(&o->iface_list, &n->iface_list, NULL) &&
(o->check_link == n->check_link);
}
static void
@ -109,6 +139,8 @@ dev_copy_config(struct proto_config *dest, struct proto_config *src)
* old nodes cannot be modified (although they contain internal lists).
*/
cfg_copy_list(&d->iface_list, &s->iface_list, sizeof(struct iface_patt));
d->check_link = s->check_link;
}
struct protocol proto_device = {

View File

@ -12,6 +12,7 @@
struct rt_dev_config {
struct proto_config c;
list iface_list; /* list of struct iface_patt */
int check_link;
};
#endif

View File

@ -206,7 +206,7 @@ fib_histogram(struct fib *f)
void *
fib_get(struct fib *f, ip_addr *a, int len)
{
unsigned int h = ipa_hash(*a);
uint h = ipa_hash(*a);
struct fib_node **ee = f->hash_table + (h >> f->hash_shift);
struct fib_node *g, *e = *ee;
u32 uid = h << 16;
@ -321,7 +321,7 @@ void
fib_delete(struct fib *f, void *E)
{
struct fib_node *e = E;
unsigned int h = fib_hash(f, &e->prefix);
uint h = fib_hash(f, &e->prefix);
struct fib_node **ee = f->hash_table + h;
struct fib_iterator *it;
@ -430,6 +430,25 @@ fit_put(struct fib_iterator *i, struct fib_node *n)
i->prev = (struct fib_iterator *) n;
}
void
fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos)
{
if (n = n->next)
goto found;
while (++hpos < f->hash_size)
if (n = f->hash_table[hpos])
goto found;
/* We are at the end */
i->prev = i->next = NULL;
i->node = NULL;
return;
found:
fit_put(i, n);
}
#ifdef DEBUGGING
/**
@ -442,7 +461,7 @@ fit_put(struct fib_iterator *i, struct fib_node *n)
void
fib_check(struct fib *f)
{
unsigned int i, ec, lo, nulls;
uint i, ec, lo, nulls;
ec = 0;
for(i=0; i<f->hash_size; i++)
@ -452,7 +471,7 @@ fib_check(struct fib *f)
for(n=f->hash_table[i]; n; n=n->next)
{
struct fib_iterator *j, *j0;
unsigned int h0 = ipa_hash(n->prefix);
uint h0 = ipa_hash(n->prefix);
if (h0 < lo)
bug("fib_check: discord in hash chains");
lo = h0;
@ -489,7 +508,7 @@ struct fib f;
void dump(char *m)
{
unsigned int i;
uint i;
debug("%s ... order=%d, size=%d, entries=%d\n", m, f.hash_order, f.hash_size, f.hash_size);
for(i=0; i<f.hash_size; i++)

View File

@ -311,7 +311,7 @@ roa_commit(struct config *new, struct config *old)
if (old)
WALK_LIST(t, roa_table_list)
{
struct symbol *sym = cf_find_symbol(t->name);
struct symbol *sym = cf_find_symbol(new, t->name);
if (sym && sym->class == SYM_ROA)
{
/* Found old table in new config */

View File

@ -144,6 +144,38 @@ rte_do_cow(rte *r)
return e;
}
/**
* rte_cow_rta - get a private writable copy of &rte with writable &rta
* @r: a route entry to be copied
* @lp: a linpool from which to allocate &rta
*
* rte_cow_rta() takes a &rte and prepares it and associated &rta for
* modification. There are three possibilities: First, both &rte and &rta are
* private copies, in that case they are returned unchanged. Second, &rte is
* private copy, but &rta is cached, in that case &rta is duplicated using
* rta_do_cow(). Third, both &rte is shared and &rta is cached, in that case
* both structures are duplicated by rte_do_cow() and rta_do_cow().
*
* Note that in the second case, cached &rta loses one reference, while private
* copy created by rta_do_cow() is a shallow copy sharing indirect data (eattrs,
* nexthops, ...) with it. To work properly, original shared &rta should have
* another reference during the life of created private copy.
*
* Result: a pointer to the new writable &rte with writable &rta.
*/
rte *
rte_cow_rta(rte *r, linpool *lp)
{
if (!rta_is_cached(r->attrs))
return r;
rte *e = rte_cow(r);
rta *a = rta_do_cow(r->attrs, lp);
rta_free(e->attrs);
e->attrs = a;
return e;
}
static int /* Actually better or at least as good as */
rte_better(rte *new, rte *old)
{
@ -172,6 +204,26 @@ rte_better(rte *new, rte *old)
return 0;
}
static int
rte_mergable(rte *pri, rte *sec)
{
int (*mergable)(rte *, rte *);
if (!rte_is_valid(pri) || !rte_is_valid(sec))
return 0;
if (pri->pref != sec->pref)
return 0;
if (pri->attrs->src->proto->proto != sec->attrs->src->proto->proto)
return 0;
if (mergable = pri->attrs->src->proto->rte_mergable)
return mergable(pri, sec);
return 0;
}
static void
rte_trace(struct proto *p, rte *e, int dir, char *msg)
{
@ -182,14 +234,14 @@ rte_trace(struct proto *p, rte *e, int dir, char *msg)
}
static inline void
rte_trace_in(unsigned int flag, struct proto *p, rte *e, char *msg)
rte_trace_in(uint flag, struct proto *p, rte *e, char *msg)
{
if (p->debug & flag)
rte_trace(p, e, '>', msg);
}
static inline void
rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg)
rte_trace_out(uint flag, struct proto *p, rte *e, char *msg)
{
if (p->debug & flag)
rte_trace(p, e, '<', msg);
@ -208,12 +260,10 @@ export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa,
rt = rt0;
*rt_free = NULL;
/* If called does not care for eattrs, we prepare one internally */
if (!tmpa)
{
tmpb = make_tmp_attrs(rt, rte_update_pool);
tmpa = &tmpb;
}
tmpa = &tmpb;
*tmpa = make_tmp_attrs(rt, rte_update_pool);
v = p->import_control ? p->import_control(p, &rt, tmpa, rte_update_pool) : 0;
if (v < 0)
@ -347,13 +397,16 @@ do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tm
}
static void
rt_notify_basic(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, int refeed)
{
// struct proto *p = ah->proto;
struct proto *p = ah->proto;
struct proto_stats *stats = ah->stats;
rte *new = new0;
rte *old = old0;
rte *new_free = NULL;
rte *old_free = NULL;
ea_list *tmpa = NULL;
if (new)
stats->exp_updates_received++;
@ -369,7 +422,7 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list
* FIXME - this is broken because 'configure soft' may change
* filters but keep routes. Refeed is expected to be called after
* change of the filters and with old == new, therefore we do not
* even try to run the filter on an old route, This may lead to
* even try to run the filter on an old route, This may lead to
* 'spurious withdraws' but ensure that there are no 'missing
* withdraws'.
*
@ -386,9 +439,26 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list
if (old && !refeed)
old = export_filter(ah, old, &old_free, NULL, 1);
/* FIXME - This is broken because of incorrect 'old' value (see above) */
if (!new && !old)
{
/*
* As mentioned above, 'old' value may be incorrect in some race conditions.
* We generally ignore it with the exception of withdraw to pipe protocol.
* In that case we rather propagate unfiltered withdraws regardless of
* export filters to ensure that when a protocol is flushed, its routes are
* removed from all tables. Possible spurious unfiltered withdraws are not
* problem here as they are ignored if there is no corresponding route at
* the other end of the pipe. We directly call rt_notify() hook instead of
* do_rt_notify() to avoid logging and stat counters.
*/
#ifdef CONFIG_PIPE
if ((p->proto == &proto_pipe) && !new0 && (p != old0->sender->proto))
p->rt_notify(p, ah->table, net, NULL, old0, NULL);
#endif
return;
}
do_rt_notify(ah, net, new, old, tmpa, refeed);
@ -400,17 +470,17 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list
}
static void
rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed, rte *before_old,
ea_list *tmpa, int feed)
rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed, rte *before_old, int feed)
{
// struct proto *p = ah->proto;
struct proto_stats *stats = ah->stats;
rte *r;
rte *new_best = NULL;
rte *old_best = NULL;
rte *new_free = NULL;
rte *old_free = NULL;
rte *r;
ea_list *tmpa = NULL;
/* Used to track whether we met old_changed position. If before_old is NULL
old_changed was the first and we met it implicitly before current best route. */
@ -517,6 +587,129 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
rte_free(old_free);
}
static struct mpnh *
mpnh_merge_rta(struct mpnh *nhs, rta *a, int max)
{
struct mpnh nh = { .gw = a->gw, .iface = a->iface };
struct mpnh *nh2 = (a->dest == RTD_MULTIPATH) ? a->nexthops : &nh;
return mpnh_merge(nhs, nh2, 1, 0, max, rte_update_pool);
}
rte *
rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, ea_list **tmpa, int silent)
{
// struct proto *p = ah->proto;
struct mpnh *nhs = NULL;
rte *best0, *best, *rt0, *rt, *tmp;
best0 = net->routes;
*rt_free = NULL;
if (!rte_is_valid(best0))
return NULL;
best = export_filter(ah, best0, rt_free, tmpa, silent);
if (!best || !rte_is_reachable(best))
return best;
for (rt0 = best0->next; rt0; rt0 = rt0->next)
{
if (!rte_mergable(best0, rt0))
continue;
rt = export_filter(ah, rt0, &tmp, NULL, 1);
if (!rt)
continue;
if (rte_is_reachable(rt))
nhs = mpnh_merge_rta(nhs, rt->attrs, ah->proto->merge_limit);
if (tmp)
rte_free(tmp);
}
if (nhs)
{
nhs = mpnh_merge_rta(nhs, best->attrs, ah->proto->merge_limit);
if (nhs->next)
{
best = rte_cow_rta(best, rte_update_pool);
best->attrs->dest = RTD_MULTIPATH;
best->attrs->nexthops = nhs;
}
}
if (best != best0)
*rt_free = best;
return best;
}
static void
rt_notify_merged(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed,
rte *new_best, rte*old_best, int refeed)
{
// struct proto *p = ah->proto;
rte *new_best_free = NULL;
rte *old_best_free = NULL;
rte *new_changed_free = NULL;
rte *old_changed_free = NULL;
ea_list *tmpa = NULL;
/* We assume that all rte arguments are either NULL or rte_is_valid() */
/* This check should be done by the caller */
if (!new_best && !old_best)
return;
/* Check whether the change is relevant to the merged route */
if ((new_best == old_best) && !refeed)
{
new_changed = rte_mergable(new_best, new_changed) ?
export_filter(ah, new_changed, &new_changed_free, NULL, 1) : NULL;
old_changed = rte_mergable(old_best, old_changed) ?
export_filter(ah, old_changed, &old_changed_free, NULL, 1) : NULL;
if (!new_changed && !old_changed)
return;
}
if (new_best)
ah->stats->exp_updates_received++;
else
ah->stats->exp_withdraws_received++;
/* Prepare new merged route */
if (new_best)
new_best = rt_export_merged(ah, net, &new_best_free, &tmpa, 0);
/* Prepare old merged route (without proper merged next hops) */
/* There are some issues with running filter on old route - see rt_notify_basic() */
if (old_best && !refeed)
old_best = export_filter(ah, old_best, &old_best_free, NULL, 1);
if (new_best || old_best)
do_rt_notify(ah, net, new_best, old_best, tmpa, refeed);
/* Discard temporary rte's */
if (new_best_free)
rte_free(new_best_free);
if (old_best_free)
rte_free(old_best_free);
if (new_changed_free)
rte_free(new_changed_free);
if (old_changed_free)
rte_free(old_changed_free);
}
/**
* rte_announce - announce a routing table change
* @tab: table the route has been added to
@ -524,7 +717,6 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
* @net: network in question
* @new: the new route to be announced
* @old: the previous route for the same network
* @tmpa: a list of temporary attributes belonging to the new route
*
* This function gets a routing table update and announces it
* to all protocols that acccepts given type of route announcement
@ -547,13 +739,20 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
* the protocol gets called.
*/
static void
rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *before_old, ea_list *tmpa)
rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old,
rte *new_best, rte *old_best, rte *before_old)
{
if (!rte_is_valid(new))
new = NULL;
if (!rte_is_valid(old))
old = before_old = NULL;
if (!rte_is_valid(new))
new = NULL;
if (!rte_is_valid(new_best))
new_best = NULL;
if (!rte_is_valid(old_best))
old_best = NULL;
if (!old && !new)
return;
@ -575,9 +774,11 @@ rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *befo
ASSERT(a->proto->export_state != ES_DOWN);
if (a->proto->accept_ra_types == type)
if (type == RA_ACCEPTED)
rt_notify_accepted(a, net, new, old, before_old, tmpa, 0);
rt_notify_accepted(a, net, new, old, before_old, 0);
else if (type == RA_MERGED)
rt_notify_merged(a, net, new, old, new_best, old_best, 0);
else
rt_notify_basic(a, net, new, old, tmpa, 0);
rt_notify_basic(a, net, new, old, 0);
}
}
@ -640,7 +841,7 @@ rte_same(rte *x, rte *y)
static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); }
static void
rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, struct rte_src *src)
rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
{
struct proto *p = ah->proto;
struct rtable *table = ah->table;
@ -687,12 +888,6 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
}
rte_free_quick(new);
#ifdef CONFIG_RIP
/* lastmod is used internally by RIP as the last time
when the route was received. */
if (src->proto->proto == &proto_rip)
old->lastmod = now;
#endif
return;
}
*k = old->next;
@ -881,11 +1076,12 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
}
/* Propagate the route change */
rte_announce(table, RA_ANY, net, new, old, NULL, tmpa);
rte_announce(table, RA_ANY, net, new, old, NULL, NULL, NULL);
if (net->routes != old_best)
rte_announce(table, RA_OPTIMAL, net, net->routes, old_best, NULL, tmpa);
rte_announce(table, RA_OPTIMAL, net, net->routes, old_best, NULL, NULL, NULL);
if (table->config->sorted)
rte_announce(table, RA_ACCEPTED, net, new, old, before_old, tmpa);
rte_announce(table, RA_ACCEPTED, net, new, old, NULL, NULL, before_old);
rte_announce(table, RA_MERGED, net, new, old, net->routes, old_best, NULL);
if (!net->routes &&
(table->gc_counter++ >= table->config->gc_max_ops) &&
@ -1050,7 +1246,7 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
recalc:
rte_hide_dummy_routes(net, &dummy);
rte_recalculate(ah, net, new, tmpa, src);
rte_recalculate(ah, net, new, src);
rte_unhide_dummy_routes(net, &dummy);
rte_update_unlock();
return;
@ -1058,20 +1254,17 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
drop:
rte_free(new);
new = NULL;
tmpa = NULL;
goto recalc;
}
/* Independent call to rte_announce(), used from next hop
recalculation, outside of rte_update(). new must be non-NULL */
static inline void
rte_announce_i(rtable *tab, unsigned type, net *n, rte *new, rte *old)
rte_announce_i(rtable *tab, unsigned type, net *net, rte *new, rte *old,
rte *new_best, rte *old_best)
{
ea_list *tmpa;
rte_update_lock();
tmpa = make_tmp_attrs(new, rte_update_pool);
rte_announce(tab, type, n, new, old, NULL, tmpa);
rte_announce(tab, type, net, new, old, new_best, old_best, NULL);
rte_update_unlock();
}
@ -1079,7 +1272,7 @@ void
rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during garbage collection */
{
rte_update_lock();
rte_recalculate(old->sender, old->net, NULL, NULL, old->attrs->src);
rte_recalculate(old->sender, old->net, NULL, old->attrs->src);
rte_update_unlock();
}
@ -1365,9 +1558,8 @@ rt_init(void)
static int
rt_prune_step(rtable *tab, int step, int *limit)
rt_prune_step(rtable *tab, int *limit)
{
static struct tbf rl_flush = TBF_DEFAULT_LOG_LIMITS;
struct fib_iterator *fit = &tab->prune_fit;
DBG("Pruning route table %s\n", tab->name);
@ -1392,9 +1584,7 @@ again:
rescan:
for (e=n->routes; e; e=e->next)
if (e->sender->proto->flushing ||
(e->flags & REF_DISCARD) ||
(step && e->attrs->src->proto->flushing))
if (e->sender->proto->flushing || (e->flags & REF_DISCARD))
{
if (*limit <= 0)
{
@ -1402,10 +1592,6 @@ again:
return 0;
}
if (step)
log_rl(&rl_flush, L_WARN "Route %I/%d from %s still in %s after flush",
n->n.prefix, n->n.pxlen, e->attrs->src->proto->name, tab->name);
rte_discard(tab, e);
(*limit)--;
@ -1445,7 +1631,7 @@ static inline int
rt_prune_table(rtable *tab)
{
int limit = 512;
return rt_prune_step(tab, 0, &limit);
return rt_prune_step(tab, &limit);
}
/**
@ -1454,44 +1640,24 @@ rt_prune_table(rtable *tab)
* The prune loop scans routing tables and removes routes belonging to flushing
* protocols, discarded routes and also stale network entries. Returns 1 when
* all such routes are pruned. It is a part of the protocol flushing loop.
*
* The prune loop runs in two steps. In the first step it prunes just the routes
* with flushing senders (in explicitly marked tables) so the route removal is
* propagated as usual. In the second step, all remaining relevant routes are
* removed. Ideally, there shouldn't be any, but it happens when pipe filters
* are changed.
*/
int
rt_prune_loop(void)
{
static int step = 0;
int limit = 512;
rtable *t;
again:
WALK_LIST(t, routing_tables)
if (! rt_prune_step(t, step, &limit))
if (! rt_prune_step(t, &limit))
return 0;
if (step == 0)
{
/* Prepare for the second step */
WALK_LIST(t, routing_tables)
t->prune_state = RPS_SCHEDULED;
step = 1;
goto again;
}
/* Done */
step = 0;
return 1;
}
void
rt_preconfig(struct config *c)
{
struct symbol *s = cf_find_symbol("master");
struct symbol *s = cf_get_symbol("master");
init_list(&c->tables);
c->master_rtc = rt_new_table(s);
@ -1562,7 +1728,7 @@ rt_next_hop_update_net(rtable *tab, net *n)
new = rt_next_hop_update_rte(tab, e);
*k = new;
rte_announce_i(tab, RA_ANY, n, new, e);
rte_announce_i(tab, RA_ANY, n, new, e, NULL, NULL);
rte_trace_in(D_ROUTES, new->sender->proto, new, "updated");
/* Call a pre-comparison hook */
@ -1602,10 +1768,13 @@ rt_next_hop_update_net(rtable *tab, net *n)
/* Announce the new best route */
if (new != old_best)
{
rte_announce_i(tab, RA_OPTIMAL, n, new, old_best);
rte_announce_i(tab, RA_OPTIMAL, n, new, old_best, NULL, NULL);
rte_trace_in(D_ROUTES, new->sender->proto, new, "updated [best]");
}
/* FIXME: Better announcement of merged routes */
rte_announce_i(tab, RA_MERGED, n, new, old_best, new, old_best);
if (free_old_best)
rte_free_quick(old_best);
@ -1693,6 +1862,7 @@ rt_unlock_table(rtable *r)
{
struct config *conf = r->deleted;
DBG("Deleting routing table %s\n", r->name);
r->config->table = NULL;
if (r->hostcache)
rt_free_hostcache(r);
rem_node(&r->n);
@ -1728,7 +1898,7 @@ rt_commit(struct config *new, struct config *old)
rtable *ot = o->table;
if (!ot->deleted)
{
struct symbol *sym = cf_find_symbol(o->name);
struct symbol *sym = cf_find_symbol(new, o->name);
if (sym && sym->class == SYM_TABLE && !new->shutdown)
{
DBG("\t%s: same\n", o->name);
@ -1766,14 +1936,13 @@ rt_commit(struct config *new, struct config *old)
static inline void
do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e)
{
ea_list *tmpa;
rte_update_lock();
tmpa = make_tmp_attrs(e, rte_update_pool);
if (type == RA_ACCEPTED)
rt_notify_accepted(h, n, e, NULL, NULL, tmpa, p->refeeding ? 2 : 1);
rt_notify_accepted(h, n, e, NULL, NULL, p->refeeding ? 2 : 1);
else if (type == RA_MERGED)
rt_notify_merged(h, n, NULL, NULL, e, p->refeeding ? e : NULL, p->refeeding);
else
rt_notify_basic(h, n, e, p->refeeding ? e : NULL, tmpa, p->refeeding);
rt_notify_basic(h, n, e, p->refeeding ? e : NULL, p->refeeding);
rte_update_unlock();
}
@ -1819,20 +1988,26 @@ again:
/* XXXX perhaps we should change feed for RA_ACCEPTED to not use 'new' */
if ((p->accept_ra_types == RA_OPTIMAL) ||
(p->accept_ra_types == RA_ACCEPTED))
(p->accept_ra_types == RA_ACCEPTED) ||
(p->accept_ra_types == RA_MERGED))
if (rte_is_valid(e))
{
if (p->export_state != ES_FEEDING)
return 1; /* In the meantime, the protocol fell down. */
do_feed_baby(p, p->accept_ra_types, h, n, e);
max_feed--;
}
if (p->accept_ra_types == RA_ANY)
for(e = n->routes; rte_is_valid(e); e = e->next)
for(e = n->routes; e; e = e->next)
{
if (p->export_state != ES_FEEDING)
return 1; /* In the meantime, the protocol fell down. */
if (!rte_is_valid(e))
continue;
do_feed_baby(p, RA_ANY, h, n, e);
max_feed--;
}
@ -1888,7 +2063,7 @@ hc_hash(ip_addr a, rtable *dep)
static inline void
hc_insert(struct hostcache *hc, struct hostentry *he)
{
unsigned int k = he->hash_key >> hc->hash_shift;
uint k = he->hash_key >> hc->hash_shift;
he->next = hc->hash_table[k];
hc->hash_table[k] = he;
}
@ -1897,7 +2072,7 @@ static inline void
hc_remove(struct hostcache *hc, struct hostentry *he)
{
struct hostentry **hep;
unsigned int k = he->hash_key >> hc->hash_shift;
uint k = he->hash_key >> hc->hash_shift;
for (hep = &hc->hash_table[k]; *hep != he; hep = &(*hep)->next);
*hep = he->next;
@ -2162,7 +2337,7 @@ rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
if (!tab->hostcache)
rt_init_hostcache(tab);
unsigned int k = hc_hash(a, dep);
uint k = hc_hash(a, dep);
struct hostcache *hc = tab->hostcache;
for (he = hc->hash_table[k >> hc->hash_shift]; he != NULL; he = he->next)
if (ipa_equal(he->addr, a) && (he->tab == dep))
@ -2279,12 +2454,22 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
rte_update_lock(); /* We use the update buffer for filtering */
tmpa = make_tmp_attrs(e, rte_update_pool);
if (d->export_mode)
/* Special case for merged export */
if ((d->export_mode == RSEM_EXPORT) && (d->export_protocol->accept_ra_types == RA_MERGED))
{
rte *rt_free;
e = rt_export_merged(a, n, &rt_free, &tmpa, 1);
pass = 1;
if (!e)
{ e = ee; goto skip; }
}
else if (d->export_mode)
{
struct proto *ep = d->export_protocol;
int ic = ep->import_control ? ep->import_control(ep, &e, &tmpa, rte_update_pool) : 0;
if (ep->accept_ra_types == RA_OPTIMAL)
if (ep->accept_ra_types == RA_OPTIMAL || ep->accept_ra_types == RA_MERGED)
pass = 1;
if (ic < 0)

View File

@ -1,4 +1,5 @@
H Protocols
C babel
C bfd
C bgp
C ospf

2
proto/babel/Doc Normal file
View File

@ -0,0 +1,2 @@
S babel.c
S packet.c

5
proto/babel/Makefile Normal file
View File

@ -0,0 +1,5 @@
source=babel.c packets.c
root-rel=../../
dir-name=proto/babel
include ../../Rules

2055
proto/babel/babel.c Normal file

File diff suppressed because it is too large Load Diff

335
proto/babel/babel.h Normal file
View File

@ -0,0 +1,335 @@
/*
* BIRD -- The Babel protocol
*
* Copyright (c) 2015--2016 Toke Hoiland-Jorgensen
*
* Can be freely distributed and used under the terms of the GNU GPL.
*
* This file contains the data structures used by Babel.
*/
#ifndef _BIRD_BABEL_H_
#define _BIRD_BABEL_H_
#include "nest/bird.h"
#include "nest/cli.h"
#include "nest/iface.h"
#include "nest/route.h"
#include "nest/protocol.h"
#include "nest/locks.h"
#include "lib/resource.h"
#include "lib/lists.h"
#include "lib/socket.h"
#include "lib/string.h"
#include "lib/timer.h"
#ifndef IPV6
#error "The Babel protocol only speaks IPv6"
#endif
#define EA_BABEL_METRIC EA_CODE(EAP_BABEL, 0)
#define EA_BABEL_ROUTER_ID EA_CODE(EAP_BABEL, 1)
#define BABEL_MAGIC 42
#define BABEL_VERSION 2
#define BABEL_PORT 6696
#define BABEL_INFINITY 0xFFFF
#define BABEL_HELLO_INTERVAL_WIRED 4 /* Default hello intervals in seconds */
#define BABEL_HELLO_INTERVAL_WIRELESS 4
#define BABEL_UPDATE_INTERVAL_FACTOR 4
#define BABEL_IHU_INTERVAL_FACTOR 3
#define BABEL_IHU_EXPIRY_FACTOR(X) ((X)*3/2) /* 1.5 */
#define BABEL_HELLO_EXPIRY_FACTOR(X) ((X)*3/2) /* 1.5 */
#define BABEL_ROUTE_EXPIRY_FACTOR(X) ((X)*7/2) /* 3.5 */
#define BABEL_ROUTE_REFRESH_INTERVAL 2 /* Seconds before route expiry to send route request */
#define BABEL_HOLD_TIME 10 /* Expiry time for our own routes */
#define BABEL_RXCOST_WIRED 96
#define BABEL_RXCOST_WIRELESS 256
#define BABEL_INITIAL_HOP_COUNT 255
#define BABEL_MAX_SEND_INTERVAL 5
#define BABEL_TIME_UNITS 100 /* On-wire times are counted in centiseconds */
#define BABEL_SEQNO_REQUEST_EXPIRY 60
#define BABEL_GARBAGE_INTERVAL 300
#define BABEL_OVERHEAD (SIZE_OF_IP_HEADER+UDP_HEADER_LENGTH)
#define BABEL_MIN_MTU (512 + BABEL_OVERHEAD)
enum babel_tlv_type {
BABEL_TLV_PAD1 = 0,
BABEL_TLV_PADN = 1,
BABEL_TLV_ACK_REQ = 2,
BABEL_TLV_ACK = 3,
BABEL_TLV_HELLO = 4,
BABEL_TLV_IHU = 5,
BABEL_TLV_ROUTER_ID = 6,
BABEL_TLV_NEXT_HOP = 7,
BABEL_TLV_UPDATE = 8,
BABEL_TLV_ROUTE_REQUEST = 9,
BABEL_TLV_SEQNO_REQUEST = 10,
/* extensions - not implemented
BABEL_TLV_TS_PC = 11,
BABEL_TLV_HMAC = 12,
BABEL_TLV_SS_UPDATE = 13,
BABEL_TLV_SS_REQUEST = 14,
BABEL_TLV_SS_SEQNO_REQUEST = 15,
*/
BABEL_TLV_MAX
};
enum babel_iface_type {
/* In practice, UNDEF and WIRED give equivalent behaviour */
BABEL_IFACE_TYPE_UNDEF = 0,
BABEL_IFACE_TYPE_WIRED = 1,
BABEL_IFACE_TYPE_WIRELESS = 2,
BABEL_IFACE_TYPE_MAX
};
enum babel_ae_type {
BABEL_AE_WILDCARD = 0,
BABEL_AE_IP4 = 1,
BABEL_AE_IP6 = 2,
BABEL_AE_IP6_LL = 3,
BABEL_AE_MAX
};
struct babel_config {
struct proto_config c;
list iface_list; /* Patterns configured -- keep it first; see babel_reconfigure why */
};
struct babel_iface_config {
struct iface_patt i;
u16 rxcost;
u8 type;
u8 check_link;
int port;
u16 hello_interval;
u16 ihu_interval;
u16 update_interval;
u16 rx_buffer; /* RX buffer size, 0 for MTU */
u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */
int tx_tos;
int tx_priority;
};
struct babel_proto {
struct proto p;
timer *timer;
struct fib rtable;
list interfaces; /* Interfaces we really know about (struct babel_iface) */
u64 router_id;
u16 update_seqno; /* To be increased on request */
u8 triggered; /* For triggering global updates */
slab *route_slab;
slab *source_slab;
slab *msg_slab;
slab *seqno_slab;
list seqno_cache; /* Seqno requests in the cache (struct babel_seqno_request) */
struct tbf log_pkt_tbf; /* TBF for packet messages */
};
struct babel_iface {
node n;
struct babel_proto *proto;
struct iface *iface;
struct babel_iface_config *cf;
u8 up;
pool *pool;
char *ifname;
sock *sk;
ip_addr addr;
int tx_length;
list neigh_list; /* List of neighbors seen on this iface (struct babel_neighbor) */
list msg_queue;
u16 hello_seqno; /* To be increased on each hello */
bird_clock_t next_hello;
bird_clock_t next_regular;
bird_clock_t next_triggered;
bird_clock_t want_triggered;
timer *timer;
event *send_event;
};
struct babel_neighbor {
node n;
struct babel_iface *ifa;
ip_addr addr;
u16 txcost;
u8 hello_cnt;
u16 hello_map;
u16 next_hello_seqno;
/* expiry timers */
bird_clock_t hello_expiry;
bird_clock_t ihu_expiry;
list routes; /* Routes this neighbour has sent us (struct babel_route) */
};
struct babel_source {
node n;
u64 router_id;
u16 seqno;
u16 metric;
bird_clock_t expires;
};
struct babel_route {
node n;
node neigh_route;
struct babel_entry *e;
struct babel_neighbor *neigh;
u16 seqno;
u16 advert_metric;
u16 metric;
u64 router_id;
ip_addr next_hop;
bird_clock_t refresh_time;
bird_clock_t expires;
u16 expiry_interval;
};
struct babel_entry {
struct fib_node n;
struct babel_proto *proto;
struct babel_route *selected_in;
struct babel_route *selected_out;
bird_clock_t updated;
list sources; /* Source entries for this prefix (struct babel_source). */
list routes; /* Routes for this prefix (struct babel_route) */
};
/* Stores forwarded seqno requests for duplicate suppression. */
struct babel_seqno_request {
node n;
ip_addr prefix;
u8 plen;
u64 router_id;
u16 seqno;
bird_clock_t updated;
};
/*
* Internal TLV messages
*/
struct babel_msg_ack_req {
u8 type;
u16 nonce;
u16 interval;
ip_addr sender;
};
struct babel_msg_ack {
u8 type;
u16 nonce;
};
struct babel_msg_hello {
u8 type;
u16 seqno;
u16 interval;
ip_addr sender;
};
struct babel_msg_ihu {
u8 type;
u8 ae;
u16 rxcost;
u16 interval;
ip_addr addr;
ip_addr sender;
};
struct babel_msg_update {
u8 type;
u8 ae;
u8 plen;
u16 interval;
u16 seqno;
u16 metric;
ip_addr prefix;
u64 router_id;
ip_addr next_hop;
ip_addr sender;
};
struct babel_msg_route_request {
u8 type;
u8 full;
u8 plen;
ip_addr prefix;
};
struct babel_msg_seqno_request {
u8 type;
u8 plen;
u16 seqno;
u8 hop_count;
u64 router_id;
ip_addr prefix;
ip_addr sender;
};
union babel_msg {
u8 type;
struct babel_msg_ack_req ack_req;
struct babel_msg_ack ack;
struct babel_msg_hello hello;
struct babel_msg_ihu ihu;
struct babel_msg_update update;
struct babel_msg_route_request route_request;
struct babel_msg_seqno_request seqno_request;
};
struct babel_msg_node {
node n;
union babel_msg msg;
};
/* babel.c */
void babel_handle_ack_req(union babel_msg *msg, struct babel_iface *ifa);
void babel_handle_ack(union babel_msg *msg, struct babel_iface *ifa);
void babel_handle_hello(union babel_msg *msg, struct babel_iface *ifa);
void babel_handle_ihu(union babel_msg *msg, struct babel_iface *ifa);
void babel_handle_router_id(union babel_msg *msg, struct babel_iface *ifa);
void babel_handle_update(union babel_msg *msg, struct babel_iface *ifa);
void babel_handle_route_request(union babel_msg *msg, struct babel_iface *ifa);
void babel_handle_seqno_request(union babel_msg *msg, struct babel_iface *ifa);
void babel_show_interfaces(struct proto *P, char *iff);
void babel_show_neighbors(struct proto *P, char *iff);
void babel_show_entries(struct proto *P);
/* packets.c */
void babel_enqueue(union babel_msg *msg, struct babel_iface *ifa);
void babel_send_unicast(union babel_msg *msg, struct babel_iface *ifa, ip_addr dest);
int babel_open_socket(struct babel_iface *ifa);
void babel_send_queue(void *arg);
#endif

129
proto/babel/config.Y Normal file
View File

@ -0,0 +1,129 @@
/*
* BIRD -- Babel Configuration
*
* Copyright (c) 2015-2016 Toke Hoiland-Jorgensen
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
CF_HDR
#include "proto/babel/babel.h"
#include "nest/iface.h"
CF_DEFINES
#define BABEL_CFG ((struct babel_config *) this_proto)
#define BABEL_IFACE ((struct babel_iface_config *) this_ipatt)
CF_DECLS
CF_KEYWORDS(BABEL, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT, WIRED,
WIRELESS, RX, TX, BUFFER, LENGTH, CHECK, LINK, BABEL_METRIC)
CF_GRAMMAR
CF_ADDTO(proto, babel_proto)
babel_proto_start: proto_start BABEL
{
this_proto = proto_config_new(&proto_babel, $1);
init_list(&BABEL_CFG->iface_list);
};
babel_proto_item:
proto_item
| INTERFACE babel_iface
;
babel_proto_opts:
/* empty */
| babel_proto_opts babel_proto_item ';'
;
babel_proto:
babel_proto_start proto_name '{' babel_proto_opts '}';
babel_iface_start:
{
this_ipatt = cfg_allocz(sizeof(struct babel_iface_config));
add_tail(&BABEL_CFG->iface_list, NODE this_ipatt);
init_list(&this_ipatt->ipn_list);
BABEL_IFACE->port = BABEL_PORT;
BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED;
BABEL_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL;
BABEL_IFACE->tx_priority = sk_priority_control;
BABEL_IFACE->check_link = 1;
};
babel_iface_finish:
{
if (BABEL_IFACE->type == BABEL_IFACE_TYPE_WIRELESS)
{
if (!BABEL_IFACE->hello_interval)
BABEL_IFACE->hello_interval = BABEL_HELLO_INTERVAL_WIRELESS;
if (!BABEL_IFACE->rxcost)
BABEL_IFACE->rxcost = BABEL_RXCOST_WIRELESS;
}
else
{
if (!BABEL_IFACE->hello_interval)
BABEL_IFACE->hello_interval = BABEL_HELLO_INTERVAL_WIRED;
if (!BABEL_IFACE->rxcost)
BABEL_IFACE->rxcost = BABEL_RXCOST_WIRED;
}
if (!BABEL_IFACE->update_interval)
BABEL_IFACE->update_interval = BABEL_IFACE->hello_interval*BABEL_UPDATE_INTERVAL_FACTOR;
BABEL_IFACE->ihu_interval = BABEL_IFACE->hello_interval*BABEL_IHU_INTERVAL_FACTOR;
};
babel_iface_item:
| PORT expr { BABEL_IFACE->port = $2; if (($2<1) || ($2>65535)) cf_error("Invalid port number"); }
| RXCOST expr { BABEL_IFACE->rxcost = $2; if (($2<1) || ($2>65535)) cf_error("Invalid rxcost"); }
| HELLO INTERVAL expr { BABEL_IFACE->hello_interval = $3; if (($3<1) || ($3>65535)) cf_error("Invalid hello interval"); }
| UPDATE INTERVAL expr { BABEL_IFACE->update_interval = $3; if (($3<1) || ($3>65535)) cf_error("Invalid hello interval"); }
| TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; }
| TYPE WIRELESS { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRELESS; }
| RX BUFFER expr { BABEL_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX buffer must be in range 256-65535"); }
| TX LENGTH expr { BABEL_IFACE->tx_length = $3; if (($3<256) || ($3>65535)) cf_error("TX length must be in range 256-65535"); }
| TX tos { BABEL_IFACE->tx_tos = $2; }
| TX PRIORITY expr { BABEL_IFACE->tx_priority = $3; }
| CHECK LINK bool { BABEL_IFACE->check_link = $3; }
;
babel_iface_opts:
/* empty */
| babel_iface_opts babel_iface_item ';'
;
babel_iface_opt_list:
/* empty */
| '{' babel_iface_opts '}'
;
babel_iface:
babel_iface_start iface_patt_list_nopx babel_iface_opt_list babel_iface_finish;
CF_ADDTO(dynamic_attr, BABEL_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_BABEL_METRIC); })
CF_CLI_HELP(SHOW BABEL, ..., [[Show information about Babel protocol]]);
CF_CLI(SHOW BABEL INTERFACES, optsym opttext, [<name>] [\"<interface>\"], [[Show information about Babel interfaces]])
{ babel_show_interfaces(proto_get_named($4, &proto_babel), $5); };
CF_CLI(SHOW BABEL NEIGHBORS, optsym opttext, [<name>] [\"<interface>\"], [[Show information about Babel neighbors]])
{ babel_show_neighbors(proto_get_named($4, &proto_babel), $5); };
CF_CLI(SHOW BABEL ENTRIES, optsym opttext, [<name>], [[Show information about Babel prefix entries]])
{ babel_show_entries(proto_get_named($4, &proto_babel)); };
CF_CODE
CF_END

1093
proto/babel/packets.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -43,7 +43,7 @@
* the needs of BFD sessions. When a new session is created, it requests a
* proper BFD interface by function bfd_get_iface(), which either finds an
* existing one in &iface_list (from &bfd_proto) or allocates a new one. When a
* session is removed, an associated iface is dicharged by bfd_free_iface().
* session is removed, an associated iface is discharged by bfd_free_iface().
*
* BFD requests are the external API for the other protocols. When a protocol
* wants a BFD session, it calls bfd_request_session(), which creates a
@ -62,7 +62,7 @@
* configuration (like static routes in the static protocol). BFD neighbors are
* handled by BFD protocol like it is a BFD client -- when a BFD neighbor is
* ready, the protocol just creates a BFD request like any other protocol.
*
*
* The protocol uses a new generic event loop (structure &birdloop) from |io.c|,
* which supports sockets, timers and events like the main loop. Timers
* (structure &timer2) are new microsecond based timers, while sockets and
@ -129,11 +129,11 @@ static inline void bfd_notify_kick(struct bfd_proto *p);
* BFD sessions
*/
static void
static void
bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
{
struct bfd_proto *p = s->ifa->bfd;
uint old_state = s->loc_state;
uint old_state = s->loc_state;
int notify;
if (state == old_state)
@ -201,8 +201,8 @@ bfd_session_control_tx_timer(struct bfd_session *s, int reset)
if (s->passive && (s->rem_id == 0))
goto stop;
if (s->rem_demand_mode &&
!s->poll_active &&
if (s->rem_demand_mode &&
!s->poll_active &&
(s->loc_state == BFD_STATE_UP) &&
(s->rem_state == BFD_STATE_UP))
goto stop;
@ -303,7 +303,7 @@ bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old
bfd_send_ctl(s->ifa->bfd, s, 1);
}
static void
static void
bfd_session_timeout(struct bfd_session *s)
{
struct bfd_proto *p = s->ifa->bfd;
@ -353,7 +353,7 @@ bfd_session_set_min_rx(struct bfd_session *s, u32 val)
if (val == s->req_min_rx_new)
return;
s->req_min_rx_new = val;
s->req_min_rx_new = val;
/* Postpone timer update if req_min_rx_int decreases and the session is up */
if ((s->loc_state != BFD_STATE_UP) || (val > s->req_min_rx_int))
@ -575,9 +575,13 @@ bfd_free_iface(struct bfd_iface *ifa)
if (!ifa || --ifa->uc)
return;
if (ifa->sk)
{
sk_stop(ifa->sk);
rfree(ifa->sk);
}
rem_node(&ifa->n);
sk_stop(ifa->sk);
rfree(ifa->sk);
mb_free(ifa);
}
@ -868,12 +872,12 @@ bfd_notify_hook(sock *sk, int len)
WALK_LIST_FIRST(s, tmp_list)
{
bfd_lock_sessions(p);
rem2_node(&s->n);
rem_node(&s->n);
state = s->loc_state;
diag = s->loc_diag;
bfd_unlock_sessions(p);
/* FIXME: convert to btime and move to bfd_session_update_state() */
/* FIXME: convert to btime and move to bfd_session_update_state() */
s->last_state_change = now;
s->notify_running = 1;
@ -1092,7 +1096,7 @@ bfd_show_sessions(struct proto *P)
/* FIXME: this is thread-unsafe, but perhaps harmless */
state = s->loc_state;
diag = s->loc_diag;
ifname = (s->ifa && s->ifa->sk->iface) ? s->ifa->sk->iface->name : "---";
ifname = (s->ifa && s->ifa->iface) ? s->ifa->iface->name : "---";
tx_int = s->last_tx ? (MAX(s->des_min_tx_int, s->rem_min_rx_int) TO_MS) : 0;
timeout = (MAX(s->req_min_rx_int, s->rem_min_tx_int) TO_MS) * s->rem_detect_mult;

View File

@ -576,7 +576,7 @@ sockets_close_fds(struct birdloop *loop)
loop->close_scheduled = 0;
}
int sk_read(sock *s);
int sk_read(sock *s, int revents);
int sk_write(sock *s);
static void
@ -605,7 +605,7 @@ sockets_fire(struct birdloop *loop)
if (pfd->revents & POLLIN)
while (e && *psk && (*psk)->rx_hook)
e = sk_read(*psk);
e = sk_read(*psk, 0);
e = 1;
if (pfd->revents & POLLOUT)

View File

@ -63,9 +63,13 @@ void
bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final)
{
sock *sk = s->ifa->sk;
struct bfd_ctl_packet *pkt = (struct bfd_ctl_packet *) sk->tbuf;
struct bfd_ctl_packet *pkt;
char fb[8];
if (!sk)
return;
pkt = (struct bfd_ctl_packet *) sk->tbuf;
pkt->vdiag = bfd_pack_vdiag(1, s->loc_diag);
pkt->flags = bfd_pack_flags(s->loc_state, 0);
pkt->detect_mult = s->detect_mult;
@ -139,7 +143,7 @@ bfd_rx_hook(sock *sk, int len)
u8 ps = bfd_pkt_get_state(pkt);
if (ps > BFD_STATE_DOWN)
DROP("invalid init state", ps);
s = bfd_find_session_by_addr(p, sk->faddr);
/* FIXME: better session matching and message */

View File

@ -127,7 +127,7 @@ path_segment_contains(byte *p, int bs, u32 asn)
/* Validates path attribute, removes AS_CONFED_* segments, and also returns path length */
static int
validate_path(struct bgp_proto *p, int as_path, int bs, byte *idata, unsigned int *ilength)
validate_path(struct bgp_proto *p, int as_path, int bs, byte *idata, uint *ilength)
{
int res = 0;
u8 *a, *dst;
@ -825,7 +825,7 @@ bgp_attach_attr_wa(ea_list **to, struct linpool *pool, unsigned attr, unsigned l
}
static int
bgp_encode_attr_hdr(byte *dst, unsigned int flags, unsigned code, int len)
bgp_encode_attr_hdr(byte *dst, uint flags, unsigned code, int len)
{
int wlen;
@ -1203,10 +1203,10 @@ encode_bgpsec_attr(struct bgp_conn *conn,
*
* Result: Length of the attribute block generated or -1 if not enough space.
*/
unsigned int
uint
bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
{
unsigned int i, code, type, flags;
uint i, code, type, flags;
byte *start = w;
int len, rv;
@ -2082,6 +2082,82 @@ bgp_rte_better(rte *new, rte *old)
}
int
bgp_rte_mergable(rte *pri, rte *sec)
{
struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->attrs->src->proto;
struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->attrs->src->proto;
eattr *x, *y;
u32 p, s;
/* Skip suppressed routes (see bgp_rte_recalculate()) */
if (pri->u.bgp.suppressed != sec->u.bgp.suppressed)
return 0;
/* RFC 4271 9.1.2.1. Route resolvability test */
if (!rte_resolvable(sec))
return 0;
/* Start with local preferences */
x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF));
y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF));
p = x ? x->u.data : pri_bgp->cf->default_local_pref;
s = y ? y->u.data : sec_bgp->cf->default_local_pref;
if (p != s)
return 0;
/* RFC 4271 9.1.2.2. a) Use AS path lengths */
if (pri_bgp->cf->compare_path_lengths || sec_bgp->cf->compare_path_lengths)
{
x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
p = x ? as_path_getlen(x->u.ptr) : AS_PATH_MAXLEN;
s = y ? as_path_getlen(y->u.ptr) : AS_PATH_MAXLEN;
if (p != s)
return 0;
// if (DELTA(p, s) > pri_bgp->cf->relax_multipath)
// return 0;
}
/* RFC 4271 9.1.2.2. b) Use origins */
x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN));
y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN));
p = x ? x->u.data : ORIGIN_INCOMPLETE;
s = y ? y->u.data : ORIGIN_INCOMPLETE;
if (p != s)
return 0;
/* RFC 4271 9.1.2.2. c) Compare MED's */
if (pri_bgp->cf->med_metric || sec_bgp->cf->med_metric ||
(bgp_get_neighbor(pri) == bgp_get_neighbor(sec)))
{
x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
p = x ? x->u.data : pri_bgp->cf->default_med;
s = y ? y->u.data : sec_bgp->cf->default_med;
if (p != s)
return 0;
}
/* RFC 4271 9.1.2.2. d) Prefer external peers */
if (pri_bgp->is_internal != sec_bgp->is_internal)
return 0;
/* RFC 4271 9.1.2.2. e) Compare IGP metrics */
p = pri_bgp->cf->igp_metric ? pri->attrs->igp_metric : 0;
s = sec_bgp->cf->igp_metric ? sec->attrs->igp_metric : 0;
if (p != s)
return 0;
/* Remaining criteria are ignored */
return 1;
}
static inline int
same_group(rte *r, u32 lpref, u32 lasn)
{
@ -2363,19 +2439,18 @@ bgp_remove_as4_attrs(struct bgp_proto *p, rta *a)
* by a &rta.
*/
struct rta *
bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len,
struct linpool *pool, byte *nlri, int nlri_len)
bgp_decode_attrs(struct bgp_conn *conn, byte *attr, uint len, struct linpool *pool, int mandatory,
byte *nlri, int nlri_len)
{
struct bgp_proto *bgp = conn->bgp;
rta *a = lp_alloc(pool, sizeof(struct rta));
unsigned int flags, code, l, i, type;
uint flags, code, l, i, type;
int errcode;
byte *z=0, *attr_start=0;
byte seen[256/8];
ea_list *ea;
struct adata *ad;
int withdraw = 0;
int mandatory = nlri_len;
#ifdef CONFIG_BGPSEC
unsigned int bgpsec_len = 0;
byte *bgpsec_start = 0;
@ -2617,7 +2692,7 @@ err:
int
bgp_get_attr(eattr *a, byte *buf, int buflen)
{
unsigned int i = EA_ID(a->id);
uint i = EA_ID(a->id);
struct attr_desc *d;
int len;

View File

@ -133,7 +133,8 @@ bgp_open(struct bgp_proto *p)
bgp_counter++;
if (p->cf->password)
if (sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, p->cf->password) < 0)
if (sk_set_md5_auth(bgp_listen_sk, p->cf->source_addr, p->cf->remote_ip,
p->cf->iface, p->cf->password, p->cf->setkey) < 0)
{
sk_log_error(bgp_listen_sk, p->p.name);
bgp_close(p, 0);
@ -203,7 +204,8 @@ bgp_close(struct bgp_proto *p, int apply_md5)
bgp_counter--;
if (p->cf->password && apply_md5)
if (sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, NULL) < 0)
if (sk_set_md5_auth(bgp_listen_sk, p->cf->source_addr, p->cf->remote_ip,
p->cf->iface, NULL, p->cf->setkey) < 0)
sk_log_error(bgp_listen_sk, p->p.name);
if (!bgp_counter)
@ -386,6 +388,8 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
if (ipa_zero(p->source_addr))
p->source_addr = conn->sk->saddr;
conn->sk->fast_rx = 0;
p->conn = conn;
p->last_error_class = 0;
p->last_error_code = 0;
@ -581,6 +585,7 @@ bgp_send_open(struct bgp_conn *conn)
conn->peer_gr_time = 0;
conn->peer_gr_flags = 0;
conn->peer_gr_aflags = 0;
conn->peer_ext_messages_support = 0;
DBG("BGP: Sending open\n");
conn->sk->rx_hook = bgp_rx;
@ -677,6 +682,10 @@ bgp_keepalive_timeout(timer *t)
DBG("BGP: Keepalive timer\n");
bgp_schedule_packet(conn, PKT_KEEPALIVE);
/* Kick TX a bit faster */
if (ev_active(conn->tx_ev))
ev_run(conn->tx_ev);
}
static void
@ -707,6 +716,7 @@ bgp_setup_sk(struct bgp_conn *conn, sock *s)
{
s->data = conn;
s->err_hook = bgp_sock_err;
s->fast_rx = 1;
conn->sk = s;
}
@ -745,8 +755,8 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c
s->dport = p->cf->remote_port;
s->iface = p->neigh ? p->neigh->iface : NULL;
s->ttl = p->cf->ttl_security ? 255 : hops;
s->rbsize = BGP_RX_BUFFER_SIZE;
s->tbsize = BGP_TX_BUFFER_SIZE;
s->rbsize = p->cf->enable_extended_messages ? BGP_RX_BUFFER_EXT_SIZE : BGP_RX_BUFFER_SIZE;
s->tbsize = p->cf->enable_extended_messages ? BGP_TX_BUFFER_EXT_SIZE : BGP_TX_BUFFER_SIZE;
s->tos = IP_PREC_INTERNET_CONTROL;
s->password = p->cf->password;
s->tx_hook = bgp_connected;
@ -824,7 +834,13 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED)
return 0;
}
/* We are in proper state and there is no other incoming connection */
/*
* BIRD should keep multiple incoming connections in OpenSent state (for
* details RFC 4271 8.2.1 par 3), but it keeps just one. Duplicate incoming
* connections are rejected istead. The exception is the case where an
* incoming connection triggers a graceful restart.
*/
acc = (p->p.proto_state == PS_START || p->p.proto_state == PS_UP) &&
(p->start_state >= BSS_CONNECT) && (!p->incoming_conn.sk);
@ -834,6 +850,10 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED)
bgp_handle_graceful_restart(p);
bgp_conn_enter_idle_state(p->conn);
acc = 1;
/* There might be separate incoming connection in OpenSent state */
if (p->incoming_conn.state > BS_ACTIVE)
bgp_close_conn(&p->incoming_conn);
}
BGP_TRACE(D_EVENTS, "Incoming connection from %I%J (port %d) %s",
@ -855,6 +875,13 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED)
if (sk_set_min_ttl(sk, 256 - hops) < 0)
goto err;
if (p->cf->enable_extended_messages)
{
sk->rbsize = BGP_RX_BUFFER_EXT_SIZE;
sk->tbsize = BGP_TX_BUFFER_EXT_SIZE;
sk_reallocate(sk);
}
bgp_setup_conn(p, &p->incoming_conn);
bgp_setup_sk(&p->incoming_conn, sk);
bgp_send_open(&p->incoming_conn);
@ -1255,6 +1282,7 @@ bgp_init(struct proto_config *C)
P->feed_begin = bgp_feed_begin;
P->feed_end = bgp_feed_end;
P->rte_better = bgp_rte_better;
P->rte_mergable = bgp_rte_mergable;
P->rte_recalculate = c->deterministic_med ? bgp_rte_recalculate : NULL;
p->cf = c;
@ -1582,21 +1610,23 @@ bgp_show_proto_info(struct proto *P)
else if (P->proto_state == PS_UP)
{
cli_msg(-1006, " Neighbor ID: %R", p->remote_id);
cli_msg(-1006, " Neighbor caps: %s%s%s%s%s%s",
cli_msg(-1006, " Neighbor caps: %s%s%s%s%s%s%s",
c->peer_refresh_support ? " refresh" : "",
c->peer_enhanced_refresh_support ? " enhanced-refresh" : "",
c->peer_gr_able ? " restart-able" : (c->peer_gr_aware ? " restart-aware" : ""),
c->peer_as4_support ? " AS4" : "",
(c->peer_add_path & ADD_PATH_RX) ? " add-path-rx" : "",
(c->peer_add_path & ADD_PATH_TX) ? " add-path-tx" : "");
cli_msg(-1006, " Session: %s%s%s%s%s%s%s",
(c->peer_add_path & ADD_PATH_TX) ? " add-path-tx" : "",
c->peer_ext_messages_support ? " ext-messages" : "");
cli_msg(-1006, " Session: %s%s%s%s%s%s%s%s",
p->is_internal ? "internal" : "external",
p->cf->multihop ? " multihop" : "",
p->rr_client ? " route-reflector" : "",
p->rs_client ? " route-server" : "",
p->as4_session ? " AS4" : "",
p->add_path_rx ? " add-path-rx" : "",
p->add_path_tx ? " add-path-tx" : "");
p->add_path_tx ? " add-path-tx" : "",
p->ext_messages ? " ext-messages" : "");
cli_msg(-1006, " Source address: %I", p->source_addr);
if (P->cf->in_limit)
cli_msg(-1006, " Route limit: %d/%d",

View File

@ -66,6 +66,7 @@ struct bgp_config {
int capabilities; /* Enable capability handshake [RFC3392] */
int enable_refresh; /* Enable local support for route refresh [RFC2918] */
int enable_as4; /* Enable local support for 4B AS numbers [RFC4893] */
int enable_extended_messages; /* Enable local support for extended messages [draft] */
/* BGPSec */
/* cannot be ifdef'd out due to config.Y compatibility */
@ -90,6 +91,7 @@ struct bgp_config {
int add_path; /* Use ADD-PATH extension [draft] */
int allow_local_as; /* Allow that number of local ASNs in incoming AS_PATHs */
int gr_mode; /* Graceful restart mode (BGP_GR_*) */
int setkey; /* Set MD5 password to system SA/SP database */
unsigned gr_time; /* Graceful restart timeout */
unsigned connect_delay_time; /* Minimum delay between connect attempts */
unsigned connect_retry_time; /* Timeout for connect attempts */
@ -130,7 +132,7 @@ struct bgp_config {
struct bgp_conn {
struct bgp_proto *bgp;
struct birdsock *sk;
unsigned int state; /* State of connection state machine */
uint state; /* State of connection state machine */
struct timer *connect_retry_timer;
struct timer *hold_timer;
struct timer *keepalive_timer;
@ -155,6 +157,7 @@ struct bgp_conn {
u16 peer_gr_time;
u8 peer_gr_flags;
u8 peer_gr_aflags;
u8 peer_ext_messages_support; /* Peer supports extended message length [draft] */
unsigned hold_time, keepalive_time; /* Times calculated from my and neighbor's requirements */
};
@ -176,6 +179,7 @@ struct bgp_proto {
u8 as4_session; /* Session uses 4B AS numbers in AS_PATH (both sides support it) */
u8 add_path_rx; /* Session expects receive of ADD-PATH extended NLRI */
u8 add_path_tx; /* Session expects transmit of ADD-PATH extended NLRI */
u8 ext_messages; /* Session allows to use extended messages (both sides support it) */
u32 local_id; /* BGP identifier of this router */
u32 remote_id; /* BGP identifier of the neighbor */
u32 rr_cluster_id; /* Route reflector cluster ID */
@ -197,7 +201,7 @@ struct bgp_proto {
struct timer *startup_timer; /* Timer used to delay protocol startup due to previous errors (startup_delay) */
struct timer *gr_timer; /* Timer waiting for reestablishment after graceful restart */
struct bgp_bucket **bucket_hash; /* Hash table of attribute buckets */
unsigned int hash_size, hash_count, hash_limit;
uint hash_size, hash_count, hash_limit;
HASH(struct bgp_prefix) prefix_hash; /* Prefixes to be sent */
slab *prefix_slab; /* Slab holding prefix nodes */
list bucket_queue; /* Queue of buckets to send */
@ -235,9 +239,15 @@ struct bgp_bucket {
#define BGP_PORT 179
#define BGP_VERSION 4
#define BGP_HEADER_LENGTH 19
#define BGP_MAX_PACKET_LENGTH 4096
#define BGP_MAX_MESSAGE_LENGTH 4096
#define BGP_MAX_EXT_MSG_LENGTH 65535
#define BGP_RX_BUFFER_SIZE 4096
#define BGP_TX_BUFFER_SIZE BGP_MAX_PACKET_LENGTH
#define BGP_TX_BUFFER_SIZE 4096
#define BGP_RX_BUFFER_EXT_SIZE 65535
#define BGP_TX_BUFFER_EXT_SIZE 65535
static inline int bgp_max_packet_length(struct bgp_proto *p)
{ return p->ext_messages ? BGP_MAX_EXT_MSG_LENGTH : BGP_MAX_MESSAGE_LENGTH; }
extern struct linpool *bgp_linpool;
@ -290,9 +300,10 @@ static inline void set_next_hop(byte *b, ip_addr addr) { ((ip_addr *) b)[0] = ad
void bgp_attach_attr(struct ea_list **to, struct linpool *pool, unsigned attr, uintptr_t val);
byte *bgp_attach_attr_wa(struct ea_list **to, struct linpool *pool, unsigned attr, unsigned len);
struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct linpool *pool, byte * nlri, int nlri_len);
struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, uint len, struct linpool *pool, int mandatory, byte * nlri, int nlri_len);
int bgp_get_attr(struct eattr *e, byte *buf, int buflen);
int bgp_rte_better(struct rte *, struct rte *);
int bgp_rte_mergable(rte *pri, rte *sec);
int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best);
void bgp_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs);
int bgp_import_control(struct proto *, struct rte **, struct ea_list **, struct linpool *);
@ -300,7 +311,7 @@ void bgp_init_bucket_table(struct bgp_proto *);
void bgp_free_bucket(struct bgp_proto *p, struct bgp_bucket *buck);
void bgp_init_prefix_table(struct bgp_proto *p, u32 order);
void bgp_free_prefix(struct bgp_proto *p, struct bgp_prefix *bp);
unsigned int bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains);
uint bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains);
void bgp_get_route_info(struct rte *, byte *buf, struct ea_list *attrs);
inline static void bgp_attach_attr_ip(struct ea_list **to, struct linpool *pool, unsigned attr, ip_addr a)

View File

@ -35,7 +35,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC,
SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX, GRACEFUL, RESTART, AWARE,
CHECK, LINK, PORT,
CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY,
BGPSEC, BGPSEC_SKI, BGPSEC_KEY_REPO_PATH, BGPSEC_PRIV_KEY_PATH,
BGPSEC_SAVE_BINARY_KEYS, BGPSEC_PREFER, BGPSEC_NO_PCOUNT0,
BGPSEC_REQUIRE, BGPSEC_NO_INVALID_ROUTES)
@ -74,6 +74,7 @@ bgp_proto_start: proto_start BGP {
BGP_CFG->default_local_pref = 100;
BGP_CFG->gr_mode = BGP_GR_AWARE;
BGP_CFG->gr_time = 120;
BGP_CFG->setkey = 1;
}
;
@ -128,9 +129,11 @@ bgp_proto:
| bgp_proto DISABLE AFTER ERROR bool ';' { BGP_CFG->disable_after_error = $5; }
| bgp_proto ENABLE ROUTE REFRESH bool ';' { BGP_CFG->enable_refresh = $5; }
| bgp_proto ENABLE AS4 bool ';' { BGP_CFG->enable_as4 = $4; }
| bgp_proto ENABLE EXTENDED MESSAGES bool ';' { BGP_CFG->enable_extended_messages = $5; }
| bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
| bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; }
| bgp_proto PASSWORD text ';' { BGP_CFG->password = $3; }
| bgp_proto SETKEY bool ';' { BGP_CFG->setkey = $3; }
| bgp_proto ROUTE LIMIT expr ';' {
this_proto->in_limit = cfg_allocz(sizeof(struct proto_limit));
this_proto->in_limit->limit = $4;

View File

@ -99,7 +99,7 @@ mrt_put_bgp4_hdr(byte *buf, struct bgp_conn *conn, int as4)
static void
mrt_dump_bgp_packet(struct bgp_conn *conn, byte *pkt, unsigned len)
{
byte buf[BGP_MAX_PACKET_LENGTH + 128];
byte *buf = alloca(128+len); /* 128 is enough for MRT headers */
byte *bp = buf + MRTDUMP_HDR_LENGTH;
int as4 = conn->bgp->as4_session;
@ -178,6 +178,14 @@ bgp_put_cap_rr(struct bgp_proto *p UNUSED, byte *buf)
return buf;
}
static byte *
bgp_put_cap_ext_msg(struct bgp_proto *p UNUSED, byte *buf)
{
*buf++ = 6; /* Capability 6: Support for extended messages */
*buf++ = 0; /* Capability data length */
return buf;
}
static byte *
bgp_put_cap_gr1(struct bgp_proto *p, byte *buf)
{
@ -326,6 +334,9 @@ bgp_create_open(struct bgp_conn *conn, byte *buf)
if (p->cf->enable_refresh)
cap = bgp_put_cap_err(p, cap);
if (p->cf->enable_extended_messages)
cap = bgp_put_cap_ext_msg(p, cap);
cap_len = cap - buf - 12;
if (cap_len > 0)
{
@ -341,8 +352,8 @@ bgp_create_open(struct bgp_conn *conn, byte *buf)
}
}
static unsigned int
bgp_encode_prefixes(struct bgp_proto *p, byte *w, struct bgp_bucket *buck, unsigned int remains)
static uint
bgp_encode_prefixes(struct bgp_proto *p, byte *w, struct bgp_bucket *buck, uint remains)
{
byte *start = w;
ip_addr a;
@ -397,7 +408,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
{
struct bgp_proto *p = conn->bgp;
struct bgp_bucket *buck;
int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4;
int remains = bgp_max_packet_length(p) - BGP_HEADER_LENGTH - 4;
byte *w;
int wd_size = 0;
int r_size = 0;
@ -485,7 +496,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
struct bgp_proto *p = conn->bgp;
struct bgp_bucket *buck;
int size, second, rem_stored;
int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4;
int remains = bgp_max_packet_length(p) - BGP_HEADER_LENGTH - 4;
byte *w, *w_stored, *tmp, *tstart;
ip_addr *ipp, ip, ip_ll;
ea_list *ea = NULL;
@ -739,7 +750,7 @@ bgp_create_end_refresh(struct bgp_conn *conn, byte *buf)
static void
bgp_create_header(byte *buf, unsigned int len, unsigned int type)
bgp_create_header(byte *buf, uint len, uint type)
{
memset(buf, 0xff, 16); /* Marker */
put_u16(buf+16, len);
@ -760,7 +771,7 @@ static int
bgp_fire_tx(struct bgp_conn *conn)
{
struct bgp_proto *p = conn->bgp;
unsigned int s = conn->packets_to_send;
uint s = conn->packets_to_send;
sock *sk = conn->sk;
byte *buf, *pkt, *end;
int type;
@ -911,6 +922,12 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len)
conn->peer_refresh_support = 1;
break;
case 6: /* Extended message length capability, draft */
if (cl != 0)
goto err;
conn->peer_ext_messages_support = 1;
break;
case 64: /* Graceful restart capability, RFC 4724 */
if (cl % 4 != 2)
goto err;
@ -1161,6 +1178,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
p->add_path_rx = (p->cf->add_path & ADD_PATH_RX) && (conn->peer_add_path & ADD_PATH_TX);
p->add_path_tx = (p->cf->add_path & ADD_PATH_TX) && (conn->peer_add_path & ADD_PATH_RX);
p->gr_ready = p->cf->gr_mode && conn->peer_gr_able;
p->ext_messages = p->cf->enable_extended_messages && conn->peer_ext_messages_support;
if (p->add_path_tx)
p->p.accept_ra_types = RA_ANY;
@ -1573,7 +1591,7 @@ static struct {
{ 2, 4, "Unsupported optional parameter" },
{ 2, 5, "Authentication failure" },
{ 2, 6, "Unacceptable hold time" },
{ 2, 7, "Required capability missing" }, /* [RFC3392] */
{ 2, 7, "Required capability missing" }, /* [RFC5492] */
{ 2, 8, "No supported AFI/SAFI" }, /* This error msg is nonstandard */
{ 3, 0, "Invalid UPDATE message" },
{ 3, 1, "Malformed attribute list" },
@ -1822,6 +1840,7 @@ int
bgp_rx(sock *sk, int size)
{
struct bgp_conn *conn = sk->data;
struct bgp_proto *p = conn->bgp;
byte *pkt_start = sk->rbuf;
byte *end = pkt_start + size;
unsigned i, len;
@ -1838,7 +1857,7 @@ bgp_rx(sock *sk, int size)
break;
}
len = get_u16(pkt_start+16);
if (len < BGP_HEADER_LENGTH || len > BGP_MAX_PACKET_LENGTH)
if (len < BGP_HEADER_LENGTH || len > bgp_max_packet_length(p))
{
bgp_error(conn, 1, 2, pkt_start+16, 2);
break;

View File

@ -493,8 +493,11 @@ ospf_iface_add(struct object_lock *lock)
ifa->flood_queue = mb_allocz(ifa->pool, ifa->flood_queue_size * sizeof(void *));
}
/* Do iface UP, unless there is no link and we use link detection */
ospf_iface_sm(ifa, (ifa->check_link && !(ifa->iface->flags & IF_LINK_UP)) ? ISM_LOOP : ISM_UP);
/* Do iface UP, unless there is no link (then wait in LOOP state) */
if (!ifa->check_link || (ifa->iface->flags & IF_LINK_UP))
ospf_iface_sm(ifa, ISM_UP);
else
ospf_iface_chstate(ifa, OSPF_IS_LOOP);
}
static inline void
@ -596,10 +599,10 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
if (ospf_is_v2(p) && (ifa->type == OSPF_IT_NBMA) && (addr->flags & IA_PEER))
ifa->type = OSPF_IT_PTMP;
if ((ifa->type == OSPF_IT_BCAST) && !(iface->flags & if_multi_flag))
if ((ifa->type == OSPF_IT_BCAST) && !(iface->flags & if_multi_flag) && !ifa->stub)
ifa->type = OSPF_IT_NBMA;
if ((ifa->type == OSPF_IT_PTP) && !(iface->flags & if_multi_flag))
if ((ifa->type == OSPF_IT_PTP) && !(iface->flags & if_multi_flag) && !ifa->stub)
ifa->type = OSPF_IT_PTMP;
if (ifa->type != old_type)
@ -1344,9 +1347,9 @@ ospf_iface_info(struct ospf_iface *ifa)
cli_msg(-1015, "\tRetransmit timer: %u", ifa->rxmtint);
if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA))
{
cli_msg(-1015, "\tDesigned router (ID): %R", ifa->drid);
cli_msg(-1015, "\tDesigned router (IP): %I", ifa->drip);
cli_msg(-1015, "\tBackup designed router (ID): %R", ifa->bdrid);
cli_msg(-1015, "\tBackup designed router (IP): %I", ifa->bdrip);
cli_msg(-1015, "\tDesignated router (ID): %R", ifa->drid);
cli_msg(-1015, "\tDesignated router (IP): %I", ifa->drip);
cli_msg(-1015, "\tBackup designated router (ID): %R", ifa->bdrid);
cli_msg(-1015, "\tBackup designated router (IP): %I", ifa->bdrip);
}
}

View File

@ -2,14 +2,15 @@
* BIRD -- OSPF
*
* (c) 1999--2004 Ondrej Filip <feela@network.cz>
* (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2009--2014 CZ.NIC z.s.p.o.
* (c) 2009--2015 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2009--2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "ospf.h"
#include "lib/fletcher16.h"
#ifndef CPU_BIG_ENDIAN
void
@ -150,127 +151,41 @@ lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain)
}
/*
void
buf_dump(const char *hdr, const byte *buf, int blen)
lsa_generate_checksum(struct ospf_lsa_header *lsa, const u8 *body)
{
char b2[1024];
char *bp;
int first = 1;
int i;
const char *lhdr = hdr;
bp = b2;
for(i = 0; i < blen; i++)
{
if ((i > 0) && ((i % 16) == 0))
{
*bp = 0;
log(L_WARN "%s\t%s", lhdr, b2);
lhdr = "";
bp = b2;
}
bp += snprintf(bp, 1022, "%02x ", buf[i]);
}
*bp = 0;
log(L_WARN "%s\t%s", lhdr, b2);
}
*/
#define MODX 4102 /* larges signed value without overflow */
/* Fletcher Checksum -- Refer to RFC1008. */
#define MODX 4102
#define LSA_CHECKSUM_OFFSET 15
/* FIXME This is VERY uneficient, I have huge endianity problems */
void
lsasum_calculate(struct ospf_lsa_header *h, void *body)
{
u16 length = h->length;
// log(L_WARN "Checksum %R %R %d start (len %d)", h->id, h->rt, h->type, length);
lsa_hton_hdr(h, h);
lsa_hton_body1(body, length - sizeof(struct ospf_lsa_header));
struct fletcher16_context ctx;
struct ospf_lsa_header hdr;
u16 len = lsa->length;
/*
char buf[1024];
memcpy(buf, h, sizeof(struct ospf_lsa_header));
memcpy(buf + sizeof(struct ospf_lsa_header), body, length - sizeof(struct ospf_lsa_header));
buf_dump("CALC", buf, length);
*/
* lsa and body are in the host order, we need to compute Fletcher-16 checksum
* for data in the network order. We also skip the initial age field.
*/
(void) lsasum_check(h, body);
lsa_hton_hdr(lsa, &hdr);
hdr.checksum = 0;
// log(L_WARN "Checksum result %4x", h->checksum);
lsa_ntoh_hdr(h, h);
lsa_ntoh_body1(body, length - sizeof(struct ospf_lsa_header));
fletcher16_init(&ctx);
fletcher16_update(&ctx, (u8 *) &hdr + 2, sizeof(struct ospf_lsa_header) - 2);
fletcher16_update_n32(&ctx, body, len - sizeof(struct ospf_lsa_header));
lsa->checksum = fletcher16_final(&ctx, len, OFFSETOF(struct ospf_lsa_header, checksum));
}
/*
* Note, that this function expects that LSA is in big endianity
* It also returns value in big endian
*/
u16
lsasum_check(struct ospf_lsa_header *h, void *body)
lsa_verify_checksum(const void *lsa_n, int lsa_len)
{
u8 *sp, *ep, *p, *q, *b;
int c0 = 0, c1 = 0;
int x, y;
u16 length;
struct fletcher16_context ctx;
b = body;
sp = (char *) h;
sp += 2; /* Skip Age field */
length = ntohs(h->length) - 2;
h->checksum = 0;
/* The whole LSA is at lsa_n in net order, we just skip initial age field */
for (ep = sp + length; sp < ep; sp = q)
{ /* Actually MODX is very large, do we need the for-cyclus? */
q = sp + MODX;
if (q > ep)
q = ep;
for (p = sp; p < q; p++)
{
/*
* I count with bytes from header and than from body
* but if there is no body, it's appended to header
* (probably checksum in update receiving) and I go on
* after header
*/
if ((b == NULL) || (p < (u8 *) (h + 1)))
{
c0 += *p;
}
else
{
c0 += *(b + (p - (u8 *) (h + 1)));
}
fletcher16_init(&ctx);
fletcher16_update(&ctx, (u8 *) lsa_n + 2, lsa_len - 2);
c1 += c0;
}
c0 %= 255;
c1 %= 255;
}
x = (int)((length - LSA_CHECKSUM_OFFSET) * c0 - c1) % 255;
if (x <= 0)
x += 255;
y = 510 - c0 - x;
if (y > 255)
y -= 255;
((u8 *) & h->checksum)[0] = x;
((u8 *) & h->checksum)[1] = y;
return h->checksum;
return fletcher16_compute(&ctx) == 0;
}
int
lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2)
/* Return codes from point of view of l1 */

View File

@ -46,9 +46,9 @@ static inline u32 lsa_get_etype(struct ospf_lsa_header *h, struct ospf_proto *p)
int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa);
void lsa_generate_checksum(struct ospf_lsa_header *lsa, const u8 *body);
u16 lsa_verify_checksum(const void *lsa_n, int lsa_len);
void lsasum_calculate(struct ospf_lsa_header *header, void *body);
u16 lsasum_check(struct ospf_lsa_header *h, void *body);
#define CMP_NEWER 1
#define CMP_SAME 0
#define CMP_OLDER -1

View File

@ -530,8 +530,8 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa,
DBG("Update Type: %04x, Id: %R, Rt: %R, Sn: 0x%08x, Age: %u, Sum: %u\n",
lsa_type, lsa.id, lsa.rt, lsa.sn, lsa.age, lsa.checksum);
/* RFC 2328 13. (1) - validate LSA checksum */
if (lsa_n->checksum != lsasum_check(lsa_n, NULL))
/* RFC 2328 13. (1) - verify LSA checksum */
if ((lsa_n->checksum == 0) || !lsa_verify_checksum(lsa_n, lsa_len))
SKIP("invalid checksum");
/* RFC 2328 13. (2) */

View File

@ -108,6 +108,7 @@ ospf_neigh_down(struct ospf_neighbor *n)
{
struct ospf_iface *ifa = n->ifa;
struct ospf_proto *p = ifa->oa->po;
u32 rid = n->rid;
if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP))
{
@ -121,7 +122,7 @@ ospf_neigh_down(struct ospf_neighbor *n)
rem_node(NODE n);
rfree(n->pool);
OSPF_TRACE(D_EVENTS, "Neighbor %R on %s removed", n->rid, ifa->ifname);
OSPF_TRACE(D_EVENTS, "Neighbor %R on %s removed", rid, ifa->ifname);
}
/**

View File

@ -450,10 +450,21 @@ ospf_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool
if (oa_is_stub(oa))
return -1; /* Do not export routes to stub areas */
eattr *ea = ea_find(e->attrs->eattrs, EA_GEN_IGP_METRIC);
u32 m1 = (ea && (ea->u.data < LSINFINITY)) ? ea->u.data : LSINFINITY;
ea_list *ea = e->attrs->eattrs;
u32 m0 = ea_get_int(ea, EA_GEN_IGP_METRIC, LSINFINITY);
u32 m1 = MIN(m0, LSINFINITY);
u32 m2 = 10000;
u32 tag = 0;
*attrs = ospf_build_attrs(*attrs, pool, m1, 10000, 0, 0);
/* Hack for setting attributes directly in static protocol */
if (e->attrs->source == RTS_STATIC)
{
m1 = ea_get_int(ea, EA_OSPF_METRIC1, m1);
m2 = ea_get_int(ea, EA_OSPF_METRIC2, 10000);
tag = ea_get_int(ea, EA_OSPF_TAG, 0);
}
*attrs = ospf_build_attrs(*attrs, pool, m1, m2, tag, 0);
return 0; /* Leave decision to the filters */
}

View File

@ -11,6 +11,7 @@
#include "ospf.h"
#include "nest/password.h"
#include "lib/md5.h"
#include "lib/socket.h"
void
ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type)
@ -108,11 +109,11 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt)
char password[OSPF_AUTH_CRYPT_SIZE];
strncpy(password, passwd->password, sizeof(password));
struct MD5Context ctxt;
MD5Init(&ctxt);
MD5Update(&ctxt, (char *) pkt, plen);
MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE);
MD5Final(tail, &ctxt);
struct md5_context ctx;
md5_init(&ctx);
md5_update(&ctx, (char *) pkt, plen);
md5_update(&ctx, password, OSPF_AUTH_CRYPT_SIZE);
memcpy((byte *) tail, md5_final(&ctx), MD5_SIZE);
break;
default:
@ -174,19 +175,17 @@ ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_
if (!pass)
DROP("no suitable password found", auth->md5.keyid);
void *tail = ((void *) pkt) + plen;
char passwd[OSPF_AUTH_CRYPT_SIZE];
char md5sum[OSPF_AUTH_CRYPT_SIZE];
byte *tail = ((byte *) pkt) + plen;
char received[OSPF_AUTH_CRYPT_SIZE];
memcpy(received, tail, OSPF_AUTH_CRYPT_SIZE);
strncpy(tail, pass->password, OSPF_AUTH_CRYPT_SIZE);
strncpy(passwd, pass->password, OSPF_AUTH_CRYPT_SIZE);
struct md5_context ctx;
md5_init(&ctx);
md5_update(&ctx, (byte *) pkt, plen + OSPF_AUTH_CRYPT_SIZE);
char *computed = md5_final(&ctx);
struct MD5Context ctxt;
MD5Init(&ctxt);
MD5Update(&ctxt, (char *) pkt, plen);
MD5Update(&ctxt, passwd, OSPF_AUTH_CRYPT_SIZE);
MD5Final(md5sum, &ctxt);
if (memcmp(md5sum, tail, OSPF_AUTH_CRYPT_SIZE))
if (memcmp(received, computed, OSPF_AUTH_CRYPT_SIZE))
DROP("wrong MD5 digest", pass->id);
if (n)
@ -223,7 +222,7 @@ ospf_rx_hook(sock *sk, int len)
return 1;
DBG("OSPF: RX hook called (iface %s, src %I, dst %I)\n",
sk->ifname, sk->faddr, sk->laddr);
sk->iface->name, sk->faddr, sk->laddr);
/* Initially, the packet is associated with the 'master' iface */
struct ospf_iface *ifa = sk->data;
@ -231,6 +230,10 @@ ospf_rx_hook(sock *sk, int len)
const char *err_dsc = NULL;
uint err_val = 0;
/* Should not happen */
if (ifa->state <= OSPF_IS_LOOP)
return 1;
int src_local, dst_local, dst_mcast;
src_local = ipa_in_net(sk->faddr, ifa->addr->prefix, ifa->addr->pxlen);
dst_local = ipa_equal(sk->laddr, ifa->addr->ip);

View File

@ -43,7 +43,7 @@ unresolved_vlink(ort *ort)
}
static inline struct mpnh *
new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, unsigned char weight)
new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, byte weight)
{
struct mpnh *nh = lp_alloc(p->nhpool, sizeof(struct mpnh));
nh->gw = gw;
@ -53,88 +53,6 @@ new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, unsigned char
return nh;
}
static inline struct mpnh *
copy_nexthop(struct ospf_proto *p, const struct mpnh *src)
{
struct mpnh *nh = lp_alloc(p->nhpool, sizeof(struct mpnh));
nh->gw = src->gw;
nh->iface = src->iface;
nh->next = NULL;
nh->weight = src->weight;
return nh;
}
/* Compare nexthops during merge.
We need to maintain nhs sorted to eliminate duplicities */
static int
cmp_nhs(struct mpnh *s1, struct mpnh *s2)
{
int r;
if (!s1)
return 1;
if (!s2)
return -1;
r = ((int) s2->weight) - ((int) s1->weight);
if (r)
return r;
r = ipa_compare(s1->gw, s2->gw);
if (r)
return r;
return ((int) s1->iface->index) - ((int) s2->iface->index);
}
static struct mpnh *
merge_nexthops(struct ospf_proto *p, struct mpnh *s1, struct mpnh *s2, int r1, int r2)
{
struct mpnh *root = NULL;
struct mpnh **n = &root;
int count = p->ecmp;
ASSERT(p->ecmp);
/*
* r1, r2 signalize whether we can reuse nexthops from s1, s2.
* New nexthops (s2, new) can be reused if they are not inherited
* from the parent (i.e. it is allocated in calc_next_hop()).
* Current nexthops (s1, en->nhs) can be reused if they weren't
* inherited in previous steps (that is stored in nhs_reuse,
* i.e. created by merging or allocalted in calc_next_hop()).
*
* Generally, a node first inherits shared nexthops from its
* parent and later possibly gets reusable copy during merging.
*/
while ((s1 || s2) && count--)
{
int cmp = cmp_nhs(s1, s2);
if (cmp < 0)
{
*n = r1 ? s1 : copy_nexthop(p, s1);
s1 = s1->next;
}
else if (cmp > 0)
{
*n = r2 ? s2 : copy_nexthop(p, s2);
s2 = s2->next;
}
else
{
*n = r1 ? s1 : (r2 ? s2 : copy_nexthop(p, s1));
s1 = s1->next;
s2 = s2->next;
}
n = &((*n)->next);
}
*n = NULL;
return root;
}
/* Returns true if there are device nexthops in n */
static inline int
has_device_nexthops(const struct mpnh *n)
@ -178,7 +96,7 @@ fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
}
}
return merge_nexthops(p, root1, root2, 1, 1);
return mpnh_merge(root1, root2, 1, 1, p->ecmp, p->nhpool);
}
@ -374,7 +292,8 @@ ort_merge(struct ospf_proto *p, ort *o, const orta *new)
if (old->nhs != new->nhs)
{
old->nhs = merge_nexthops(p, old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse);
old->nhs = mpnh_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
p->ecmp, p->nhpool);
old->nhs_reuse = 1;
}
@ -389,7 +308,8 @@ ort_merge_ext(struct ospf_proto *p, ort *o, const orta *new)
if (old->nhs != new->nhs)
{
old->nhs = merge_nexthops(p, old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse);
old->nhs = mpnh_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
p->ecmp, p->nhpool);
old->nhs_reuse = 1;
}
@ -1885,8 +1805,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
return;
}
/* We know that en->color == CANDIDATE and en->nhs is defined. */
/* If en->dist > 0, we know that en->color == CANDIDATE and en->nhs is defined. */
if ((dist == en->dist) && !nh_is_vlink(en->nhs))
{
/*
@ -1900,7 +1819,14 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
* allocated in calc_next_hop()).
*
* Generally, a node first inherits shared nexthops from its parent and
* later possibly gets reusable copy during merging.
* later possibly gets reusable (private) copy during merging. This is more
* or less same for both top_hash_entry nodes and orta nodes.
*
* Note that when a child inherits a private nexthop from its parent, it
* should make the nexthop shared for both parent and child, while we only
* update nhs_reuse for the child node. This makes nhs_reuse field for the
* parent technically incorrect, but it is not a problem as parent's nhs
* will not be modified (and nhs_reuse examined) afterwards.
*/
/* Keep old ones */
@ -1909,7 +1835,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
/* Merge old and new */
int new_reuse = (par->nhs != nhs);
en->nhs = merge_nexthops(p, en->nhs, nhs, en->nhs_reuse, new_reuse);
en->nhs = mpnh_merge(en->nhs, nhs, en->nhs_reuse, new_reuse, p->ecmp, p->nhpool);
en->nhs_reuse = 1;
return;
}

View File

@ -18,7 +18,8 @@
typedef struct orta
{
u8 type; /* RTS_OSPF_* */
u8 nhs_reuse; /* Whether nhs nodes can be reused during merging */
u8 nhs_reuse; /* Whether nhs nodes can be reused during merging.
See a note in rt.c:add_cand() */
u32 options;
/*
* For ORT_ROUTER routes, options field are router-LSA style

View File

@ -129,7 +129,7 @@ ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_ls
en->lsa.age = 0;
en->init_age = 0;
en->inst_time = now;
lsasum_calculate(&en->lsa, en->lsa_body);
lsa_generate_checksum(&en->lsa, en->lsa_body);
OSPF_TRACE(D_EVENTS, "Advancing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
@ -238,7 +238,7 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa
en->lsa.age = 0;
en->init_age = 0;
en->inst_time = now;
lsasum_calculate(&en->lsa, en->lsa_body);
lsa_generate_checksum(&en->lsa, en->lsa_body);
OSPF_TRACE(D_EVENTS, "Originating LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
@ -278,7 +278,7 @@ ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa)
if (!SNODE_VALID(en))
s_add_tail(&p->lsal, SNODE en);
if (en->lsa_body == NULL)
if (!en->nf || !en->lsa_body)
en->nf = lsa->nf;
if (en->nf != lsa->nf)
@ -382,7 +382,7 @@ ospf_refresh_lsa(struct ospf_proto *p, struct top_hash_entry *en)
en->lsa.age = 0;
en->init_age = 0;
en->inst_time = now;
lsasum_calculate(&en->lsa, en->lsa_body);
lsa_generate_checksum(&en->lsa, en->lsa_body);
ospf_flood_lsa(p, en, NULL);
}

View File

@ -39,7 +39,7 @@ struct top_hash_entry
#define INSPF 2
u8 mode; /* LSA generated during RT calculation (LSA_RTCALC or LSA_STALE)*/
u8 nhs_reuse; /* Whether nhs nodes can be reused during merging.
See a note in rt.c:merge_nexthops() */
See a note in rt.c:add_cand() */
};

View File

@ -1,2 +1,2 @@
S rip.c
S auth.c
S packets.c

View File

@ -1,4 +1,4 @@
source=rip.c auth.c
source=rip.c packets.c
root-rel=../../
dir-name=proto/rip

View File

@ -1,168 +0,0 @@
/*
* Rest in pieces - RIP protocol
*
* Copyright (c) 1999 Pavel Machek <pavel@ucw.cz>
* Copyright (c) 2004 Ondrej Filip <feela@network.cz>
*
* Bug fixes by Eric Leblond <eleblond@init-sys.com>, April 2003
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#undef LOCAL_DEBUG
#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
#include "lib/socket.h"
#include "lib/resource.h"
#include "lib/lists.h"
#include "lib/timer.h"
#include "lib/md5.h"
#include "lib/string.h"
#include "rip.h"
#define P ((struct rip_proto *) p)
#define P_CF ((struct rip_proto_config *)p->cf)
#define PACKETLEN(num) (num * sizeof(struct rip_block) + sizeof(struct rip_packet_heading))
/*
* rip_incoming_authentication - check authentication of incomming packet and return 1 if there's problem.
*/
int
rip_incoming_authentication( struct proto *p, struct rip_block_auth *block, struct rip_packet *packet, int num, ip_addr whotoldme )
{
DBG( "Incoming authentication: " );
switch (ntohs(block->authtype)) { /* Authentication type */
case AT_PLAINTEXT:
{
struct password_item *passwd = password_find(P_CF->passwords, 1);
DBG( "Plaintext passwd" );
if (!passwd) {
log( L_AUTH "No passwords set and password authentication came" );
return 1;
}
if (strncmp( (char *) (&block->packetlen), passwd->password, 16)) {
log( L_AUTH "Passwd authentication failed!" );
DBG( "Expected %s, got %.16s\n", passwd->password, &block->packetlen );
return 1;
}
}
break;
case AT_MD5:
DBG( "md5 password" );
{
struct password_item *pass = NULL, *ptmp;
struct rip_md5_tail *tail;
struct MD5Context ctxt;
char md5sum_packet[16];
char md5sum_computed[16];
struct neighbor *neigh = neigh_find(p, &whotoldme, 0);
list *l = P_CF->passwords;
if (ntohs(block->packetlen) != PACKETLEN(num) - sizeof(struct rip_md5_tail) ) {
log( L_ERR "Packet length in MD5 does not match computed value" );
return 1;
}
tail = (struct rip_md5_tail *) ((char *) packet + (ntohs(block->packetlen) ));
if ((tail->mustbeFFFF != 0xffff) || (tail->mustbe0001 != 0x0100)) {
log( L_ERR "MD5 tail signature is not there" );
return 1;
}
WALK_LIST(ptmp, *l)
{
if (block->keyid != ptmp->id) continue;
if ((ptmp->genfrom > now_real) || (ptmp->gento < now_real)) continue;
pass = ptmp;
break;
}
if(!pass) return 1;
if (!neigh) {
log( L_AUTH "Non-neighbour MD5 checksummed packet?" );
} else {
if (neigh->aux > block->seq) {
log( L_AUTH "MD5 protected packet with lower numbers" );
return 1;
}
neigh->aux = block->seq;
}
memcpy(md5sum_packet, tail->md5, 16);
strncpy(tail->md5, pass->password, 16);
MD5Init(&ctxt);
MD5Update(&ctxt, (char *) packet, ntohs(block->packetlen) + sizeof(struct rip_block_auth) );
MD5Final(md5sum_computed, &ctxt);
if (memcmp(md5sum_packet, md5sum_computed, 16))
return 1;
}
}
return 0;
}
/*
* rip_outgoing_authentication - append authentication information to the packet.
* %num: number of rip_blocks already in packets. This function returns size of packet to send.
*/
int
rip_outgoing_authentication( struct proto *p, struct rip_block_auth *block, struct rip_packet *packet, int num )
{
struct password_item *passwd = password_find(P_CF->passwords, 1);
if (!P_CF->authtype)
return PACKETLEN(num);
DBG( "Outgoing authentication: " );
if (!passwd) {
log( L_ERR "No suitable password found for authentication" );
return PACKETLEN(num);
}
block->authtype = htons(P_CF->authtype);
block->mustbeFFFF = 0xffff;
switch (P_CF->authtype) {
case AT_PLAINTEXT:
strncpy( (char *) (&block->packetlen), passwd->password, 16);
return PACKETLEN(num);
case AT_MD5:
{
struct rip_md5_tail *tail;
struct MD5Context ctxt;
static u32 sequence = 0;
if (num > PACKET_MD5_MAX)
bug( "We can not add MD5 authentication to this long packet" );
/* need to preset the sequence number to a sane value */
if (!sequence)
sequence = (u32) time(NULL);
block->keyid = passwd->id;
block->authlen = sizeof(struct rip_block_auth);
block->seq = sequence++;
block->zero0 = 0;
block->zero1 = 0;
block->packetlen = htons(PACKETLEN(num));
tail = (struct rip_md5_tail *) ((char *) packet + PACKETLEN(num) );
tail->mustbeFFFF = 0xffff;
tail->mustbe0001 = 0x0100;
strncpy(tail->md5, passwd->password, 16);
MD5Init(&ctxt);
MD5Update(&ctxt, (char *) packet, PACKETLEN(num) + sizeof(struct rip_md5_tail));
MD5Final(tail->md5, &ctxt);
return PACKETLEN(num) + block->authlen;
}
default:
bug( "Unknown authtype in outgoing authentication?" );
}
}

View File

@ -1,17 +1,14 @@
/*
* BIRD -- RIP Configuration
*
* (c) 1998--1999 Pavel Machek <pavel@ucw.cz>
* (c) 2004--2013 Ondrej Filip <feela@network.cz>
* (c) 2009--2015 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2009--2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
/*
To add:
version1 switch
*/
CF_HDR
#include "proto/rip/rip.h"
@ -19,76 +16,141 @@ CF_HDR
CF_DEFINES
#define RIP_CFG ((struct rip_proto_config *) this_proto)
#define RIP_IPATT ((struct rip_patt *) this_ipatt)
#define RIP_CFG ((struct rip_config *) this_proto)
#define RIP_IFACE ((struct rip_iface_config *) this_ipatt)
static inline int rip_cfg_is_v2(void) { return RIP_CFG->rip2; }
static inline int rip_cfg_is_ng(void) { return ! RIP_CFG->rip2; }
static inline void
rip_check_auth(void)
{
if (rip_cfg_is_ng())
cf_error("Authentication not supported in RIPng");
}
#ifdef IPV6
#define RIP_DEFAULT_TTL_SECURITY 2
#else
#define RIP_DEFAULT_TTL_SECURITY 0
#endif
CF_DECLS
CF_KEYWORDS(RIP, INFINITY, METRIC, PORT, PERIOD, GARBAGE, TIMEOUT,
MODE, BROADCAST, MULTICAST, QUIET, NOLISTEN, VERSION1,
AUTHENTICATION, NONE, PLAINTEXT, MD5, TTL, SECURITY,
HONOR, NEVER, NEIGHBOR, ALWAYS, TX, PRIORITY, ONLY,
RIP_METRIC, RIP_TAG)
CF_KEYWORDS(RIP, ECMP, LIMIT, WEIGHT, INFINITY, METRIC, UPDATE, TIMEOUT,
GARBAGE, PORT, ADDRESS, MODE, BROADCAST, MULTICAST, PASSIVE,
VERSION, SPLIT, HORIZON, POISON, REVERSE, CHECK, ZERO, TIME, BFD,
AUTHENTICATION, NONE, PLAINTEXT, CRYPTOGRAPHIC, MD5, TTL, SECURITY,
RX, TX, BUFFER, LENGTH, PRIORITY, ONLY, LINK, RIP_METRIC, RIP_TAG)
%type <i> rip_mode rip_auth
%type <i> rip_auth
CF_GRAMMAR
CF_ADDTO(proto, rip_cfg '}' { RIP_CFG->passwords = get_passwords(); } )
CF_ADDTO(proto, rip_proto)
rip_cfg_start: proto_start RIP {
this_proto = proto_config_new(&proto_rip, $1);
rip_init_config(RIP_CFG);
}
rip_proto_start: proto_start RIP
{
this_proto = proto_config_new(&proto_rip, $1);
init_list(&RIP_CFG->patt_list);
RIP_CFG->rip2 = RIP_IS_V2;
RIP_CFG->infinity = RIP_DEFAULT_INFINITY;
RIP_CFG->min_timeout_time = 60;
RIP_CFG->max_garbage_time = 60;
};
rip_proto_item:
proto_item
| ECMP bool { RIP_CFG->ecmp = $2 ? RIP_DEFAULT_ECMP_LIMIT : 0; }
| ECMP bool LIMIT expr { RIP_CFG->ecmp = $2 ? $4 : 0; if ($4 < 0) cf_error("ECMP limit cannot be negative"); }
| INFINITY expr { RIP_CFG->infinity = $2; }
| INTERFACE rip_iface
;
rip_cfg:
rip_cfg_start proto_name '{'
| rip_cfg proto_item ';'
| rip_cfg INFINITY expr ';' { RIP_CFG->infinity = $3; }
| rip_cfg PORT expr ';' { RIP_CFG->port = $3; }
| rip_cfg PERIOD expr ';' { RIP_CFG->period = $3; }
| rip_cfg GARBAGE TIME expr ';' { RIP_CFG->garbage_time = $4; }
| rip_cfg TIMEOUT TIME expr ';' { RIP_CFG->timeout_time = $4; }
| rip_cfg AUTHENTICATION rip_auth ';' {RIP_CFG->authtype = $3; }
| rip_cfg password_list ';'
| rip_cfg HONOR ALWAYS ';' { RIP_CFG->honor = HO_ALWAYS; }
| rip_cfg HONOR NEIGHBOR ';' { RIP_CFG->honor = HO_NEIGHBOR; }
| rip_cfg HONOR NEVER ';' { RIP_CFG->honor = HO_NEVER; }
| rip_cfg INTERFACE rip_iface ';'
rip_proto_opts:
/* empty */
| rip_proto_opts rip_proto_item ';'
;
rip_auth:
PLAINTEXT { $$=AT_PLAINTEXT; }
| MD5 { $$=AT_MD5; }
| NONE { $$=AT_NONE; }
;
rip_proto:
rip_proto_start proto_name '{' rip_proto_opts '}';
rip_mode:
BROADCAST { $$=IM_BROADCAST; }
| MULTICAST { $$=0; }
| QUIET { $$=IM_QUIET; }
| NOLISTEN { $$=IM_NOLISTEN; }
| VERSION1 { $$=IM_VERSION1 | IM_BROADCAST; }
;
rip_iface_start:
{
this_ipatt = cfg_allocz(sizeof(struct rip_iface_config));
add_tail(&RIP_CFG->patt_list, NODE this_ipatt);
init_list(&this_ipatt->ipn_list);
reset_passwords();
RIP_IFACE->metric = 1;
RIP_IFACE->port = rip_cfg_is_v2() ? RIP_PORT : RIP_NG_PORT;
RIP_IFACE->version = rip_cfg_is_v2() ? RIP_V2 : RIP_V1;
RIP_IFACE->split_horizon = 1;
RIP_IFACE->poison_reverse = 1;
RIP_IFACE->check_zero = 1;
RIP_IFACE->ttl_security = rip_cfg_is_v2() ? 0 : 1;
RIP_IFACE->rx_buffer = rip_cfg_is_v2() ? RIP_MAX_PKT_LENGTH : 0;
RIP_IFACE->tx_length = rip_cfg_is_v2() ? RIP_MAX_PKT_LENGTH : 0;
RIP_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL;
RIP_IFACE->tx_priority = sk_priority_control;
RIP_IFACE->update_time = RIP_DEFAULT_UPDATE_TIME;
RIP_IFACE->timeout_time = RIP_DEFAULT_TIMEOUT_TIME;
RIP_IFACE->garbage_time = RIP_DEFAULT_GARBAGE_TIME;
};
rip_iface_finish:
{
RIP_IFACE->passwords = get_passwords();
if (!RIP_IFACE->auth_type != !RIP_IFACE->passwords)
log(L_WARN "Authentication and password options should be used together");
/* Default mode is broadcast for RIPv1, multicast for RIPv2 and RIPng */
if (!RIP_IFACE->mode)
RIP_IFACE->mode = (rip_cfg_is_v2() && (RIP_IFACE->version == RIP_V1)) ?
RIP_IM_BROADCAST : RIP_IM_MULTICAST;
RIP_CFG->min_timeout_time = MIN_(RIP_CFG->min_timeout_time, RIP_IFACE->timeout_time);
RIP_CFG->max_garbage_time = MAX_(RIP_CFG->max_garbage_time, RIP_IFACE->garbage_time);
};
rip_iface_item:
| METRIC expr { RIP_IPATT->metric = $2; }
| MODE rip_mode { RIP_IPATT->mode |= $2; }
| TX tos { RIP_IPATT->tx_tos = $2; }
| TX PRIORITY expr { RIP_IPATT->tx_priority = $3; }
| TTL SECURITY bool { RIP_IPATT->ttl_security = $3; }
| TTL SECURITY TX ONLY { RIP_IPATT->ttl_security = 2; }
METRIC expr { RIP_IFACE->metric = $2; if (($2<1) || ($2>255)) cf_error("Metric must be in range 1-255"); }
| MODE MULTICAST { RIP_IFACE->mode = RIP_IM_MULTICAST; }
| MODE BROADCAST { RIP_IFACE->mode = RIP_IM_BROADCAST; if (rip_cfg_is_ng()) cf_error("Broadcast not supported in RIPng"); }
| PASSIVE bool { RIP_IFACE->passive = $2; }
| ADDRESS ipa { RIP_IFACE->address = $2; }
| PORT expr { RIP_IFACE->port = $2; if (($2<1) || ($2>65535)) cf_error("Invalid port number"); }
| VERSION expr { RIP_IFACE->version = $2;
if (rip_cfg_is_ng()) cf_error("Version not supported in RIPng");
if (($2 != RIP_V1) && ($2 != RIP_V2)) cf_error("Unsupported version");
}
| VERSION ONLY bool { RIP_IFACE->version_only = $3; }
| SPLIT HORIZON bool { RIP_IFACE->split_horizon = $3; }
| POISON REVERSE bool { RIP_IFACE->poison_reverse = $3; }
| CHECK ZERO bool { RIP_IFACE->check_zero = $3; }
| UPDATE TIME expr { RIP_IFACE->update_time = $3; if ($3<=0) cf_error("Update time must be positive"); }
| TIMEOUT TIME expr { RIP_IFACE->timeout_time = $3; if ($3<=0) cf_error("Timeout time must be positive"); }
| GARBAGE TIME expr { RIP_IFACE->garbage_time = $3; if ($3<=0) cf_error("Garbage time must be positive"); }
| ECMP WEIGHT expr { RIP_IFACE->ecmp_weight = $3 - 1; if (($3<1) || ($3>256)) cf_error("ECMP weight must be in range 1-256"); }
| RX BUFFER expr { RIP_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX length must be in range 256-65535"); }
| TX LENGTH expr { RIP_IFACE->tx_length = $3; if (($3<256) || ($3>65535)) cf_error("TX length must be in range 256-65535"); }
| TX tos { RIP_IFACE->tx_tos = $2; }
| TX PRIORITY expr { RIP_IFACE->tx_priority = $3; }
| TTL SECURITY bool { RIP_IFACE->ttl_security = $3; }
| TTL SECURITY TX ONLY { RIP_IFACE->ttl_security = 2; }
| CHECK LINK bool { RIP_IFACE->check_link = $3; }
| BFD bool { RIP_IFACE->bfd = $2; cf_check_bfd($2); }
| AUTHENTICATION rip_auth { RIP_IFACE->auth_type = $2; if ($2) rip_check_auth(); }
| password_list { rip_check_auth(); }
;
rip_auth:
NONE { $$ = RIP_AUTH_NONE; }
| PLAINTEXT { $$ = RIP_AUTH_PLAIN; }
| CRYPTOGRAPHIC { $$ = RIP_AUTH_CRYPTO; }
| MD5 { $$ = RIP_AUTH_CRYPTO; }
;
rip_iface_opts:
rip_iface_opts:
/* empty */
| rip_iface_opts rip_iface_item ';'
;
@ -98,25 +160,22 @@ rip_iface_opt_list:
| '{' rip_iface_opts '}'
;
rip_iface_init:
/* EMPTY */ {
this_ipatt = cfg_allocz(sizeof(struct rip_patt));
add_tail(&RIP_CFG->iface_list, NODE this_ipatt);
init_list(&this_ipatt->ipn_list);
RIP_IPATT->metric = 1;
RIP_IPATT->tx_tos = IP_PREC_INTERNET_CONTROL;
RIP_IPATT->tx_priority = sk_priority_control;
RIP_IPATT->ttl_security = RIP_DEFAULT_TTL_SECURITY;
}
;
rip_iface:
rip_iface_start iface_patt_list_nopx rip_iface_opt_list rip_iface_finish;
rip_iface: /* TODO: switch to iface_patt_list_nopx */
rip_iface_init iface_patt_list rip_iface_opt_list
;
CF_ADDTO(dynamic_attr, RIP_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_RIP_METRIC); })
CF_ADDTO(dynamic_attr, RIP_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_RIP_TAG); })
CF_CLI_HELP(SHOW RIP, ..., [[Show information about RIP protocol]]);
CF_CLI(SHOW RIP INTERFACES, optsym opttext, [<name>] [\"<interface>\"], [[Show information about RIP interfaces]])
{ rip_show_interfaces(proto_get_named($4, &proto_rip), $5); };
CF_CLI(SHOW RIP NEIGHBORS, optsym opttext, [<name>] [\"<interface>\"], [[Show information about RIP neighbors]])
{ rip_show_neighbors(proto_get_named($4, &proto_rip), $5); };
CF_CODE
CF_END

770
proto/rip/packets.c Normal file
View File

@ -0,0 +1,770 @@
/*
* BIRD -- Routing Information Protocol (RIP)
*
* (c) 1998--1999 Pavel Machek <pavel@ucw.cz>
* (c) 2004--2013 Ondrej Filip <feela@network.cz>
* (c) 2009--2015 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2009--2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "rip.h"
#include "lib/md5.h"
#define RIP_CMD_REQUEST 1 /* want info */
#define RIP_CMD_RESPONSE 2 /* responding to request */
#define RIP_BLOCK_LENGTH 20
#define RIP_PASSWD_LENGTH 16
#define RIP_MD5_LENGTH 16
#define RIP_AF_IPV4 2
#define RIP_AF_AUTH 0xffff
/* RIP packet header */
struct rip_packet
{
u8 command;
u8 version;
u16 unused;
};
/* RTE block for RIPv2 */
struct rip_block_v2
{
u16 family;
u16 tag;
ip4_addr network;
ip4_addr netmask;
ip4_addr next_hop;
u32 metric;
};
/* RTE block for RIPng */
struct rip_block_ng
{
ip6_addr prefix;
u16 tag;
u8 pxlen;
u8 metric;
};
/* Authentication block for RIPv2 */
struct rip_block_auth
{
u16 must_be_ffff;
u16 auth_type;
char password[0];
u16 packet_len;
u8 key_id;
u8 auth_len;
u32 seq_num;
u32 unused1;
u32 unused2;
};
/* Authentication tail, RFC 4822 */
struct rip_auth_tail
{
u16 must_be_ffff;
u16 must_be_0001;
byte auth_data[];
};
/* Internal representation of RTE block data */
struct rip_block
{
ip_addr prefix;
int pxlen;
u32 metric;
u16 tag;
u16 no_af;
ip_addr next_hop;
};
#define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0)
#define DROP1(DSC) do { err_dsc = DSC; goto drop; } while(0)
#define SKIP(DSC) do { err_dsc = DSC; goto skip; } while(0)
#define LOG_PKT(msg, args...) \
log_rl(&p->log_pkt_tbf, L_REMOTE "%s: " msg, p->p.name, args)
#define LOG_PKT_AUTH(msg, args...) \
log_rl(&p->log_pkt_tbf, L_AUTH "%s: " msg, p->p.name, args)
#define LOG_RTE(msg, args...) \
log_rl(&p->log_rte_tbf, L_REMOTE "%s: " msg, p->p.name, args)
static inline void * rip_tx_buffer(struct rip_iface *ifa)
{ return ifa->sk->tbuf; }
static inline uint rip_pkt_hdrlen(struct rip_iface *ifa)
{ return sizeof(struct rip_packet) + (ifa->cf->auth_type ? RIP_BLOCK_LENGTH : 0); }
static inline void
rip_put_block(struct rip_proto *p, byte *pos, struct rip_block *rte)
{
if (rip_is_v2(p))
{
struct rip_block_v2 *block = (void *) pos;
block->family = rte->no_af ? 0 : htons(RIP_AF_IPV4);
block->tag = htons(rte->tag);
block->network = ip4_hton(ipa_to_ip4(rte->prefix));
block->netmask = ip4_hton(ip4_mkmask(rte->pxlen));
block->next_hop = ip4_hton(ipa_to_ip4(rte->next_hop));
block->metric = htonl(rte->metric);
}
else /* RIPng */
{
struct rip_block_ng *block = (void *) pos;
block->prefix = ip6_hton(ipa_to_ip6(rte->prefix));
block->tag = htons(rte->tag);
block->pxlen = rte->pxlen;
block->metric = rte->metric;
}
}
static inline void
rip_put_next_hop(struct rip_proto *p, byte *pos, struct rip_block *rte)
{
struct rip_block_ng *block = (void *) pos;
block->prefix = ip6_hton(ipa_to_ip6(rte->next_hop));
block->tag = 0;
block->pxlen = 0;
block->metric = 0xff;
}
static inline int
rip_get_block(struct rip_proto *p, byte *pos, struct rip_block *rte)
{
if (rip_is_v2(p))
{
struct rip_block_v2 *block = (void *) pos;
/* Skip blocks with strange AF, including authentication blocks */
if (block->family != (rte->no_af ? 0 : htons(RIP_AF_IPV4)))
return 0;
rte->prefix = ipa_from_ip4(ip4_ntoh(block->network));
rte->pxlen = ip4_masklen(ip4_ntoh(block->netmask));
rte->metric = ntohl(block->metric);
rte->tag = ntohs(block->tag);
rte->next_hop = ipa_from_ip4(ip4_ntoh(block->next_hop));
return 1;
}
else /* RIPng */
{
struct rip_block_ng *block = (void *) pos;
/* Handle and skip next hop blocks */
if (block->metric == 0xff)
{
rte->next_hop = ipa_from_ip6(ip6_ntoh(block->prefix));
if (!ipa_is_link_local(rte->next_hop)) rte->next_hop = IPA_NONE;
return 0;
}
rte->prefix = ipa_from_ip6(ip6_ntoh(block->prefix));
rte->pxlen = block->pxlen;
rte->metric = block->metric;
rte->tag = ntohs(block->tag);
/* rte->next_hop is deliberately kept unmodified */;
return 1;
}
}
static inline void
rip_update_csn(struct rip_proto *p UNUSED, struct rip_iface *ifa)
{
/*
* We update crypto sequence numbers at the beginning of update session to
* avoid issues with packet reordering, so packets inside one update session
* have the same CSN. We are using real time, but enforcing monotonicity.
*/
if (ifa->cf->auth_type == RIP_AUTH_CRYPTO)
ifa->csn = (ifa->csn < (u32) now_real) ? (u32) now_real : ifa->csn + 1;
}
static void
rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen)
{
struct rip_block_auth *auth = (void *) (pkt + 1);
struct password_item *pass = password_find(ifa->cf->passwords, 0);
if (!pass)
{
/* FIXME: This should not happen */
log(L_ERR "%s: No suitable password found for authentication", p->p.name);
memset(auth, 0, sizeof(struct rip_block_auth));
return;
}
switch (ifa->cf->auth_type)
{
case RIP_AUTH_PLAIN:
auth->must_be_ffff = htons(0xffff);
auth->auth_type = htons(RIP_AUTH_PLAIN);
strncpy(auth->password, pass->password, RIP_PASSWD_LENGTH);
return;
case RIP_AUTH_CRYPTO:
auth->must_be_ffff = htons(0xffff);
auth->auth_type = htons(RIP_AUTH_CRYPTO);
auth->packet_len = htons(*plen);
auth->key_id = pass->id;
auth->auth_len = sizeof(struct rip_auth_tail) + RIP_MD5_LENGTH;
auth->seq_num = ifa->csn_ready ? htonl(ifa->csn) : 0;
auth->unused1 = 0;
auth->unused2 = 0;
ifa->csn_ready = 1;
/*
* Note that RFC 4822 is unclear whether auth_len should cover whole
* authentication trailer or just auth_data length.
*
* Crypto sequence numbers are increased by sender in rip_update_csn().
* First CSN should be zero, this is handled by csn_ready.
*/
struct rip_auth_tail *tail = (void *) ((byte *) pkt + *plen);
tail->must_be_ffff = htons(0xffff);
tail->must_be_0001 = htons(0x0001);
strncpy(tail->auth_data, pass->password, RIP_MD5_LENGTH);
*plen += sizeof(struct rip_auth_tail) + RIP_MD5_LENGTH;
struct md5_context ctx;
md5_init(&ctx);
md5_update(&ctx, (byte *) pkt, *plen);
memcpy(tail->auth_data, md5_final(&ctx), RIP_MD5_LENGTH);
return;
default:
bug("Unknown authentication type");
}
}
static int
rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen, struct rip_neighbor *n)
{
struct rip_block_auth *auth = (void *) (pkt + 1);
struct password_item *pass = NULL;
const char *err_dsc = NULL;
uint err_val = 0;
uint auth_type = 0;
/* Check for authentication entry */
if ((*plen >= (sizeof(struct rip_packet) + sizeof(struct rip_block_auth))) &&
(auth->must_be_ffff == htons(0xffff)))
auth_type = ntohs(auth->auth_type);
if (auth_type != ifa->cf->auth_type)
DROP("authentication method mismatch", auth_type);
switch (auth_type)
{
case RIP_AUTH_NONE:
return 1;
case RIP_AUTH_PLAIN:
pass = password_find_by_value(ifa->cf->passwords, auth->password, RIP_PASSWD_LENGTH);
if (!pass)
DROP1("wrong password");
return 1;
case RIP_AUTH_CRYPTO:
pass = password_find_by_id(ifa->cf->passwords, auth->key_id);
if (!pass)
DROP("no suitable password found", auth->key_id);
uint data_len = ntohs(auth->packet_len);
uint auth_len = sizeof(struct rip_auth_tail) + RIP_MD5_LENGTH;
if (data_len + auth_len != *plen)
DROP("packet length mismatch", data_len);
if ((auth->auth_len != RIP_MD5_LENGTH) && (auth->auth_len != auth_len))
DROP("authentication data length mismatch", auth->auth_len);
struct rip_auth_tail *tail = (void *) ((byte *) pkt + data_len);
if ((tail->must_be_ffff != htons(0xffff)) || (tail->must_be_0001 != htons(0x0001)))
DROP1("authentication trailer is missing");
/* Accept higher sequence number, or zero if connectivity is lost */
/* FIXME: sequence number must be password/SA specific */
u32 rcv_csn = ntohl(auth->seq_num);
if ((rcv_csn < n->csn) && (rcv_csn || n->uc))
{
/* We want to report both new and old CSN */
LOG_PKT_AUTH("Authentication failed for %I on %s - "
"lower sequence number (rcv %u, old %u)",
n->nbr->addr, ifa->iface->name, rcv_csn, n->csn);
return 0;
}
char received[RIP_MD5_LENGTH];
memcpy(received, tail->auth_data, RIP_MD5_LENGTH);
strncpy(tail->auth_data, pass->password, RIP_MD5_LENGTH);
struct md5_context ctx;
md5_init(&ctx);
md5_update(&ctx, (byte *) pkt, *plen);
char *computed = md5_final(&ctx);
if (memcmp(received, computed, RIP_MD5_LENGTH))
DROP("wrong MD5 digest", pass->id);
*plen = data_len;
n->csn = rcv_csn;
return 1;
}
drop:
LOG_PKT_AUTH("Authentication failed for %I on %s - %s (%u)",
n->nbr->addr, ifa->iface->name, err_dsc, err_val);
return 0;
}
static inline int
rip_send_to(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, ip_addr dst)
{
if (ifa->cf->auth_type)
rip_fill_authentication(p, ifa, pkt, &plen);
return sk_send_to(ifa->sk, plen, dst, 0);
}
void
rip_send_request(struct rip_proto *p, struct rip_iface *ifa)
{
byte *pos = rip_tx_buffer(ifa);
struct rip_packet *pkt = (void *) pos;
pkt->command = RIP_CMD_REQUEST;
pkt->version = ifa->cf->version;
pkt->unused = 0;
pos += rip_pkt_hdrlen(ifa);
struct rip_block b = { .no_af = 1, .metric = p->infinity };
rip_put_block(p, pos, &b);
pos += RIP_BLOCK_LENGTH;
rip_update_csn(p, ifa);
TRACE(D_PACKETS, "Sending request via %s", ifa->iface->name);
rip_send_to(p, ifa, pkt, pos - (byte *) pkt, ifa->addr);
}
static void
rip_receive_request(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, struct rip_neighbor *from)
{
TRACE(D_PACKETS, "Request received from %I on %s", from->nbr->addr, ifa->iface->name);
byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa);
/* We expect one regular block */
if (plen != (rip_pkt_hdrlen(ifa) + RIP_BLOCK_LENGTH))
return;
struct rip_block b = { .no_af = 1 };
if (!rip_get_block(p, pos, &b))
return;
/* Special case - zero prefix, infinity metric */
if (ipa_nonzero(b.prefix) || b.pxlen || (b.metric != p->infinity))
return;
/* We do nothing if TX is already active */
if (ifa->tx_active)
{
TRACE(D_EVENTS, "Skipping request from %I on %s, TX is busy", from->nbr->addr, ifa->iface->name);
return;
}
if (!ifa->cf->passive)
rip_send_table(p, ifa, from->nbr->addr, 0);
}
static int
rip_send_response(struct rip_proto *p, struct rip_iface *ifa)
{
if (! ifa->tx_active)
return 0;
byte *pos = rip_tx_buffer(ifa);
byte *max = rip_tx_buffer(ifa) + ifa->tx_plen -
(rip_is_v2(p) ? RIP_BLOCK_LENGTH : 2*RIP_BLOCK_LENGTH);
ip_addr last_next_hop = IPA_NONE;
int send = 0;
struct rip_packet *pkt = (void *) pos;
pkt->command = RIP_CMD_RESPONSE;
pkt->version = ifa->cf->version;
pkt->unused = 0;
pos += rip_pkt_hdrlen(ifa);
FIB_ITERATE_START(&p->rtable, &ifa->tx_fit, z)
{
struct rip_entry *en = (struct rip_entry *) z;
/* Dummy entries */
if (!en->valid)
goto next_entry;
/* Stale entries that should be removed */
if ((en->valid == RIP_ENTRY_STALE) &&
((en->changed + ifa->cf->garbage_time) <= now))
goto next_entry;
/* Triggered updates */
if (en->changed < ifa->tx_changed)
goto next_entry;
/* Not enough space for current entry */
if (pos > max)
{
FIB_ITERATE_PUT(&ifa->tx_fit, z);
goto break_loop;
}
struct rip_block rte = {
.prefix = en->n.prefix,
.pxlen = en->n.pxlen,
.metric = en->metric,
.tag = en->tag
};
if (en->iface == ifa->iface)
rte.next_hop = en->next_hop;
if (rip_is_v2(p) && (ifa->cf->version == RIP_V1))
{
/* Skipping subnets (i.e. not hosts, classful networks or default route) */
if (ip4_masklen(ip4_class_mask(ipa_to_ip4(en->n.prefix))) != en->n.pxlen)
goto next_entry;
rte.tag = 0;
rte.pxlen = 0;
rte.next_hop = IPA_NONE;
}
/* Split horizon */
if (en->from == ifa->iface && ifa->cf->split_horizon)
{
if (ifa->cf->poison_reverse)
{
rte.metric = p->infinity;
rte.next_hop = IPA_NONE;
}
else
goto next_entry;
}
// TRACE(D_PACKETS, " %I/%d -> %I metric %d", rte.prefix, rte.pxlen, rte.next_hop, rte.metric);
/* RIPng next hop entry */
if (rip_is_ng(p) && !ipa_equal(rte.next_hop, last_next_hop))
{
last_next_hop = rte.next_hop;
rip_put_next_hop(p, pos, &rte);
pos += RIP_BLOCK_LENGTH;
}
rip_put_block(p, pos, &rte);
pos += RIP_BLOCK_LENGTH;
send = 1;
next_entry: ;
}
FIB_ITERATE_END(z);
ifa->tx_active = 0;
/* Do not send empty packet */
if (!send)
return 0;
break_loop:
TRACE(D_PACKETS, "Sending response via %s", ifa->iface->name);
return rip_send_to(p, ifa, pkt, pos - (byte *) pkt, ifa->tx_addr);
}
/**
* rip_send_table - RIP interface timer hook
* @p: RIP instance
* @ifa: RIP interface
* @addr: destination IP address
* @changed: time limit for triggered updates
*
* The function activates an update session and starts sending routing update
* packets (using rip_send_response()). The session may be finished during the
* call or may continue in rip_tx_hook() until all appropriate routes are
* transmitted. Note that there may be at most one active update session per
* interface, the function will terminate the old active session before
* activating the new one.
*/
void
rip_send_table(struct rip_proto *p, struct rip_iface *ifa, ip_addr addr, bird_clock_t changed)
{
DBG("RIP: Opening TX session to %I on %s\n", dst, ifa->iface->name);
rip_reset_tx_session(p, ifa);
ifa->tx_active = 1;
ifa->tx_addr = addr;
ifa->tx_changed = changed;
FIB_ITERATE_INIT(&ifa->tx_fit, &p->rtable);
rip_update_csn(p, ifa);
while (rip_send_response(p, ifa) > 0)
;
}
static void
rip_tx_hook(sock *sk)
{
struct rip_iface *ifa = sk->data;
struct rip_proto *p = ifa->rip;
DBG("RIP: TX hook called (iface %s, src %I, dst %I)\n",
sk->iface->name, sk->saddr, sk->daddr);
while (rip_send_response(p, ifa) > 0)
;
}
static void
rip_err_hook(sock *sk, int err)
{
struct rip_iface *ifa = sk->data;
struct rip_proto *p = ifa->rip;
log(L_ERR "%s: Socket error on %s: %M", p->p.name, ifa->iface->name, err);
rip_reset_tx_session(p, ifa);
}
static void
rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, struct rip_neighbor *from)
{
struct rip_block rte = {};
const char *err_dsc = NULL;
TRACE(D_PACKETS, "Response received from %I on %s", from->nbr->addr, ifa->iface->name);
byte *pos = (byte *) pkt + sizeof(struct rip_packet);
byte *end = (byte *) pkt + plen;
for (; pos < end; pos += RIP_BLOCK_LENGTH)
{
/* Find next regular RTE */
if (!rip_get_block(p, pos, &rte))
continue;
int c = ipa_classify_net(rte.prefix);
if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
SKIP("invalid prefix");
if (rip_is_v2(p) && (pkt->version == RIP_V1))
{
if (ifa->cf->check_zero && (rte.tag || rte.pxlen || ipa_nonzero(rte.next_hop)))
SKIP("RIPv1 reserved field is nonzero");
rte.tag = 0;
rte.pxlen = ip4_masklen(ip4_class_mask(ipa_to_ip4(rte.prefix)));
rte.next_hop = IPA_NONE;
}
if ((rte.pxlen < 0) || (rte.pxlen > MAX_PREFIX_LENGTH))
SKIP("invalid prefix length");
if (rte.metric > p->infinity)
SKIP("invalid metric");
if (ipa_nonzero(rte.next_hop))
{
neighbor *nbr = neigh_find2(&p->p, &rte.next_hop, ifa->iface, 0);
if (!nbr || (nbr->scope <= 0))
rte.next_hop = IPA_NONE;
}
// TRACE(D_PACKETS, " %I/%d -> %I metric %d", rte.prefix, rte.pxlen, rte.next_hop, rte.metric);
rte.metric += ifa->cf->metric;
if (rte.metric < p->infinity)
{
struct rip_rte new = {
.from = from,
.next_hop = ipa_nonzero(rte.next_hop) ? rte.next_hop : from->nbr->addr,
.metric = rte.metric,
.tag = rte.tag,
.expires = now + ifa->cf->timeout_time
};
rip_update_rte(p, &rte.prefix, rte.pxlen, &new);
}
else
rip_withdraw_rte(p, &rte.prefix, rte.pxlen, from);
continue;
skip:
LOG_RTE("Ignoring route %I/%d received from %I - %s",
rte.prefix, rte.pxlen, from->nbr->addr, err_dsc);
}
}
static int
rip_rx_hook(sock *sk, int len)
{
struct rip_iface *ifa = sk->data;
struct rip_proto *p = ifa->rip;
const char *err_dsc = NULL;
uint err_val = 0;
if (sk->lifindex != sk->iface->index)
return 1;
DBG("RIP: RX hook called (iface %s, src %I, dst %I)\n",
sk->iface->name, sk->faddr, sk->laddr);
/* Silently ignore my own packets */
/* FIXME: Better local address check */
if (ipa_equal(ifa->iface->addr->ip, sk->faddr))
return 1;
if (rip_is_ng(p) && !ipa_is_link_local(sk->faddr))
DROP1("wrong src address");
struct rip_neighbor *n = rip_get_neighbor(p, &sk->faddr, ifa);
if (!n)
DROP1("not from neighbor");
if ((ifa->cf->ttl_security == 1) && (sk->rcv_ttl < 255))
DROP("wrong TTL", sk->rcv_ttl);
if (sk->fport != sk->dport)
DROP("wrong src port", sk->fport);
if (len < sizeof(struct rip_packet))
DROP("too short", len);
if (sk->flags & SKF_TRUNCATED)
DROP("truncated", len);
struct rip_packet *pkt = (struct rip_packet *) sk->rbuf;
uint plen = len;
if (!pkt->version || (ifa->cf->version_only && (pkt->version != ifa->cf->version)))
DROP("wrong version", pkt->version);
/* rip_check_authentication() has its own error logging */
if (rip_is_v2(p) && !rip_check_authentication(p, ifa, pkt, &plen, n))
return 1;
if ((plen - sizeof(struct rip_packet)) % RIP_BLOCK_LENGTH)
DROP("invalid length", plen);
n->last_seen = now;
rip_update_bfd(p, n);
switch (pkt->command)
{
case RIP_CMD_REQUEST:
rip_receive_request(p, ifa, pkt, plen, n);
break;
case RIP_CMD_RESPONSE:
rip_receive_response(p, ifa, pkt, plen, n);
break;
default:
DROP("unknown command", pkt->command);
}
return 1;
drop:
LOG_PKT("Bad packet from %I via %s - %s (%u)",
sk->faddr, sk->iface->name, err_dsc, err_val);
return 1;
}
int
rip_open_socket(struct rip_iface *ifa)
{
struct rip_proto *p = ifa->rip;
sock *sk = sk_new(p->p.pool);
sk->type = SK_UDP;
sk->sport = ifa->cf->port;
sk->dport = ifa->cf->port;
sk->iface = ifa->iface;
/*
* For RIPv2, we explicitly choose a primary address, mainly to ensure that
* RIP and BFD uses the same one. For RIPng, we left it to kernel, which
* should choose some link-local address based on the same scope rule.
*/
if (rip_is_v2(p))
sk->saddr = ifa->iface->addr->ip;
sk->rx_hook = rip_rx_hook;
sk->tx_hook = rip_tx_hook;
sk->err_hook = rip_err_hook;
sk->data = ifa;
sk->tos = ifa->cf->tx_tos;
sk->priority = ifa->cf->tx_priority;
sk->ttl = ifa->cf->ttl_security ? 255 : 1;
sk->flags = SKF_LADDR_RX | ((ifa->cf->ttl_security == 1) ? SKF_TTL_RX : 0);
/* sk->rbsize and sk->tbsize are handled in rip_iface_update_buffers() */
if (sk_open(sk) < 0)
goto err;
if (ifa->cf->mode == RIP_IM_MULTICAST)
{
if (sk_setup_multicast(sk) < 0)
goto err;
if (sk_join_group(sk, ifa->addr) < 0)
goto err;
}
else /* Broadcast */
{
if (sk_setup_broadcast(sk) < 0)
goto err;
if (ipa_zero(ifa->addr))
{
sk->err = "Missing broadcast address";
goto err;
}
}
ifa->sk = sk;
return 1;
err:
sk_log_error(sk, p->p.name);
rfree(sk);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,185 +1,227 @@
/*
* Structures for RIP protocol
* BIRD -- Routing Information Protocol (RIP)
*
FIXME: in V6, they insert additional entry whenever next hop differs. Such entry is identified by 0xff in metric.
* (c) 1998--1999 Pavel Machek <pavel@ucw.cz>
* (c) 2004--2013 Ondrej Filip <feela@network.cz>
* (c) 2009--2015 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2009--2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_RIP_H_
#define _BIRD_RIP_H_
#include "nest/bird.h"
#include "nest/cli.h"
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
#include "nest/password.h"
#include "nest/locks.h"
#define EA_RIP_TAG EA_CODE(EAP_RIP, 0)
#define EA_RIP_METRIC EA_CODE(EAP_RIP, 1)
#define PACKET_MAX 25
#define PACKET_MD5_MAX 18 /* FIXME */
#include "nest/bfd.h"
#include "lib/lists.h"
#include "lib/resource.h"
#include "lib/socket.h"
#include "lib/string.h"
#include "lib/timer.h"
#define RIP_V1 1
#define RIP_V2 2
#define RIP_NG 1 /* A new version numbering */
#ifndef IPV6
#define RIP_PORT 520 /* RIP for IPv4 */
#ifdef IPV6
#define RIP_IS_V2 0
#else
#define RIP_PORT 521 /* RIPng */
#define RIP_IS_V2 1
#endif
struct rip_connection {
node n;
int num;
struct proto *proto;
ip_addr addr;
sock *send;
struct rip_interface *rif;
struct fib_iterator iter;
ip_addr daddr;
int dport;
int done;
};
struct rip_packet_heading { /* 4 bytes */
u8 command;
#define RIPCMD_REQUEST 1 /* want info */
#define RIPCMD_RESPONSE 2 /* responding to request */
#define RIPCMD_TRACEON 3 /* turn tracing on */
#define RIPCMD_TRACEOFF 4 /* turn it off */
#define RIPCMD_MAX 5
u8 version;
#define RIP_V1 1
#define RIP_V2 2
#define RIP_NG 1 /* this is verion 1 of RIPng */
u16 unused;
#define RIP_PORT 520 /* RIP for IPv4 */
#define RIP_NG_PORT 521 /* RIPng */
#define RIP_MAX_PKT_LENGTH 532 /* 512 + IP4_HEADER_LENGTH */
#define RIP_AUTH_TAIL_LENGTH 20 /* 4 + MD5 length */
#define RIP_DEFAULT_ECMP_LIMIT 16
#define RIP_DEFAULT_INFINITY 16
#define RIP_DEFAULT_UPDATE_TIME 30
#define RIP_DEFAULT_TIMEOUT_TIME 180
#define RIP_DEFAULT_GARBAGE_TIME 120
struct rip_config
{
struct proto_config c;
list patt_list; /* List of iface configs (struct rip_iface_config) */
u8 rip2; /* RIPv2 (IPv4) or RIPng (IPv6) */
u8 ecmp; /* Maximum number of nexthops in ECMP route, or 0 */
u8 infinity; /* Maximum metric value, representing infinity */
u32 min_timeout_time; /* Minimum of interface timeout_time */
u32 max_garbage_time; /* Maximum of interface garbage_time */
};
#ifndef IPV6
struct rip_block { /* 20 bytes */
u16 family; /* 0xffff on first message means this is authentication */
u16 tag;
ip_addr network;
ip_addr netmask;
ip_addr nexthop;
u32 metric;
};
#else
struct rip_block { /* IPv6 version!, 20 bytes, too */
ip_addr network;
u16 tag;
u8 pxlen;
u8 metric;
};
#endif
struct rip_block_auth { /* 20 bytes */
u16 mustbeFFFF;
u16 authtype;
u16 packetlen;
u8 keyid;
u8 authlen;
u32 seq;
u32 zero0;
u32 zero1;
};
struct rip_md5_tail { /* 20 bytes */
u16 mustbeFFFF;
u16 mustbe0001;
char md5[16];
};
struct rip_entry {
struct fib_node n;
ip_addr whotoldme;
ip_addr nexthop;
int metric;
u16 tag;
bird_clock_t updated, changed;
int flags;
};
struct rip_packet {
struct rip_packet_heading heading;
struct rip_block block[PACKET_MAX];
};
struct rip_interface {
node n;
struct proto *proto;
struct iface *iface;
sock *sock;
struct rip_connection *busy;
int metric; /* You don't want to put struct rip_patt *patt here -- think about reconfigure */
int mode;
int check_ttl; /* Check incoming packets for TTL 255 */
int triggered;
struct object_lock *lock;
int multicast;
};
struct rip_patt {
struct rip_iface_config
{
struct iface_patt i;
int metric; /* If you add entries here, don't forget to modify patt_compare! */
int mode;
#define IM_BROADCAST 2
#define IM_QUIET 4
#define IM_NOLISTEN 8
#define IM_VERSION1 16
ip_addr address; /* Configured dst address */
u16 port; /* Src+dst port */
u8 metric; /* Incoming metric */
u8 mode; /* Interface mode (RIP_IM_*) */
u8 passive; /* Passive iface - no packets are sent */
u8 version; /* RIP version used for outgoing packets */
u8 version_only; /* FIXXX */
u8 split_horizon; /* Split horizon is used in route updates */
u8 poison_reverse; /* Poisoned reverse is used in route updates */
u8 check_zero; /* Validation of RIPv1 reserved fields */
u8 ecmp_weight; /* Weight for ECMP routes*/
u8 auth_type; /* Authentication type (RIP_AUTH_*) */
u8 ttl_security; /* bool + 2 for TX only (send, but do not check on RX) */
u8 check_link; /* Whether iface link change is used */
u8 bfd; /* Use BFD on iface */
u16 rx_buffer; /* RX buffer size, 0 for MTU */
u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */
int tx_tos;
int tx_priority;
int ttl_security; /* bool + 2 for TX only (send, but do not check on RX) */
u32 update_time; /* Periodic update interval */
u32 timeout_time; /* Route expiration timeout */
u32 garbage_time; /* Unreachable entry GC timeout */
list *passwords; /* Passwords for authentication */
};
struct rip_proto_config {
struct proto_config c;
list iface_list; /* Patterns configured -- keep it first; see rip_reconfigure why */
list *passwords; /* Passwords, keep second */
struct rip_proto
{
struct proto p;
struct fib rtable; /* Internal routing table */
list iface_list; /* List of interfaces (struct rip_iface) */
slab *rte_slab; /* Slab for internal routes (struct rip_rte) */
timer *timer; /* Main protocol timer */
int infinity; /* User configurable data; must be comparable with memcmp */
int port;
int period;
int garbage_time;
int timeout_time;
u8 ecmp; /* Maximum number of nexthops in ECMP route, or 0 */
u8 infinity; /* Maximum metric value, representing infinity */
u8 triggered; /* Logical AND of interface want_triggered values */
u8 rt_reload; /* Route reload is scheduled */
int authtype;
#define AT_NONE 0
#define AT_PLAINTEXT 2
#define AT_MD5 3
int honor;
#define HO_NEVER 0
#define HO_NEIGHBOR 1
#define HO_ALWAYS 2
struct tbf log_pkt_tbf; /* TBF for packet messages */
struct tbf log_rte_tbf; /* TBF for RTE messages */
};
struct rip_proto {
struct proto inherited;
timer *timer;
list connections;
struct fib rtable;
list garbage;
list interfaces; /* Interfaces we really know about */
#ifdef LOCAL_DEBUG
int magic;
struct rip_iface
{
node n;
struct rip_proto *rip;
struct iface *iface; /* Underyling core interface */
struct rip_iface_config *cf; /* Related config, must be updated in reconfigure */
struct object_lock *lock; /* Interface lock */
timer *timer; /* Interface timer */
sock *sk; /* UDP socket */
u8 up; /* Interface is active */
u8 csn_ready; /* Nonzero CSN can be used */
u16 tx_plen; /* Max TX packet data length */
u32 csn; /* Last used crypto sequence number */
ip_addr addr; /* Destination multicast/broadcast address */
list neigh_list; /* List of iface neighbors (struct rip_neighbor) */
/* Update scheduling */
bird_clock_t next_regular; /* Next time when regular update should be called */
bird_clock_t next_triggered; /* Next time when triggerd update may be called */
bird_clock_t want_triggered; /* Nonzero if triggered update is scheduled */
/* Active update */
int tx_active; /* Update session is active */
ip_addr tx_addr; /* Update session destination address */
bird_clock_t tx_changed; /* Minimal changed time for triggered update */
struct fib_iterator tx_fit; /* FIB iterator in RIP routing table (p.rtable) */
};
struct rip_neighbor
{
node n;
struct rip_iface *ifa; /* Associated interface, may be NULL if stale */
struct neighbor *nbr; /* Associaded core neighbor, may be NULL if stale */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
bird_clock_t last_seen; /* Time of last received and accepted message */
u32 uc; /* Use count, number of routes linking the neighbor */
u32 csn; /* Last received crypto sequence number */
};
struct rip_entry
{
struct fib_node n;
struct rip_rte *routes; /* List of incoming routes */
u8 valid; /* Entry validity state (RIP_ENTRY_*) */
u8 metric; /* Outgoing route metric */
u16 tag; /* Outgoing route tag */
struct iface *from; /* Outgoing route from, NULL if from proto */
struct iface *iface; /* Outgoing route iface (for next hop) */
ip_addr next_hop; /* Outgoing route next hop */
bird_clock_t changed; /* Last time when the outgoing route metric changed */
};
struct rip_rte
{
struct rip_rte *next;
struct rip_neighbor *from; /* Advertising router */
ip_addr next_hop; /* Route next hop (iface is from->nbr->iface) */
u16 metric; /* Route metric (after increase) */
u16 tag; /* Route tag */
bird_clock_t expires; /* Time of route expiration */
};
#define RIP_AUTH_NONE 0
#define RIP_AUTH_PLAIN 2
#define RIP_AUTH_CRYPTO 3
#define RIP_IM_MULTICAST 1
#define RIP_IM_BROADCAST 2
#define RIP_ENTRY_DUMMY 0 /* Only used to store list of incoming routes */
#define RIP_ENTRY_VALID 1 /* Valid outgoing route */
#define RIP_ENTRY_STALE 2 /* Stale outgoing route, waiting for GC */
#define EA_RIP_METRIC EA_CODE(EAP_RIP, 0)
#define EA_RIP_TAG EA_CODE(EAP_RIP, 1)
#define rip_is_v2(X) RIP_IS_V2
#define rip_is_ng(X) (!RIP_IS_V2)
/*
static inline int rip_is_v2(struct rip_proto *p)
{ return p->rip2; }
static inline int rip_is_ng(struct rip_proto *p)
{ return ! p->rip2; }
*/
static inline void
rip_reset_tx_session(struct rip_proto *p, struct rip_iface *ifa)
{
if (ifa->tx_active)
{
FIB_ITERATE_UNLINK(&ifa->tx_fit, &p->rtable);
ifa->tx_active = 0;
}
}
/* rip.c */
void rip_update_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_rte *new);
void rip_withdraw_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_neighbor *from);
struct rip_neighbor * rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa);
void rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n);
void rip_show_interfaces(struct proto *P, char *iff);
void rip_show_neighbors(struct proto *P, char *iff);
/* packets.c */
void rip_send_request(struct rip_proto *p, struct rip_iface *ifa);
void rip_send_table(struct rip_proto *p, struct rip_iface *ifa, ip_addr addr, bird_clock_t changed);
int rip_open_socket(struct rip_iface *ifa);
#endif
int tx_count; /* Do one regular update once in a while */
int rnd_count; /* Randomize sending time */
};
#ifdef LOCAL_DEBUG
#define RIP_MAGIC 81861253
#define CHK_MAGIC do { if (P->magic != RIP_MAGIC) bug( "Not enough magic" ); } while (0)
#else
#define CHK_MAGIC do { } while (0)
#endif
void rip_init_config(struct rip_proto_config *c);
/* Authentication functions */
int rip_incoming_authentication( struct proto *p, struct rip_block_auth *block, struct rip_packet *packet, int num, ip_addr whotoldme );
int rip_outgoing_authentication( struct proto *p, struct rip_block_auth *block, struct rip_packet *packet, int num );

View File

@ -14,11 +14,24 @@ CF_DEFINES
#define STATIC_CFG ((struct static_config *) this_proto)
static struct static_route *this_srt, *this_srt_nh, *last_srt_nh;
static struct f_inst **this_srt_last_cmd;
static void
static_route_finish(void)
{
struct static_route *r;
/* Update undefined use_bfd entries in multipath nexthops */
if (this_srt->dest == RTD_MULTIPATH)
for (r = this_srt->mp_next; r; r = r->mp_next)
if (r->use_bfd < 0)
r->use_bfd = this_srt->use_bfd;
}
CF_DECLS
CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK)
CF_KEYWORDS(MULTIPATH, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE)
CF_KEYWORDS(MULTIPATH, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD)
CF_GRAMMAR
@ -36,7 +49,7 @@ static_proto:
| static_proto proto_item ';'
| static_proto CHECK LINK bool ';' { STATIC_CFG->check_link = $4; }
| static_proto IGP TABLE rtable ';' { STATIC_CFG->igp_table = $4; }
| static_proto stat_route ';'
| static_proto stat_route stat_route_opt_list ';' { static_route_finish(); }
;
stat_route0: ROUTE prefix {
@ -44,6 +57,7 @@ stat_route0: ROUTE prefix {
add_tail(&STATIC_CFG->other_routes, &this_srt->n);
this_srt->net = $2.addr;
this_srt->masklen = $2.len;
this_srt_last_cmd = &(this_srt->cmds);
}
;
@ -55,11 +69,15 @@ stat_multipath1:
this_srt_nh->via = $2;
this_srt_nh->via_if = $3;
this_srt_nh->if_name = (void *) this_srt; /* really */
this_srt_nh->use_bfd = -1; /* undefined */
}
| stat_multipath1 WEIGHT expr {
this_srt_nh->masklen = $3 - 1; /* really */
if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256");
}
| stat_multipath1 BFD bool {
this_srt_nh->use_bfd = $3; cf_check_bfd($3);
}
;
stat_multipath:
@ -94,6 +112,22 @@ stat_route:
| stat_route0 PROHIBIT { this_srt->dest = RTD_PROHIBIT; }
;
stat_route_item:
cmd { *this_srt_last_cmd = $1; this_srt_last_cmd = &($1->next); }
| BFD bool ';' { this_srt->use_bfd = $2; cf_check_bfd($2); }
;
stat_route_opts:
/* empty */
| stat_route_opts stat_route_item
;
stat_route_opt_list:
/* empty */
| '{' stat_route_opts '}'
;
CF_CLI(SHOW STATIC, optsym, [<name>], [[Show details of static protocol]])
{ static_show(proto_get_named($3, &proto_static)); } ;

View File

@ -42,11 +42,14 @@
#include "nest/route.h"
#include "nest/cli.h"
#include "conf/conf.h"
#include "filter/filter.h"
#include "lib/string.h"
#include "lib/alloca.h"
#include "static.h"
static linpool *static_lp;
static inline rtable *
p_igp_table(struct proto *p)
{
@ -54,12 +57,11 @@ p_igp_table(struct proto *p)
return cf->igp_table ? cf->igp_table->table : p->table;
}
static void
static_install(struct proto *p, struct static_route *r, struct iface *ifa)
{
net *n;
rta a, *aa;
rta a;
rte *e;
if (r->installed > 0)
@ -108,13 +110,21 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa)
if (r->dest == RTDX_RECURSIVE)
rta_set_recursive_next_hop(p->table, &a, p_igp_table(p), &r->via, &r->via);
aa = rta_lookup(&a);
/* We skip rta_lookup() here */
n = net_get(p->table, r->net, r->masklen);
e = rte_get_temp(aa);
e = rte_get_temp(&a);
e->net = n;
e->pflags = 0;
if (r->cmds)
f_eval_rte(r->cmds, &e, static_lp);
rte_update(p, n, e);
r->installed = 1;
if (r->cmds)
lp_flush(static_lp);
}
static void
@ -131,6 +141,29 @@ static_remove(struct proto *p, struct static_route *r)
r->installed = 0;
}
static void
static_bfd_notify(struct bfd_request *req);
static void
static_update_bfd(struct proto *p, struct static_route *r)
{
struct neighbor *nb = r->neigh;
int bfd_up = (nb->scope > 0) && r->use_bfd;
if (bfd_up && !r->bfd_req)
{
// ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip;
r->bfd_req = bfd_request_session(p->pool, r->via, nb->ifa->ip, nb->iface,
static_bfd_notify, r);
}
if (!bfd_up && r->bfd_req)
{
rfree(r->bfd_req);
r->bfd_req = NULL;
}
}
static int
static_decide(struct static_config *cf, struct static_route *r)
{
@ -143,6 +176,9 @@ static_decide(struct static_config *cf, struct static_route *r)
if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP))
return 0;
if (r->bfd_req && r->bfd_req->state != BFD_STATE_UP)
return 0;
return 1;
}
@ -161,6 +197,8 @@ static_add(struct proto *p, struct static_config *cf, struct static_route *r)
r->chain = n->data;
n->data = r;
r->neigh = n;
static_update_bfd(p, r);
if (static_decide(cf, r))
static_install(p, r, n->iface);
else
@ -190,6 +228,8 @@ static_add(struct proto *p, struct static_config *cf, struct static_route *r)
r2->chain = n->data;
n->data = r2;
r2->neigh = n;
static_update_bfd(p, r2);
r2->installed = static_decide(cf, r2);
count += r2->installed;
}
@ -212,6 +252,26 @@ static_add(struct proto *p, struct static_config *cf, struct static_route *r)
}
}
static void
static_rte_cleanup(struct proto *p, struct static_route *r)
{
struct static_route *r2;
if (r->bfd_req)
{
rfree(r->bfd_req);
r->bfd_req = NULL;
}
if (r->dest == RTD_MULTIPATH)
for (r2 = r->mp_next; r2; r2 = r2->mp_next)
if (r2->bfd_req)
{
rfree(r2->bfd_req);
r2->bfd_req = NULL;
}
}
static int
static_start(struct proto *p)
{
@ -220,6 +280,9 @@ static_start(struct proto *p)
DBG("Static: take off!\n");
if (!static_lp)
static_lp = lp_new(&root_pool, 1008);
if (cf->igp_table)
rt_lock_table(cf->igp_table->table);
@ -241,7 +304,10 @@ static_shutdown(struct proto *p)
WALK_LIST(r, cf->iface_routes)
r->installed = 0;
WALK_LIST(r, cf->other_routes)
{
static_rte_cleanup(p, r);
r->installed = 0;
}
return PS_DOWN;
}
@ -255,6 +321,44 @@ static_cleanup(struct proto *p)
rt_unlock_table(cf->igp_table->table);
}
static void
static_update_rte(struct proto *p, struct static_route *r)
{
switch (r->dest)
{
case RTD_ROUTER:
if (static_decide((struct static_config *) p->cf, r))
static_install(p, r, r->neigh->iface);
else
static_remove(p, r);
break;
case RTD_NONE: /* a part of multipath route */
{
int decision = static_decide((struct static_config *) p->cf, r);
if (decision == r->installed)
break; /* no change */
r->installed = decision;
struct static_route *r1, *r2;
int count = 0;
r1 = (void *) r->if_name; /* really */
for (r2 = r1->mp_next; r2; r2 = r2->mp_next)
count += r2->installed;
if (count)
{
/* Set of nexthops changed - force reinstall */
r1->installed = 0;
static_install(p, r1, NULL);
}
else
static_remove(p, r1);
break;
}
}
}
static void
static_neigh_notify(struct neighbor *n)
@ -264,40 +368,21 @@ static_neigh_notify(struct neighbor *n)
DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
for(r=n->data; r; r=r->chain)
switch (r->dest)
{
case RTD_ROUTER:
if (static_decide((struct static_config *) p->cf, r))
static_install(p, r, n->iface);
else
static_remove(p, r);
break;
{
static_update_bfd(p, r);
static_update_rte(p, r);
}
}
case RTD_NONE: /* a part of multipath route */
{
int decision = static_decide((struct static_config *) p->cf, r);
if (decision == r->installed)
break; /* no change */
r->installed = decision;
static void
static_bfd_notify(struct bfd_request *req)
{
struct static_route *r = req->data;
struct proto *p = r->neigh->proto;
struct static_route *r1, *r2;
int count = 0;
r1 = (void *) r->if_name; /* really */
for (r2 = r1->mp_next; r2; r2 = r2->mp_next)
count += r2->installed;
// if (req->down) TRACE(D_EVENTS, "BFD session down for nbr %I on %s", XXXX);
if (count)
{
/* Set of nexthops changed - force reinstall */
r1->installed = 0;
static_install(p, r1, NULL);
}
else
static_remove(p, r1);
break;
}
}
static_update_rte(p, r);
}
static void
@ -352,6 +437,12 @@ static_if_notify(struct proto *p, unsigned flags, struct iface *i)
}
}
int
static_rte_mergable(rte *pri, rte *sec)
{
return 1;
}
void
static_init_config(struct static_config *c)
{
@ -366,6 +457,7 @@ static_init(struct proto_config *c)
p->neigh_notify = static_neigh_notify;
p->if_notify = static_if_notify;
p->rte_mergable = static_rte_mergable;
return p;
}
@ -394,7 +486,7 @@ static_same_dest(struct static_route *x, struct static_route *y)
for (x = x->mp_next, y = y->mp_next;
x && y;
x = x->mp_next, y = y->mp_next)
if (!ipa_equal(x->via, y->via) || (x->via_if != y->via_if))
if (!ipa_equal(x->via, y->via) || (x->via_if != y->via_if) || (x->use_bfd != y->use_bfd))
return 0;
return !x && !y;
@ -406,6 +498,13 @@ static_same_dest(struct static_route *x, struct static_route *y)
}
}
static inline int
static_same_rte(struct static_route *x, struct static_route *y)
{
return static_same_dest(x, y) && i_same(x->cmds, y->cmds);
}
static void
static_match(struct proto *p, struct static_route *r, struct static_config *n)
{
@ -434,7 +533,7 @@ static_match(struct proto *p, struct static_route *r, struct static_config *n)
found:
/* If destination is different, force reinstall */
if ((r->installed > 0) && !static_same_dest(r, t))
if ((r->installed > 0) && !static_same_rte(r, t))
t->installed = -1;
else
t->installed = r->installed;
@ -472,6 +571,9 @@ static_reconfigure(struct proto *p, struct proto_config *new)
WALK_LIST(r, n->other_routes)
static_add(p, n, r);
WALK_LIST(r, o->other_routes)
static_rte_cleanup(p, r);
return 1;
}
@ -557,13 +659,14 @@ static_show_rt(struct static_route *r)
case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break;
default: bsprintf(via, "???");
}
cli_msg(-1009, "%I/%d %s%s", r->net, r->masklen, via, r->installed ? "" : " (dormant)");
cli_msg(-1009, "%I/%d %s%s%s", r->net, r->masklen, via,
r->bfd_req ? " (bfd)" : "", r->installed ? "" : " (dormant)");
struct static_route *r2;
if (r->dest == RTD_MULTIPATH)
for (r2 = r->mp_next; r2; r2 = r2->mp_next)
cli_msg(-1009, "\tvia %I%J weight %d%s", r2->via, r2->via_if, r2->masklen + 1, /* really */
r2->installed ? "" : " (dormant)");
cli_msg(-1009, "\tvia %I%J weight %d%s%s", r2->via, r2->via_if, r2->masklen + 1, /* really */
r2->bfd_req ? " (bfd)" : "", r2->installed ? "" : " (dormant)");
}
void

View File

@ -9,6 +9,9 @@
#ifndef _BIRD_STATIC_H_
#define _BIRD_STATIC_H_
#include "nest/route.h"
#include "nest/bfd.h"
struct static_config {
struct proto_config c;
list iface_routes; /* Routes to search on interface events */
@ -31,7 +34,10 @@ struct static_route {
struct neighbor *neigh;
byte *if_name; /* Name for RTD_DEVICE routes */
struct static_route *mp_next; /* Nexthops for RTD_MULTIPATH routes */
struct f_inst *cmds; /* List of commands for setting attributes */
int installed; /* Installed in rt table, -1 for reinstall */
int use_bfd; /* Configured to use BFD */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
};
/* Dummy nodes (parts of multipath route) abuses masklen field for weight

View File

@ -44,6 +44,7 @@
#undef CONFIG_OSPF
#undef CONFIG_PIPE
#undef CONFIG_BGPSEC
#undef CONFIG_BABEL
/* We use multithreading */
#undef USE_PTHREADS

View File

@ -2,3 +2,4 @@ krt-sock.c
krt-sock.Y
krt-sys.h
sysio.h
setkey.h

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