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

Merge branch 'master' into mrtdump

This commit is contained in:
Pavel Tvrdik 2016-05-02 14:00:21 +02:00
commit 1dea356ac9
50 changed files with 3036 additions and 1515 deletions

View File

@ -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
@ -92,10 +92,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->mrt_proto_file = -1; /* Indication that the file descriptor should not be used */
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};
@ -120,11 +125,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);
@ -138,7 +145,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;
}
/**
@ -151,14 +163,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;
}
/**
@ -238,10 +258,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++;
@ -255,9 +271,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

@ -178,7 +178,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

View File

@ -318,8 +318,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
@ -1158,9 +1159,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/.
@ -1898,6 +1902,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
@ -2093,7 +2102,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
@ -2302,7 +2311,7 @@ these attributes:
<tag>ip <cf/krt_prefsrc/</tag> (Linux)
The preferred source address. Used in source address selection for
outgoing packets. Has to be one of the 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.
@ -2515,7 +2524,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.
@ -2647,8 +2656,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.
@ -3283,16 +3292,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
@ -3300,39 +3307,156 @@ 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>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
@ -3348,43 +3472,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
@ -3395,27 +3507,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; };

View File

@ -55,6 +55,8 @@ 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
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

@ -1091,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;

View File

@ -32,6 +32,7 @@
#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)))

View File

@ -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,8 +15,11 @@
#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)
@ -32,6 +35,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

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))
@ -62,7 +75,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 *);

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;

View File

@ -28,6 +28,7 @@ typedef struct birdsock {
struct iface *iface; /* Interface; specify this for broad/multicast sockets */
byte *rbuf, *rpos; /* NULL=allocate automatically */
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 */

View File

@ -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

@ -35,6 +35,7 @@ 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);

View File

@ -113,7 +113,7 @@ idval:
$$ = ipa_to_u32(SYM_VAL($1).px.ip);
#endif
else
cf_error("Number of IPv4 address constant expected");
cf_error("Number or IPv4 address constant expected");
}
;

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 */
@ -1260,6 +1260,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);
}

View File

@ -75,6 +75,8 @@ void fib_check(const struct fib *); /* Consistency check for debugging */
void fit_init(struct fib_iterator *, const struct fib *); /* Internal functions, don't call */
struct fib_node *fit_get(const 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; \
@ -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
@ -175,7 +182,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 */
@ -197,10 +204,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
@ -218,8 +224,8 @@ typedef struct rte {
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;
@ -508,19 +514,25 @@ 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++; }

View File

@ -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
/**

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

@ -897,12 +897,6 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *sr
}
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;
@ -1672,7 +1666,7 @@ rt_prune_loop(void)
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);
@ -1883,6 +1877,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);
@ -1919,7 +1914,7 @@ rt_commit(struct config *new, struct config *old)
mrt_table_dump_stop_periodic(ot);
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);

View File

@ -872,7 +872,7 @@ 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);

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

@ -376,6 +376,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;
@ -668,6 +670,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
@ -698,6 +704,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;
}
@ -815,7 +822,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);
@ -825,6 +838,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",

View File

@ -171,6 +171,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)
{
@ -231,14 +239,6 @@ bgp_put_cap_err(struct bgp_proto *p UNUSED, byte *buf)
return buf;
}
static byte *
bgp_put_cap_ext_msg(struct bgp_proto *p UNUSED, byte *buf)
{
*buf++ = 230; /* Capability TBD: Support for extended messages */
*buf++ = 0; /* Capability data length */
return buf;
}
static byte *
bgp_create_open(struct bgp_conn *conn, byte *buf)
@ -835,6 +835,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;
@ -875,12 +881,6 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len)
conn->peer_enhanced_refresh_support = 1;
break;
case 230: /* Extended message length capability, draft, cap number TBD */
if (cl != 0)
goto err;
conn->peer_ext_messages_support = 1;
break;
/* We can safely ignore all other capabilities */
}
len -= 2 + cl;

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)

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

@ -109,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 md5_context ctxt;
md5_init(&ctxt);
md5_update(&ctxt, (char *) pkt, plen);
md5_update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE);
memcpy((byte *) tail, md5_final(&ctxt), MD5_SIZE);
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:
@ -175,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 md5_context ctxt;
md5_init(&ctxt);
md5_update(&ctxt, (char *) pkt, plen);
md5_update(&ctxt, passwd, OSPF_AUTH_CRYPT_SIZE);
memcpy(md5sum, md5_final(&ctxt), MD5_SIZE);
if (memcmp(md5sum, tail, OSPF_AUTH_CRYPT_SIZE))
if (memcmp(received, computed, OSPF_AUTH_CRYPT_SIZE))
DROP("wrong MD5 digest", pass->id);
if (n)
@ -224,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;
@ -232,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

@ -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)

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,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,188 +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"
#include "nest/iface.h"
#include "nest/bfd.h"
#include "lib/lists.h"
#include "lib/resource.h"
#include "lib/socket.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 "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

@ -493,9 +493,8 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
e->net = net;
e->u.krt.src = src;
e->u.krt.proto = src2;
/* These are probably too Linux-specific */
e->u.krt.type = 0;
e->u.krt.seen = 0;
e->u.krt.best = 0;
e->u.krt.metric = 0;
if (scan)
@ -970,13 +969,15 @@ krt_sock_close_shared(void)
}
}
void
int
krt_sys_start(struct krt_proto *p)
{
krt_table_map[KRT_CF->sys.table_id] = p;
krt_sock_open_shared();
p->sys.sk = krt_sock;
return 1;
}
void
@ -992,10 +993,11 @@ krt_sys_shutdown(struct krt_proto *p)
#else
void
int
krt_sys_start(struct krt_proto *p)
{
p->sys.sk = krt_sock_open(p->p.pool, p, KRT_CF->sys.table_id);
return 1;
}
void

View File

@ -42,6 +42,7 @@ struct krt_state {
};
static inline void krt_sys_io_init(void) { }
static inline void krt_sys_init(struct krt_proto *p UNUSED) { }
static inline int krt_sys_get_attr(eattr *a UNUSED, byte *buf UNUSED, int buflen UNUSED) { }

View File

@ -84,18 +84,18 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i) { return NULL; }
#define EA_KRT_FEATURE_ALLFRAG EA_KRT_FEATURES | EA_BIT(0x3)
#define NL_NUM_TABLES 256
struct krt_params {
int table_id; /* Kernel table ID we sync with */
u32 table_id; /* Kernel table ID we sync with */
};
struct krt_state {
struct krt_proto *hash_next;
};
static inline void krt_sys_init(struct krt_proto *p UNUSED) { }
static inline void krt_sys_preconfig(struct config *c UNUSED) { }
static inline void krt_sys_postconfig(struct krt_config *x UNUSED) { }
#endif

View File

@ -23,8 +23,6 @@ CF_ADDTO(kern_proto, kern_proto kern_sys_item ';')
kern_sys_item:
KERNEL TABLE expr {
if ($3 <= 0 || $3 >= NL_NUM_TABLES)
cf_error("Kernel routing table number out of range");
THIS_KRT->sys.table_id = $3;
}
;

View File

@ -25,6 +25,7 @@
#include "lib/krt.h"
#include "lib/socket.h"
#include "lib/string.h"
#include "lib/hash.h"
#include "conf/conf.h"
#include <asm/types.h>
@ -32,6 +33,7 @@
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#ifndef MSG_TRUNC /* Hack: Several versions of glibc miss this one :( */
#define MSG_TRUNC 0x20
#endif
@ -40,6 +42,11 @@
#define IFF_LOWER_UP 0x10000
#endif
#ifndef RTA_TABLE
#define RTA_TABLE 15
#endif
/*
* Synchronous Netlink interface
*/
@ -219,26 +226,110 @@ nl_checkin(struct nlmsghdr *h, int lsize)
return NLMSG_DATA(h);
}
struct nl_want_attrs {
u8 defined:1;
u8 checksize:1;
u8 size;
};
#define BIRD_IFLA_MAX (IFLA_WIRELESS+1)
static struct nl_want_attrs ifla_attr_want[BIRD_IFLA_MAX] = {
[IFLA_IFNAME] = { 1, 0, 0 },
[IFLA_MTU] = { 1, 1, sizeof(u32) },
[IFLA_WIRELESS] = { 1, 0, 0 },
};
#define BIRD_IFA_MAX (IFA_ANYCAST+1)
#ifndef IPV6
static struct nl_want_attrs ifa_attr_want4[BIRD_IFA_MAX] = {
[IFA_ADDRESS] = { 1, 1, sizeof(ip4_addr) },
[IFA_LOCAL] = { 1, 1, sizeof(ip4_addr) },
[IFA_BROADCAST] = { 1, 1, sizeof(ip4_addr) },
};
#else
static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MAX] = {
[IFA_ADDRESS] = { 1, 1, sizeof(ip6_addr) },
[IFA_LOCAL] = { 1, 1, sizeof(ip6_addr) },
};
#endif
#define BIRD_RTA_MAX (RTA_TABLE+1)
static struct nl_want_attrs mpnh_attr_want4[BIRD_RTA_MAX] = {
[RTA_GATEWAY] = { 1, 1, sizeof(ip4_addr) },
};
#ifndef IPV6
static struct nl_want_attrs rtm_attr_want4[BIRD_RTA_MAX] = {
[RTA_DST] = { 1, 1, sizeof(ip4_addr) },
[RTA_OIF] = { 1, 1, sizeof(u32) },
[RTA_GATEWAY] = { 1, 1, sizeof(ip4_addr) },
[RTA_PRIORITY] = { 1, 1, sizeof(u32) },
[RTA_PREFSRC] = { 1, 1, sizeof(ip4_addr) },
[RTA_METRICS] = { 1, 0, 0 },
[RTA_MULTIPATH] = { 1, 0, 0 },
[RTA_FLOW] = { 1, 1, sizeof(u32) },
[RTA_TABLE] = { 1, 1, sizeof(u32) },
};
#else
static struct nl_want_attrs rtm_attr_want6[BIRD_RTA_MAX] = {
[RTA_DST] = { 1, 1, sizeof(ip6_addr) },
[RTA_IIF] = { 1, 1, sizeof(u32) },
[RTA_OIF] = { 1, 1, sizeof(u32) },
[RTA_GATEWAY] = { 1, 1, sizeof(ip6_addr) },
[RTA_PRIORITY] = { 1, 1, sizeof(u32) },
[RTA_PREFSRC] = { 1, 1, sizeof(ip6_addr) },
[RTA_METRICS] = { 1, 0, 0 },
[RTA_FLOW] = { 1, 1, sizeof(u32) },
[RTA_TABLE] = { 1, 1, sizeof(u32) },
};
#endif
static int
nl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize)
nl_parse_attrs(struct rtattr *a, struct nl_want_attrs *want, struct rtattr **k, int ksize)
{
int max = ksize / sizeof(struct rtattr *);
bzero(k, ksize);
while (RTA_OK(a, nl_attr_len))
for ( ; RTA_OK(a, nl_attr_len); a = RTA_NEXT(a, nl_attr_len))
{
if (a->rta_type < max)
k[a->rta_type] = a;
a = RTA_NEXT(a, nl_attr_len);
if ((a->rta_type >= max) || !want[a->rta_type].defined)
continue;
if (want[a->rta_type].checksize && (RTA_PAYLOAD(a) != want[a->rta_type].size))
{
log(L_ERR "nl_parse_attrs: Malformed message received");
return 0;
}
k[a->rta_type] = a;
}
if (nl_attr_len)
{
log(L_ERR "nl_parse_attrs: remnant of size %d", nl_attr_len);
return 0;
}
else
return 1;
return 1;
}
static inline u32 rta_get_u32(struct rtattr *a)
{ return *(u32 *) RTA_DATA(a); }
static inline ip4_addr rta_get_ip4(struct rtattr *a)
{ return ip4_ntoh(*(ip4_addr *) RTA_DATA(a)); }
static inline ip6_addr rta_get_ip6(struct rtattr *a)
{ return ip6_ntoh(*(ip6_addr *) RTA_DATA(a)); }
struct rtattr *
nl_add_attr(struct nlmsghdr *h, uint bufsize, uint code, const void *data, uint dlen)
{
@ -333,7 +424,7 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
static int nh_buf_size; /* in number of structures */
static int nh_buf_used;
struct rtattr *a[RTA_CACHEINFO+1];
struct rtattr *a[BIRD_RTA_MAX];
struct rtnexthop *nh = RTA_DATA(ra);
struct mpnh *rv, *first, **last;
int len = RTA_PAYLOAD(ra);
@ -364,12 +455,9 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
/* Nonexistent RTNH_PAYLOAD ?? */
nl_attr_len = nh->rtnh_len - RTNH_LENGTH(0);
nl_parse_attrs(RTNH_DATA(nh), a, sizeof(a));
nl_parse_attrs(RTNH_DATA(nh), mpnh_attr_want4, a, sizeof(a));
if (a[RTA_GATEWAY])
{
if (RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr))
return NULL;
memcpy(&rv->gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ip_addr));
ipa_ntoh(rv->gw);
@ -420,7 +508,7 @@ nl_parse_metrics(struct rtattr *hdr, u32 *metrics, int max)
return -1;
metrics[0] |= 1 << a->rta_type;
metrics[a->rta_type] = *(u32 *)RTA_DATA(a);
metrics[a->rta_type] = rta_get_u32(a);
}
if (len > 0)
@ -438,7 +526,7 @@ static void
nl_parse_link(struct nlmsghdr *h, int scan)
{
struct ifinfomsg *i;
struct rtattr *a[IFLA_WIRELESS+1];
struct rtattr *a[BIRD_IFLA_MAX];
int new = h->nlmsg_type == RTM_NEWLINK;
struct iface f = {};
struct iface *ifi;
@ -446,17 +534,25 @@ nl_parse_link(struct nlmsghdr *h, int scan)
u32 mtu;
uint fl;
if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFLA_RTA(i), a, sizeof(a)))
if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFLA_RTA(i), ifla_attr_want, a, sizeof(a)))
return;
if (!a[IFLA_IFNAME] || RTA_PAYLOAD(a[IFLA_IFNAME]) < 2 ||
!a[IFLA_MTU] || RTA_PAYLOAD(a[IFLA_MTU]) != 4)
if (!a[IFLA_IFNAME] || (RTA_PAYLOAD(a[IFLA_IFNAME]) < 2) || !a[IFLA_MTU])
{
if (scan || !a[IFLA_WIRELESS])
log(L_ERR "nl_parse_link: Malformed message received");
/*
* IFLA_IFNAME and IFLA_MTU are required, in fact, but there may also come
* a message with IFLA_WIRELESS set, where (e.g.) no IFLA_IFNAME exists.
* We simply ignore all such messages with IFLA_WIRELESS without notice.
*/
if (a[IFLA_WIRELESS])
return;
log(L_ERR "KIF: Malformed message received");
return;
}
name = RTA_DATA(a[IFLA_IFNAME]);
memcpy(&mtu, RTA_DATA(a[IFLA_MTU]), sizeof(u32));
mtu = rta_get_u32(a[IFLA_MTU]);
ifi = if_find_by_index(i->ifi_index);
if (!new)
@ -505,26 +601,40 @@ static void
nl_parse_addr(struct nlmsghdr *h, int scan)
{
struct ifaddrmsg *i;
struct rtattr *a[IFA_ANYCAST+1];
struct rtattr *a[BIRD_IFA_MAX];
int new = h->nlmsg_type == RTM_NEWADDR;
struct ifa ifa;
struct iface *ifi;
int scope;
if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFA_RTA(i), a, sizeof(a)))
if (!(i = nl_checkin(h, sizeof(*i))))
return;
if (i->ifa_family != BIRD_AF)
return;
if (!a[IFA_ADDRESS] || RTA_PAYLOAD(a[IFA_ADDRESS]) != sizeof(ip_addr)
#ifdef IPV6
|| a[IFA_LOCAL] && RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip_addr)
#else
|| !a[IFA_LOCAL] || RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip_addr)
|| (a[IFA_BROADCAST] && RTA_PAYLOAD(a[IFA_BROADCAST]) != sizeof(ip_addr))
#endif
)
switch (i->ifa_family)
{
log(L_ERR "nl_parse_addr: Malformed message received");
#ifndef IPV6
case AF_INET:
if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want4, a, sizeof(a)))
return;
if (!a[IFA_LOCAL])
{
log(L_ERR "KIF: Malformed message received (missing IFA_LOCAL)");
return;
}
break;
#else
case AF_INET6:
if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want6, a, sizeof(a)))
return;
break;
#endif
default:
return;
}
if (!a[IFA_ADDRESS])
{
log(L_ERR "KIF: Malformed message received (missing IFA_ADDRESS)");
return;
}
@ -640,7 +750,23 @@ kif_do_scan(struct kif_proto *p UNUSED)
* Routes
*/
static struct krt_proto *nl_table_map[NL_NUM_TABLES];
static inline u32
krt_table_id(struct krt_proto *p)
{
return KRT_CF->sys.table_id;
}
static HASH(struct krt_proto) nl_table_map;
#define RTH_FN(k) u32_hash(k)
#define RTH_EQ(k1,k2) k1 == k2
#define RTH_KEY(p) krt_table_id(p)
#define RTH_NEXT(p) p->sys.hash_next
#define RTH_REHASH rth_rehash
#define RTH_PARAMS /8, *2, 2, 2, 6, 20
HASH_DEFINE_REHASH_FN(RTH, struct krt_proto)
int
krt_capable(rte *e)
@ -698,12 +824,15 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new)
r.r.rtm_family = BIRD_AF;
r.r.rtm_dst_len = net->n.pxlen;
r.r.rtm_tos = 0;
r.r.rtm_table = KRT_CF->sys.table_id;
r.r.rtm_protocol = RTPROT_BIRD;
r.r.rtm_scope = RT_SCOPE_UNIVERSE;
nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix);
if (krt_table_id(p) < 256)
r.r.rtm_table = krt_table_id(p);
else
nl_add_attr_u32(&r.h, sizeof(r), RTA_TABLE, krt_table_id(p));
/* For route delete, we do not specify route attributes */
if (!new)
return nl_exchange(&r.h);
@ -799,31 +928,35 @@ nl_parse_route(struct nlmsghdr *h, int scan)
{
struct krt_proto *p;
struct rtmsg *i;
struct rtattr *a[RTA_CACHEINFO+1];
struct rtattr *a[BIRD_RTA_MAX];
int new = h->nlmsg_type == RTM_NEWROUTE;
ip_addr dst = IPA_NONE;
u32 oif = ~0;
u32 table;
int src;
if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(RTM_RTA(i), a, sizeof(a)))
if (!(i = nl_checkin(h, sizeof(*i))))
return;
if (i->rtm_family != BIRD_AF)
return;
if ((a[RTA_DST] && RTA_PAYLOAD(a[RTA_DST]) != sizeof(ip_addr)) ||
#ifdef IPV6
(a[RTA_IIF] && RTA_PAYLOAD(a[RTA_IIF]) != 4) ||
#endif
(a[RTA_OIF] && RTA_PAYLOAD(a[RTA_OIF]) != 4) ||
(a[RTA_GATEWAY] && RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr)) ||
(a[RTA_PRIORITY] && RTA_PAYLOAD(a[RTA_PRIORITY]) != 4) ||
(a[RTA_PREFSRC] && RTA_PAYLOAD(a[RTA_PREFSRC]) != sizeof(ip_addr)) ||
(a[RTA_FLOW] && RTA_PAYLOAD(a[RTA_FLOW]) != 4))
switch (i->rtm_family)
{
log(L_ERR "KRT: Malformed message received");
return;
#ifndef IPV6
case AF_INET:
if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want4, a, sizeof(a)))
return;
break;
#else
case AF_INET6:
if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want6, a, sizeof(a)))
return;
break;
#endif
default:
return;
}
if (a[RTA_DST])
{
memcpy(&dst, RTA_DATA(a[RTA_DST]), sizeof(dst));
@ -831,12 +964,17 @@ nl_parse_route(struct nlmsghdr *h, int scan)
}
if (a[RTA_OIF])
memcpy(&oif, RTA_DATA(a[RTA_OIF]), sizeof(oif));
oif = rta_get_u32(a[RTA_OIF]);
p = nl_table_map[i->rtm_table]; /* Do we know this table? */
DBG("KRT: Got %I/%d, type=%d, oif=%d, table=%d, prid=%d, proto=%s\n", dst, i->rtm_dst_len, i->rtm_type, oif, i->rtm_table, i->rtm_protocol, p ? p->p.name : "(none)");
if (a[RTA_TABLE])
table = rta_get_u32(a[RTA_TABLE]);
else
table = i->rtm_table;
p = HASH_FIND(nl_table_map, RTH, table); /* Do we know this table? */
DBG("KRT: Got %I/%d, type=%d, oif=%d, table=%d, prid=%d, proto=%s\n", dst, i->rtm_dst_len, i->rtm_type, oif, table, i->rtm_protocol, p ? p->p.name : "(none)");
if (!p)
SKIP("unknown table %d\n", i->rtm_table);
SKIP("unknown table %d\n", table);
#ifdef IPV6
@ -895,7 +1033,7 @@ nl_parse_route(struct nlmsghdr *h, int scan)
{
case RTN_UNICAST:
if (a[RTA_MULTIPATH])
if (a[RTA_MULTIPATH] && (i->rtm_family == AF_INET))
{
ra.dest = RTD_MULTIPATH;
ra.nexthops = nl_parse_multipath(p, a[RTA_MULTIPATH]);
@ -964,12 +1102,12 @@ nl_parse_route(struct nlmsghdr *h, int scan)
e->net = net;
e->u.krt.src = src;
e->u.krt.proto = i->rtm_protocol;
e->u.krt.type = i->rtm_type;
e->u.krt.seen = 0;
e->u.krt.best = 0;
e->u.krt.metric = 0;
if (a[RTA_PRIORITY])
memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric));
else
e->u.krt.metric = 0;
e->u.krt.metric = rta_get_u32(a[RTA_PRIORITY]);
if (a[RTA_PREFSRC])
{
@ -1000,7 +1138,7 @@ nl_parse_route(struct nlmsghdr *h, int scan)
ea->attrs[0].id = EA_KRT_REALM;
ea->attrs[0].flags = 0;
ea->attrs[0].type = EAF_TYPE_INT;
memcpy(&ea->attrs[0].u.data, RTA_DATA(a[RTA_FLOW]), 4);
ea->attrs[0].u.data = rta_get_u32(a[RTA_FLOW]);
}
if (a[RTA_METRICS])
@ -1074,12 +1212,14 @@ nl_async_msg(struct nlmsghdr *h)
case RTM_NEWLINK:
case RTM_DELLINK:
DBG("KRT: Received async link notification (%d)\n", h->nlmsg_type);
nl_parse_link(h, 0);
if (kif_proto)
nl_parse_link(h, 0);
break;
case RTM_NEWADDR:
case RTM_DELADDR:
DBG("KRT: Received async address notification (%d)\n", h->nlmsg_type);
nl_parse_addr(h, 0);
if (kif_proto)
nl_parse_addr(h, 0);
break;
default:
DBG("KRT: Received unknown async notification (%d)\n", h->nlmsg_type);
@ -1177,25 +1317,41 @@ nl_open_async(void)
bug("Netlink: sk_open failed");
}
/*
* Interface to the UNIX krt module
*/
static u8 nl_cf_table[(NL_NUM_TABLES+7) / 8];
void
krt_sys_io_init(void)
{
HASH_INIT(nl_table_map, krt_pool, 6);
}
int
krt_sys_start(struct krt_proto *p)
{
nl_table_map[KRT_CF->sys.table_id] = p;
struct krt_proto *old = HASH_FIND(nl_table_map, RTH, krt_table_id(p));
if (old)
{
log(L_ERR "%s: Kernel table %u already registered by %s",
p->p.name, krt_table_id(p), old->p.name);
return 0;
}
HASH_INSERT2(nl_table_map, RTH, krt_pool, p);
nl_open();
nl_open_async();
return 1;
}
void
krt_sys_shutdown(struct krt_proto *p UNUSED)
krt_sys_shutdown(struct krt_proto *p)
{
nl_table_map[KRT_CF->sys.table_id] = NULL;
HASH_REMOVE2(nl_table_map, RTH, krt_pool, p);
}
int
@ -1204,23 +1360,6 @@ krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt
return n->sys.table_id == o->sys.table_id;
}
void
krt_sys_preconfig(struct config *c UNUSED)
{
bzero(&nl_cf_table, sizeof(nl_cf_table));
}
void
krt_sys_postconfig(struct krt_config *x)
{
int id = x->sys.table_id;
if (nl_cf_table[id/8] & (1 << (id%8)))
cf_error("Multiple kernel syncers defined for table #%d", id);
nl_cf_table[id/8] |= (1 << (id%8));
}
void
krt_sys_init_config(struct krt_config *cf)
{

View File

@ -19,6 +19,7 @@
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
@ -41,12 +42,12 @@
#include "lib/sysio.h"
/* Maximum number of calls of tx handler for one socket in one
* select iteration. Should be small enough to not monopolize CPU by
* poll iteration. Should be small enough to not monopolize CPU by
* one protocol instance.
*/
#define MAX_STEPS 4
/* Maximum number of calls of rx handler for all sockets in one select
/* Maximum number of calls of rx handler for all sockets in one poll
iteration. RX callbacks are often much more costly so we limit
this to gen small latencies */
#define MAX_RX_STEPS 4
@ -1017,7 +1018,6 @@ sk_log_error(sock *s, const char *p)
static list sock_list;
static struct birdsock *current_sock;
static struct birdsock *stored_sock;
static int sock_recalc_fdsets_p;
static inline sock *
sk_next(sock *s)
@ -1073,7 +1073,6 @@ sk_free(resource *r)
if (s == stored_sock)
stored_sock = sk_next(s);
rem_node(&s->n);
sock_recalc_fdsets_p = 1;
}
}
@ -1271,7 +1270,6 @@ static void
sk_insert(sock *s)
{
add_tail(&sock_list, &s->n);
sock_recalc_fdsets_p = 1;
}
static void
@ -1676,19 +1674,12 @@ sk_maybe_write(sock *s)
int
sk_rx_ready(sock *s)
{
fd_set rd, wr;
struct timeval timo;
int rv;
FD_ZERO(&rd);
FD_ZERO(&wr);
FD_SET(s->fd, &rd);
timo.tv_sec = 0;
timo.tv_usec = 0;
struct pollfd pfd = { .fd = s->fd };
pfd.events |= POLLIN;
redo:
rv = select(s->fd+1, &rd, &wr, NULL, &timo);
rv = poll(&pfd, 1, 0);
if ((rv < 0) && (errno == EINTR || errno == EAGAIN))
goto redo;
@ -1757,7 +1748,7 @@ sk_send_full(sock *s, unsigned len, struct iface *ifa,
/* sk_read() and sk_write() are called from BFD's event loop */
int
sk_read(sock *s)
sk_read(sock *s, int revents)
{
switch (s->type)
{
@ -1776,6 +1767,11 @@ sk_read(sock *s)
{
if (errno != EINTR && errno != EAGAIN)
s->err_hook(s, errno);
else if (errno == EAGAIN && !(revents & POLLIN))
{
log(L_ERR "Got EAGAIN from read when revents=%x (without POLLIN)", revents);
s->err_hook(s, 0);
}
}
else if (!c)
s->err_hook(s, 0);
@ -2042,62 +2038,63 @@ static int short_loops = 0;
void
io_loop(void)
{
fd_set rd, wr;
struct timeval timo;
int poll_tout;
time_t tout;
int hi, events;
int nfds, events, pout;
sock *s;
node *n;
int fdmax = 256;
struct pollfd *pfd = xmalloc(fdmax * sizeof(struct pollfd));
watchdog_start1();
sock_recalc_fdsets_p = 1;
for(;;)
{
events = ev_run_list(&global_event_list);
timers:
update_times();
tout = tm_first_shot();
if (tout <= now)
{
tm_shot();
continue;
goto timers;
}
timo.tv_sec = events ? 0 : MIN(tout - now, 3);
timo.tv_usec = 0;
poll_tout = (events ? 0 : MIN(tout - now, 3)) * 1000; /* Time in milliseconds */
io_close_event();
if (sock_recalc_fdsets_p)
{
sock_recalc_fdsets_p = 0;
FD_ZERO(&rd);
FD_ZERO(&wr);
}
hi = 0;
nfds = 0;
WALK_LIST(n, sock_list)
{
pfd[nfds] = (struct pollfd) { .fd = -1 }; /* everything other set to 0 by this */
s = SKIP_BACK(sock, n, n);
if (s->rx_hook)
{
FD_SET(s->fd, &rd);
if (s->fd > hi)
hi = s->fd;
pfd[nfds].fd = s->fd;
pfd[nfds].events |= POLLIN;
}
else
FD_CLR(s->fd, &rd);
if (s->tx_hook && s->ttx != s->tpos)
{
FD_SET(s->fd, &wr);
if (s->fd > hi)
hi = s->fd;
pfd[nfds].fd = s->fd;
pfd[nfds].events |= POLLOUT;
}
if (pfd[nfds].fd != -1)
{
s->index = nfds;
nfds++;
}
else
FD_CLR(s->fd, &wr);
s->index = -1;
if (nfds >= fdmax)
{
fdmax *= 2;
pfd = xrealloc(pfd, fdmax * sizeof(struct pollfd));
}
}
/*
* Yes, this is racy. But even if the signal comes before this test
* and entering select(), it gets caught on the next timer tick.
* and entering poll(), it gets caught on the next timer tick.
*/
if (async_config_flag)
@ -2122,18 +2119,18 @@ io_loop(void)
continue;
}
/* And finally enter select() to find active sockets */
/* And finally enter poll() to find active sockets */
watchdog_stop();
hi = select(hi+1, &rd, &wr, NULL, &timo);
pout = poll(pfd, nfds, poll_tout);
watchdog_start();
if (hi < 0)
if (pout < 0)
{
if (errno == EINTR || errno == EAGAIN)
continue;
die("select: %m");
die("poll: %m");
}
if (hi)
if (pout)
{
/* guaranteed to be non-empty */
current_sock = SKIP_BACK(sock, n, HEAD(sock_list));
@ -2141,23 +2138,29 @@ io_loop(void)
while (current_sock)
{
sock *s = current_sock;
if (s->index == -1)
{
current_sock = sk_next(s);
goto next;
}
int e;
int steps;
steps = MAX_STEPS;
if ((s->type >= SK_MAGIC) && FD_ISSET(s->fd, &rd) && s->rx_hook)
if (s->fast_rx && (pfd[s->index].revents & (POLLIN | POLLHUP | POLLERR)) && s->rx_hook)
do
{
steps--;
io_log_event(s->rx_hook, s->data);
e = sk_read(s);
e = sk_read(s, pfd[s->index].revents);
if (s != current_sock)
goto next;
}
while (e && s->rx_hook && steps);
steps = MAX_STEPS;
if (FD_ISSET(s->fd, &wr))
if (pfd[s->index].revents & POLLOUT)
do
{
steps--;
@ -2184,13 +2187,17 @@ io_loop(void)
while (current_sock && count < MAX_RX_STEPS)
{
sock *s = current_sock;
int e UNUSED;
if (s->index == -1)
{
current_sock = sk_next(s);
goto next2;
}
if ((s->type < SK_MAGIC) && FD_ISSET(s->fd, &rd) && s->rx_hook)
if (!s->fast_rx && (pfd[s->index].revents & (POLLIN | POLLHUP | POLLERR)) && s->rx_hook)
{
count++;
io_log_event(s->rx_hook, s->data);
e = sk_read(s);
sk_read(s, pfd[s->index].revents);
if (s != current_sock)
goto next2;
}

View File

@ -77,14 +77,15 @@ krt_io_init(void)
krt_pool = rp_new(&root_pool, "Kernel Syncer");
krt_filter_lp = lp_new(krt_pool, 4080);
init_list(&krt_proto_list);
krt_sys_io_init();
}
/*
* Interfaces
*/
struct kif_proto *kif_proto;
static struct kif_config *kif_cf;
static struct kif_proto *kif_proto;
static timer *kif_scan_timer;
static bird_clock_t kif_last_shot;
@ -416,46 +417,58 @@ again:
net *n = (net *) f;
rte *e, **ee, *best, **pbest, *old_best;
old_best = n->routes;
/*
* Note that old_best may be NULL even if there was an old best route in
* the previous step, because it might be replaced in krt_learn_scan().
* But in that case there is a new valid best route.
*/
old_best = NULL;
best = NULL;
pbest = NULL;
ee = &n->routes;
while (e = *ee)
{
if (e->u.krt.best)
old_best = e;
if (!e->u.krt.seen)
{
*ee = e->next;
rte_free(e);
continue;
}
if (!best || best->u.krt.metric > e->u.krt.metric)
{
best = e;
pbest = ee;
}
e->u.krt.seen = 0;
e->u.krt.best = 0;
ee = &e->next;
}
if (!n->routes)
{
DBG("%I/%d: deleting\n", n->n.prefix, n->n.pxlen);
if (old_best)
{
krt_learn_announce_delete(p, n);
n->n.flags &= ~KRF_INSTALLED;
}
krt_learn_announce_delete(p, n);
FIB_ITERATE_PUT(&fit, f);
fib_delete(fib, f);
goto again;
}
best->u.krt.best = 1;
*pbest = best->next;
best->next = n->routes;
n->routes = best;
if (best != old_best || !(n->n.flags & KRF_INSTALLED) || p->reload)
if ((best != old_best) || p->reload)
{
DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric);
krt_learn_announce_update(p, best);
n->n.flags |= KRF_INSTALLED;
}
else
DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric);
@ -514,31 +527,31 @@ krt_learn_async(struct krt_proto *p, rte *e, int new)
best = n->routes;
bestp = &n->routes;
for(gg=&n->routes; g=*gg; gg=&g->next)
{
if (best->u.krt.metric > g->u.krt.metric)
{
best = g;
bestp = gg;
}
g->u.krt.best = 0;
}
if (best)
{
best->u.krt.best = 1;
*bestp = best->next;
best->next = n->routes;
n->routes = best;
}
if (best != old_best)
{
DBG("krt_learn_async: distributing change\n");
if (best)
{
krt_learn_announce_update(p, best);
n->n.flags |= KRF_INSTALLED;
}
krt_learn_announce_update(p, best);
else
{
n->routes = NULL;
krt_learn_announce_delete(p, n);
n->n.flags &= ~KRF_INSTALLED;
}
krt_learn_announce_delete(p, n);
}
}
@ -563,7 +576,7 @@ krt_dump(struct proto *P)
static void
krt_dump_attrs(rte *e)
{
debug(" [m=%d,p=%d,t=%d]", e->u.krt.metric, e->u.krt.proto, e->u.krt.type);
debug(" [m=%d,p=%d]", e->u.krt.metric, e->u.krt.proto);
}
#endif
@ -1126,7 +1139,11 @@ krt_start(struct proto *P)
krt_learn_init(p);
#endif
krt_sys_start(p);
if (!krt_sys_start(p))
{
rem_node(&p->krt_node);
return PS_START;
}
krt_scan_timer_start(p);
@ -1150,8 +1167,10 @@ krt_shutdown(struct proto *P)
p->ready = 0;
p->initialized = 0;
krt_sys_shutdown(p);
if (p->p.proto_state == PS_START)
return PS_DOWN;
krt_sys_shutdown(p);
rem_node(&p->krt_node);
return PS_DOWN;

View File

@ -112,6 +112,8 @@ struct kif_proto {
struct kif_state sys; /* Sysdep state */
};
struct kif_proto *kif_proto;
#define KIF_CF ((struct kif_config *)p->p.cf)
struct proto_config * krt_init_config(int class);
@ -119,8 +121,9 @@ struct proto_config * krt_init_config(int class);
/* krt sysdep */
void krt_sys_io_init(void);
void krt_sys_init(struct krt_proto *);
void krt_sys_start(struct krt_proto *);
int krt_sys_start(struct krt_proto *);
void krt_sys_shutdown(struct krt_proto *);
int krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o);

View File

@ -284,17 +284,18 @@ log_switch(int debug, list *l, char *new_syslog_name)
current_log_list = l;
#ifdef HAVE_SYSLOG
if (current_syslog_name && new_syslog_name &&
!strcmp(current_syslog_name, new_syslog_name))
char *old_syslog_name = current_syslog_name;
current_syslog_name = new_syslog_name;
if (old_syslog_name && new_syslog_name &&
!strcmp(old_syslog_name, new_syslog_name))
return;
if (current_syslog_name)
if (old_syslog_name)
closelog();
if (new_syslog_name)
openlog(new_syslog_name, LOG_CONS | LOG_NDELAY, LOG_DAEMON);
current_syslog_name = new_syslog_name;
#endif
}

View File

@ -96,7 +96,7 @@ drop_gid(gid_t gid)
static inline void
add_num_const(char *name, int val)
{
struct symbol *s = cf_find_symbol(name);
struct symbol *s = cf_get_symbol(name);
s->class = SYM_CONSTANT | T_INT;
s->def = cfg_allocz(sizeof(struct f_val));
SYM_TYPE(s) = T_INT;
@ -450,6 +450,7 @@ cli_connect(sock *s, int size UNUSED)
s->err_hook = cli_err;
s->data = c = cli_new(s);
s->pool = c->pool; /* We need to have all the socket buffers allocated in the cli pool */
s->fast_rx = 1;
c->rx_pos = c->rx_buf;
c->rx_aux = NULL;
rmove(s, c->pool);
@ -466,6 +467,7 @@ cli_init_unix(uid_t use_uid, gid_t use_gid)
s->type = SK_UNIX_PASSIVE;
s->rx_hook = cli_connect;
s->rbsize = 1024;
s->fast_rx = 1;
/* Return value intentionally ignored */
unlink(path_control_socket);