Merge branch 'master' of ssh://git.nic.cz/bird
This commit is contained in:
commit
60c412b936
33 changed files with 1073 additions and 327 deletions
9
NEWS
9
NEWS
|
@ -1,3 +1,12 @@
|
||||||
|
Version 1.3.8 (2012-08-07)
|
||||||
|
o Generalized import and export route limits.
|
||||||
|
o RDNSS and DNSSL support for RAdv.
|
||||||
|
o Include in config file support wildcards.
|
||||||
|
o History deduplication in BIRD client.
|
||||||
|
o New route attributes krt_source, krt_metric.
|
||||||
|
o Different instance ID support for OSPFv3.
|
||||||
|
o Real broadcast mode for OSPFv2.
|
||||||
|
o Several minor bugfixes.
|
||||||
|
|
||||||
Version 1.3.7 (2012-03-22)
|
Version 1.3.7 (2012-03-22)
|
||||||
o Route Origin Authorization basics.
|
o Route Origin Authorization basics.
|
||||||
|
|
229
conf/cf-lex.l
229
conf/cf-lex.l
|
@ -31,6 +31,12 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <glob.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#define PARSER 1
|
#define PARSER 1
|
||||||
|
|
||||||
|
@ -64,27 +70,23 @@ struct sym_scope {
|
||||||
};
|
};
|
||||||
static struct sym_scope *conf_this_scope;
|
static struct sym_scope *conf_this_scope;
|
||||||
|
|
||||||
#define MAX_INCLUDE_DEPTH 5
|
|
||||||
|
|
||||||
static struct include_file_stack *ifs_head;
|
|
||||||
static int ifs_depth;
|
|
||||||
|
|
||||||
static int cf_hash(byte *c);
|
static int cf_hash(byte *c);
|
||||||
static struct symbol *cf_find_sym(byte *c, unsigned int h0);
|
static struct symbol *cf_find_sym(byte *c, unsigned int h0);
|
||||||
|
|
||||||
linpool *cfg_mem;
|
linpool *cfg_mem;
|
||||||
|
|
||||||
int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
|
int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
|
||||||
int (*cf_open_hook)(char *filename);
|
|
||||||
struct include_file_stack *ifs;
|
struct include_file_stack *ifs;
|
||||||
|
static struct include_file_stack *ifs_head;
|
||||||
|
|
||||||
#define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->conf_fd);
|
#define MAX_INCLUDE_DEPTH 8
|
||||||
|
|
||||||
|
#define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->fd);
|
||||||
#define YY_NO_UNPUT
|
#define YY_NO_UNPUT
|
||||||
#define YY_FATAL_ERROR(msg) cf_error(msg)
|
#define YY_FATAL_ERROR(msg) cf_error(msg)
|
||||||
|
|
||||||
static void new_include(void);
|
static void cf_include(char *arg, int alen);
|
||||||
static int check_eof(void);
|
static int check_eof(void);
|
||||||
static struct include_file_stack *new_stack(struct include_file_stack *old);
|
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
@ -103,7 +105,23 @@ WHITE [ \t]
|
||||||
include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
|
include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
|
||||||
|
|
||||||
%%
|
%%
|
||||||
{include} { if(cf_open_hook) new_include(); }
|
{include} {
|
||||||
|
char *start, *end;
|
||||||
|
|
||||||
|
if (!ifs->depth)
|
||||||
|
cf_error("Include not allowed in CLI");
|
||||||
|
|
||||||
|
start = strchr(yytext, '"');
|
||||||
|
start++;
|
||||||
|
|
||||||
|
end = strchr(start, '"');
|
||||||
|
*end = 0;
|
||||||
|
|
||||||
|
if (start == end)
|
||||||
|
cf_error("Include with empty argument");
|
||||||
|
|
||||||
|
cf_include(start, end-start);
|
||||||
|
}
|
||||||
|
|
||||||
{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ {
|
{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ {
|
||||||
#ifdef IPV6
|
#ifdef IPV6
|
||||||
|
@ -200,11 +218,11 @@ else: {
|
||||||
|
|
||||||
["][^"\n]*\n cf_error("Unterminated string");
|
["][^"\n]*\n cf_error("Unterminated string");
|
||||||
|
|
||||||
<INITIAL,COMMENT><<EOF>> { if(check_eof()) return END; }
|
<INITIAL,COMMENT><<EOF>> { if (check_eof()) return END; }
|
||||||
|
|
||||||
{WHITE}+
|
{WHITE}+
|
||||||
|
|
||||||
\n ifs->conf_lino++;
|
\n ifs->lino++;
|
||||||
|
|
||||||
# BEGIN(COMMENT);
|
# BEGIN(COMMENT);
|
||||||
|
|
||||||
|
@ -213,14 +231,14 @@ else: {
|
||||||
. cf_error("Unknown character");
|
. cf_error("Unknown character");
|
||||||
|
|
||||||
<COMMENT>\n {
|
<COMMENT>\n {
|
||||||
ifs->conf_lino++;
|
ifs->lino++;
|
||||||
BEGIN(INITIAL);
|
BEGIN(INITIAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
<COMMENT>.
|
<COMMENT>.
|
||||||
|
|
||||||
<CCOMM>\*\/ BEGIN(INITIAL);
|
<CCOMM>\*\/ BEGIN(INITIAL);
|
||||||
<CCOMM>\n ifs->conf_lino++;
|
<CCOMM>\n ifs->lino++;
|
||||||
<CCOMM>\/\* cf_error("Comment nesting not supported");
|
<CCOMM>\/\* cf_error("Comment nesting not supported");
|
||||||
<CCOMM><<EOF>> cf_error("Unterminated comment");
|
<CCOMM><<EOF>> cf_error("Unterminated comment");
|
||||||
<CCOMM>.
|
<CCOMM>.
|
||||||
|
@ -246,48 +264,141 @@ cf_hash(byte *c)
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Open included file with properly swapped buffers */
|
|
||||||
static void
|
/*
|
||||||
new_include(void)
|
* IFS stack - it contains structures needed for recursive processing
|
||||||
|
* of include in config files. On the top of the stack is a structure
|
||||||
|
* for currently processed file. Other structures are either for
|
||||||
|
* active files interrupted because of include directive (these have
|
||||||
|
* fd and flex buffer) or for inactive files scheduled to be processed
|
||||||
|
* later (when parent requested including of several files by wildcard
|
||||||
|
* match - these do not have fd and flex buffer yet).
|
||||||
|
*
|
||||||
|
* FIXME: Most of these ifs and include functions are really sysdep/unix.
|
||||||
|
*
|
||||||
|
* FIXME: Resources (fd, flex buffers and glob data) in IFS stack
|
||||||
|
* are not freed when cf_error() is called.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct include_file_stack *
|
||||||
|
push_ifs(struct include_file_stack *old)
|
||||||
{
|
{
|
||||||
char *fname, *p = NULL;
|
struct include_file_stack *ret;
|
||||||
|
ret = cfg_allocz(sizeof(struct include_file_stack));
|
||||||
|
ret->lino = 1;
|
||||||
|
ret->prev = old;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if ((fname = strchr(yytext, '"')) != NULL) {
|
static struct include_file_stack *
|
||||||
|
pop_ifs(struct include_file_stack *old)
|
||||||
|
{
|
||||||
|
yy_delete_buffer(old->buffer);
|
||||||
|
close(old->fd);
|
||||||
|
return old->prev;
|
||||||
|
}
|
||||||
|
|
||||||
if ((p = strchr(++fname, '"')) != NULL) *p = '\0';
|
static void
|
||||||
|
enter_ifs(struct include_file_stack *new)
|
||||||
|
{
|
||||||
|
if (!new->buffer)
|
||||||
|
{
|
||||||
|
new->fd = open(new->file_name, O_RDONLY);
|
||||||
|
if (new->fd < 0)
|
||||||
|
{
|
||||||
|
ifs = ifs->up;
|
||||||
|
cf_error("Unable to open included file %s: %m", new->file_name);
|
||||||
|
}
|
||||||
|
|
||||||
if (ifs_depth >= MAX_INCLUDE_DEPTH)
|
new->buffer = yy_create_buffer(NULL, YY_BUF_SIZE);
|
||||||
cf_error("Max include depth reached.");
|
}
|
||||||
|
|
||||||
/* Save current stack */
|
yy_switch_to_buffer(new->buffer);
|
||||||
ifs->stack = YY_CURRENT_BUFFER;
|
}
|
||||||
/* Prepare new stack */
|
|
||||||
ifs->next = new_stack(ifs);
|
|
||||||
ifs = ifs->next;
|
|
||||||
strcpy(ifs->conf_fname, fname); /* XXX: strlcpy should be here */
|
|
||||||
ifs->conf_fd = cf_open_hook(fname);
|
|
||||||
|
|
||||||
yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
|
static void
|
||||||
}
|
cf_include(char *arg, int alen)
|
||||||
|
{
|
||||||
|
struct include_file_stack *base_ifs = ifs;
|
||||||
|
int new_depth, rv, i;
|
||||||
|
char *patt;
|
||||||
|
glob_t g;
|
||||||
|
|
||||||
|
new_depth = ifs->depth + 1;
|
||||||
|
if (new_depth > MAX_INCLUDE_DEPTH)
|
||||||
|
cf_error("Max include depth reached");
|
||||||
|
|
||||||
|
/* expand arg to properly handle relative filenames */
|
||||||
|
if (*arg != '/')
|
||||||
|
{
|
||||||
|
int dlen = strlen(ifs->file_name);
|
||||||
|
char *dir = alloca(dlen + 1);
|
||||||
|
patt = alloca(dlen + alen + 2);
|
||||||
|
memcpy(dir, ifs->file_name, dlen + 1);
|
||||||
|
sprintf(patt, "%s/%s", dirname(dir), arg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
patt = arg;
|
||||||
|
|
||||||
|
/* Skip globbing if there are no wildcards, mainly to get proper
|
||||||
|
response when the included config file is missing */
|
||||||
|
if (!strpbrk(arg, "?*["))
|
||||||
|
{
|
||||||
|
ifs = push_ifs(ifs);
|
||||||
|
ifs->file_name = cfg_strdup(patt);
|
||||||
|
ifs->depth = new_depth;
|
||||||
|
ifs->up = base_ifs;
|
||||||
|
enter_ifs(ifs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expand the pattern */
|
||||||
|
rv = glob(patt, GLOB_ERR | GLOB_NOESCAPE, NULL, &g);
|
||||||
|
if (rv == GLOB_ABORTED)
|
||||||
|
cf_error("Unable to match pattern %s: %m", patt);
|
||||||
|
if ((rv != 0) || (g.gl_pathc <= 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we put all found files to ifs stack in reverse order, they
|
||||||
|
* will be activated and processed in order as ifs stack is popped
|
||||||
|
* by pop_ifs() and enter_ifs() in check_eof().
|
||||||
|
*/
|
||||||
|
for(i = g.gl_pathc - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
char *fname = g.gl_pathv[i];
|
||||||
|
struct stat fs;
|
||||||
|
|
||||||
|
if (stat(fname, &fs) < 0)
|
||||||
|
cf_error("Unable to stat included file %s: %m", fname);
|
||||||
|
|
||||||
|
if (fs.st_mode & S_IFDIR)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Prepare new stack item */
|
||||||
|
ifs = push_ifs(ifs);
|
||||||
|
ifs->file_name = cfg_strdup(fname);
|
||||||
|
ifs->depth = new_depth;
|
||||||
|
ifs->up = base_ifs;
|
||||||
|
}
|
||||||
|
|
||||||
|
globfree(&g);
|
||||||
|
enter_ifs(ifs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
check_eof(void)
|
check_eof(void)
|
||||||
{
|
{
|
||||||
if (ifs == ifs_head) {
|
if (ifs == ifs_head)
|
||||||
/* EOF in main config file */
|
{
|
||||||
ifs->conf_lino = 1;
|
/* EOF in main config file */
|
||||||
return 1;
|
ifs->lino = 1; /* Why this? */
|
||||||
}
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
ifs_depth--;
|
ifs = pop_ifs(ifs);
|
||||||
close(ifs->conf_fd);
|
enter_ifs(ifs);
|
||||||
ifs = ifs->prev;
|
return 0;
|
||||||
ifs->next = NULL;
|
|
||||||
|
|
||||||
yy_delete_buffer(YY_CURRENT_BUFFER);
|
|
||||||
yy_switch_to_buffer(ifs->stack);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct symbol *
|
static struct symbol *
|
||||||
|
@ -415,16 +526,6 @@ cf_lex_init_kh(void)
|
||||||
kw_hash_inited = 1;
|
kw_hash_inited = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct include_file_stack *
|
|
||||||
new_stack(struct include_file_stack *old)
|
|
||||||
{
|
|
||||||
struct include_file_stack *ret;
|
|
||||||
ret = cfg_allocz(sizeof(struct include_file_stack));
|
|
||||||
ret->conf_lino = 1;
|
|
||||||
ret->prev = old;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cf_lex_init - initialize the lexer
|
* cf_lex_init - initialize the lexer
|
||||||
* @is_cli: true if we're going to parse CLI command, false for configuration
|
* @is_cli: true if we're going to parse CLI command, false for configuration
|
||||||
|
@ -437,19 +538,23 @@ cf_lex_init(int is_cli, struct config *c)
|
||||||
{
|
{
|
||||||
if (!kw_hash_inited)
|
if (!kw_hash_inited)
|
||||||
cf_lex_init_kh();
|
cf_lex_init_kh();
|
||||||
ifs_head = new_stack(NULL);
|
|
||||||
ifs = ifs_head;
|
ifs_head = ifs = push_ifs(NULL);
|
||||||
ifs_depth = 0;
|
if (!is_cli)
|
||||||
if (!is_cli) {
|
{
|
||||||
ifs->conf_fd = c->file_fd;
|
ifs->file_name = c->file_name;
|
||||||
ifs_depth = 1;
|
ifs->fd = c->file_fd;
|
||||||
strcpy(ifs->conf_fname, c->file_name);
|
ifs->depth = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
yyrestart(NULL);
|
yyrestart(NULL);
|
||||||
|
ifs->buffer = YY_CURRENT_BUFFER;
|
||||||
|
|
||||||
if (is_cli)
|
if (is_cli)
|
||||||
BEGIN(CLI);
|
BEGIN(CLI);
|
||||||
else
|
else
|
||||||
BEGIN(INITIAL);
|
BEGIN(INITIAL);
|
||||||
|
|
||||||
conf_this_scope = cfg_allocz(sizeof(struct sym_scope));
|
conf_this_scope = cfg_allocz(sizeof(struct sym_scope));
|
||||||
conf_this_scope->active = 1;
|
conf_this_scope->active = 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -357,8 +357,8 @@ cf_error(char *msg, ...)
|
||||||
if (bvsnprintf(buf, sizeof(buf), msg, args) < 0)
|
if (bvsnprintf(buf, sizeof(buf), msg, args) < 0)
|
||||||
strcpy(buf, "<bug: error message too long>");
|
strcpy(buf, "<bug: error message too long>");
|
||||||
new_config->err_msg = cfg_strdup(buf);
|
new_config->err_msg = cfg_strdup(buf);
|
||||||
new_config->err_lino = ifs->conf_lino;
|
new_config->err_lino = ifs->lino;
|
||||||
new_config->err_file_name = ifs->conf_fname;
|
new_config->err_file_name = ifs->file_name;
|
||||||
longjmp(conf_jmpbuf, 1);
|
longjmp(conf_jmpbuf, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
conf/conf.h
16
conf/conf.h
|
@ -12,7 +12,6 @@
|
||||||
#include "lib/resource.h"
|
#include "lib/resource.h"
|
||||||
#include "lib/timer.h"
|
#include "lib/timer.h"
|
||||||
|
|
||||||
#define BIRD_FNAME_MAX 255 /* Would be better to use some UNIX define */
|
|
||||||
|
|
||||||
/* Configuration structure */
|
/* Configuration structure */
|
||||||
|
|
||||||
|
@ -91,7 +90,6 @@ void cfg_copy_list(list *dest, list *src, unsigned node_size);
|
||||||
/* Lexer */
|
/* Lexer */
|
||||||
|
|
||||||
extern int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
|
extern int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
|
||||||
extern int (*cf_open_hook)(char *filename);
|
|
||||||
|
|
||||||
struct symbol {
|
struct symbol {
|
||||||
struct symbol *next;
|
struct symbol *next;
|
||||||
|
@ -117,12 +115,14 @@ struct symbol {
|
||||||
#define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */
|
#define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */
|
||||||
|
|
||||||
struct include_file_stack {
|
struct include_file_stack {
|
||||||
void *stack; /* Internal lexer state */
|
void *buffer; /* Internal lexer state */
|
||||||
unsigned int conf_lino; /* Current file lineno (at include) */
|
char *file_name; /* File name */
|
||||||
char conf_fname[BIRD_FNAME_MAX]; /* Current file name */
|
int fd; /* File descriptor */
|
||||||
int conf_fd; /* Current file descriptor */
|
int lino; /* Current line num */
|
||||||
struct include_file_stack *prev;
|
int depth; /* Include depth, 0 = cannot include */
|
||||||
struct include_file_stack *next;
|
|
||||||
|
struct include_file_stack *prev; /* Previous record in stack */
|
||||||
|
struct include_file_stack *up; /* Parent (who included this file) */
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct include_file_stack *ifs;
|
extern struct include_file_stack *ifs;
|
||||||
|
|
154
doc/bird.sgml
154
doc/bird.sgml
|
@ -215,7 +215,27 @@ an <it/import/ filter checks routes in the opposite direction.
|
||||||
When the routing table gets a route from a protocol, it recalculates
|
When the routing table gets a route from a protocol, it recalculates
|
||||||
the selected route and broadcasts it to all protocols connected to
|
the selected route and broadcasts it to all protocols connected to
|
||||||
the table. The protocols typically send the update to other routers
|
the table. The protocols typically send the update to other routers
|
||||||
in the network.
|
in the network. Note that although most protocols are interested
|
||||||
|
in receiving just selected routes, some protocols (e.g. the <cf/Pipe/
|
||||||
|
protocol) receive and process all entries in routing tables (accepted
|
||||||
|
by filters).
|
||||||
|
|
||||||
|
<p><label id="dsc-sorted">Usually, a routing table just chooses a
|
||||||
|
selected route from a list of entries for one network. But if the
|
||||||
|
<cf/sorted/ option is activated, these lists of entries are kept
|
||||||
|
completely sorted (according to preference or some protocol-dependent
|
||||||
|
metric).
|
||||||
|
|
||||||
|
This is needed for some features of some protocols
|
||||||
|
(e.g. <cf/secondary/ option of BGP protocol, which allows to accept
|
||||||
|
not just a selected route, but the first route (in the sorted list)
|
||||||
|
that is accepted by filters), but it is incompatible with some other
|
||||||
|
features (e.g. <cf/deterministic med/ option of BGP protocol, which
|
||||||
|
activates a way of choosing selected route that cannot be described
|
||||||
|
using comparison and ordering). Minor advantage is that routes are
|
||||||
|
shown sorted in <cf/show route/, minor disadvantage is that it is
|
||||||
|
slightly more computationally expensive.
|
||||||
|
|
||||||
|
|
||||||
<chapt>Configuration
|
<chapt>Configuration
|
||||||
|
|
||||||
|
@ -354,11 +374,14 @@ protocol rip {
|
||||||
defaults are here for a compatibility with older versions
|
defaults are here for a compatibility with older versions
|
||||||
and might change in the future.
|
and might change in the future.
|
||||||
|
|
||||||
<tag>table <m/name/</tag> Create a new routing table. The default
|
<tag>table <m/name/ [sorted]</tag>
|
||||||
routing table is created implicitly, other routing tables have
|
Create a new routing table. The default routing table is
|
||||||
to be added by this command.
|
created implicitly, other routing tables have to be added by
|
||||||
|
this command. Option <cf/sorted/ can be used to enable
|
||||||
|
sorting of routes, see <ref id="dsc-sorted" name="sorted table">
|
||||||
|
description for details.
|
||||||
|
|
||||||
<tag>roa table [ { roa table options ... } ] <m/name/</tag>
|
<tag>roa table <m/name/ [ { roa table options ... } ]</tag>
|
||||||
Create a new ROA (Route Origin Authorization) table. ROA
|
Create a new ROA (Route Origin Authorization) table. ROA
|
||||||
tables can be used to validate route origination of BGP
|
tables can be used to validate route origination of BGP
|
||||||
routes. A ROA table contains ROA entries, each consist of a
|
routes. A ROA table contains ROA entries, each consist of a
|
||||||
|
@ -454,7 +477,7 @@ to zero to disable it. An empty <cf><m/switch/</cf> is equivalent to <cf/on/
|
||||||
problems in details of its behavior -- the number of exported
|
problems in details of its behavior -- the number of exported
|
||||||
routes can temporarily exceed the limit without triggering it
|
routes can temporarily exceed the limit without triggering it
|
||||||
during protocol reload, exported routes counter ignores route
|
during protocol reload, exported routes counter ignores route
|
||||||
blocking and block action also blocks route updates of alread
|
blocking and block action also blocks route updates of already
|
||||||
accepted routes -- and these details will probably change in
|
accepted routes -- and these details will probably change in
|
||||||
the future. Default: <cf/none/.
|
the future. Default: <cf/none/.
|
||||||
|
|
||||||
|
@ -1270,8 +1293,10 @@ for each neighbor using the following configuration parameters:
|
||||||
mode is the behavior specified by the BGP standard. Direct
|
mode is the behavior specified by the BGP standard. Direct
|
||||||
mode is simpler, does not require any routes in a routing
|
mode is simpler, does not require any routes in a routing
|
||||||
table, and was used in older versions of BIRD, but does not
|
table, and was used in older versions of BIRD, but does not
|
||||||
handle well nontrivial iBGP setups and multihop. Default:
|
handle well nontrivial iBGP setups and multihop. Recursive
|
||||||
<cf/direct/ for singlehop eBGP, <cf/recursive/ otherwise.
|
mode is incompatible with <ref id="dsc-sorted" name="sorted
|
||||||
|
tables">. Default: <cf/direct/ for singlehop eBGP,
|
||||||
|
<cf/recursive/ otherwise.
|
||||||
|
|
||||||
<tag>igp table <m/name/</tag> Specifies a table that is used
|
<tag>igp table <m/name/</tag> Specifies a table that is used
|
||||||
as an IGP routing table. Default: the same as the table BGP is
|
as an IGP routing table. Default: the same as the table BGP is
|
||||||
|
@ -1317,6 +1342,16 @@ for each neighbor using the following configuration parameters:
|
||||||
attributes to be transparent (for example does not prepend its AS number to
|
attributes to be transparent (for example does not prepend its AS number to
|
||||||
AS PATH attribute and keeps MED attribute). Default: disabled.
|
AS PATH attribute and keeps MED attribute). Default: disabled.
|
||||||
|
|
||||||
|
<tag>secondary <m/switch/</tag> Usually, if an import filter
|
||||||
|
rejects a selected route, no other route is propagated for
|
||||||
|
that network. This option allows to try the next route in
|
||||||
|
order until one that is accepted is found or all routes for
|
||||||
|
that network are rejected. This can be used for route servers
|
||||||
|
that need to propagate different tables to each client but do
|
||||||
|
not want to have these tables explicitly (to conserve memory).
|
||||||
|
This option requires that the connected routing table is
|
||||||
|
<ref id="dsc-sorted" name="sorted">. Default: off.
|
||||||
|
|
||||||
<tag>enable route refresh <m/switch/</tag> When BGP speaker
|
<tag>enable route refresh <m/switch/</tag> When BGP speaker
|
||||||
changes its import filter, it has to re-examine all routes
|
changes its import filter, it has to re-examine all routes
|
||||||
received from its neighbor against the new filter. As these
|
received from its neighbor against the new filter. As these
|
||||||
|
@ -1422,7 +1457,9 @@ for each neighbor using the following configuration parameters:
|
||||||
This option enables a different (and slower) algorithm
|
This option enables a different (and slower) algorithm
|
||||||
implementing proper RFC 4271 route selection, which is
|
implementing proper RFC 4271 route selection, which is
|
||||||
deterministic. Alternative way how to get deterministic
|
deterministic. Alternative way how to get deterministic
|
||||||
behavior is to use <cf/med metric/ option. Default: off.
|
behavior is to use <cf/med metric/ option. This option is
|
||||||
|
incompatible with <ref id="dsc-sorted" name="sorted tables">.
|
||||||
|
Default: off.
|
||||||
|
|
||||||
<tag>igp metric <m/switch/</tag> Enable comparison of internal
|
<tag>igp metric <m/switch/</tag> Enable comparison of internal
|
||||||
distances to boundary routers during best route selection. Default: on.
|
distances to boundary routers during best route selection. Default: on.
|
||||||
|
@ -2321,22 +2358,25 @@ advertisement packets to connected networks. These packets contain
|
||||||
basic information about a local network (e.g. a list of network
|
basic information about a local network (e.g. a list of network
|
||||||
prefixes), which allows network hosts to autoconfigure network
|
prefixes), which allows network hosts to autoconfigure network
|
||||||
addresses and choose a default route. BIRD implements router behavior
|
addresses and choose a default route. BIRD implements router behavior
|
||||||
as defined in RFC 4861<htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc4861.txt">.
|
as defined in
|
||||||
|
RFC 4861<htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc4861.txt">
|
||||||
|
and also the DNS extensions from
|
||||||
|
RFC 6106<htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc6106.txt">.
|
||||||
|
|
||||||
<sect1>Configuration
|
<sect1>Configuration
|
||||||
|
|
||||||
<p>There are two classes of definitions in RAdv configuration --
|
<p>There are several classes of definitions in RAdv configuration --
|
||||||
interface definitions and prefix definitions:
|
interface definitions, prefix definitions and DNS definitions:
|
||||||
|
|
||||||
<descrip>
|
<descrip>
|
||||||
<tag>interface <m/pattern [, ...]/ { <m/options/ }</tag>
|
<tag>interface <m/pattern [, ...]/ { <m/options/ }</tag>
|
||||||
Interface definitions specify a set of interfaces on which the
|
Interface definitions specify a set of interfaces on which the
|
||||||
protocol is activated and contain interface specific options.
|
protocol is activated and contain interface specific options.
|
||||||
See <ref id="dsc-iface" name="interface"> common options for
|
See <ref id="dsc-iface" name="interface"> common options for
|
||||||
detailed description.
|
detailed description.
|
||||||
|
|
||||||
<tag>prefix <m/prefix/ { <m/options/ }</tag>
|
<tag>prefix <m/prefix/ { <m/options/ }</tag>
|
||||||
Prefix definitions allows to modify a list of advertised
|
Prefix definitions allow to modify a list of advertised
|
||||||
prefixes. By default, the advertised prefixes are the same as
|
prefixes. By default, the advertised prefixes are the same as
|
||||||
the network prefixes assigned to the interface. For each
|
the network prefixes assigned to the interface. For each
|
||||||
network prefix, the matching prefix definition is found and
|
network prefix, the matching prefix definition is found and
|
||||||
|
@ -2350,6 +2390,24 @@ interface definitions and prefix definitions:
|
||||||
definitions. As expected, the prefix definition is matching if
|
definitions. As expected, the prefix definition is matching if
|
||||||
the network prefix is a subnet of the prefix in prefix
|
the network prefix is a subnet of the prefix in prefix
|
||||||
definition.
|
definition.
|
||||||
|
|
||||||
|
<tag>rdnss { <m/options/ }</tag>
|
||||||
|
RDNSS definitions allow to specify a list of advertised
|
||||||
|
recursive DNS servers together with their options. As options
|
||||||
|
are seldom necessary, there is also a short variant <cf>rdnss
|
||||||
|
<m/address/</cf> that just specifies one DNS server. Multiple
|
||||||
|
definitions are cumulative. RDNSS definitions may also be
|
||||||
|
interface-specific when used inside interface options. By
|
||||||
|
default, interface uses both global and interface-specific
|
||||||
|
options, but that can be changed by <cf/rdnss local/ option.
|
||||||
|
|
||||||
|
<tag>dnssl { <m/options/ }</tag>
|
||||||
|
DNSSL definitions allow to specify a list of advertised DNS
|
||||||
|
search domains together with their options. Like <cf/rdnss/
|
||||||
|
above, multiple definitions are cumulative, they can be used
|
||||||
|
also as interface-specific options and there is a short
|
||||||
|
variant <cf>dnssl <m/domain/</cf> that just specifies one DNS
|
||||||
|
search domain.
|
||||||
</descrip>
|
</descrip>
|
||||||
|
|
||||||
<p>Interface specific options:
|
<p>Interface specific options:
|
||||||
|
@ -2362,8 +2420,8 @@ interface definitions and prefix definitions:
|
||||||
|
|
||||||
<tag>min ra interval <m/expr/</tag>
|
<tag>min ra interval <m/expr/</tag>
|
||||||
This option specifies the minimum length of that intervals, in
|
This option specifies the minimum length of that intervals, in
|
||||||
seconds. Must be at least 3 and at most 3/4 * max ra interval.
|
seconds. Must be at least 3 and at most 3/4 * <cf/max ra interval/.
|
||||||
Default: about 1/3 * max ra interval.
|
Default: about 1/3 * <cf/max ra interval/.
|
||||||
|
|
||||||
<tag>min delay <m/expr/</tag>
|
<tag>min delay <m/expr/</tag>
|
||||||
The minimum delay between two consecutive router advertisements,
|
The minimum delay between two consecutive router advertisements,
|
||||||
|
@ -2400,7 +2458,17 @@ interface definitions and prefix definitions:
|
||||||
This option specifies the time (in seconds) how long (after
|
This option specifies the time (in seconds) how long (after
|
||||||
the receipt of RA) hosts may use the router as a default
|
the receipt of RA) hosts may use the router as a default
|
||||||
router. 0 means do not use as a default router. Default: 3 *
|
router. 0 means do not use as a default router. Default: 3 *
|
||||||
max ra interval.
|
<cf/max ra interval/.
|
||||||
|
|
||||||
|
<tag>rdnss local <m/bool/</tag>
|
||||||
|
Use only local (interface-specific) RDNSS definitions for this
|
||||||
|
interface. Otherwise, both global and local definitions are
|
||||||
|
used. Could also be used to disable RDNSS for given interface
|
||||||
|
if no local definitons are specified. Default: no.
|
||||||
|
|
||||||
|
<tag>dnssl local <m/bool/</tag>
|
||||||
|
Use only local DNSSL definitions for this interface. See
|
||||||
|
<cf/rdnss local/ option above. Default: no.
|
||||||
</descrip>
|
</descrip>
|
||||||
|
|
||||||
|
|
||||||
|
@ -2429,6 +2497,42 @@ interface definitions and prefix definitions:
|
||||||
14400 (4 hours)
|
14400 (4 hours)
|
||||||
</descrip>
|
</descrip>
|
||||||
|
|
||||||
|
|
||||||
|
<p>RDNSS specific options:
|
||||||
|
|
||||||
|
<descrip>
|
||||||
|
<tag>ns <m/address/</tag>
|
||||||
|
This option specifies one recursive DNS server. Can be used
|
||||||
|
multiple times for multiple servers. It is mandatory to have
|
||||||
|
at least one <cf/ns/ option in <cf/rdnss/ definition.
|
||||||
|
|
||||||
|
<tag>lifetime [mult] <m/expr/</tag>
|
||||||
|
This option specifies the time how long the RDNSS information
|
||||||
|
may be used by clients after the receipt of RA. It is
|
||||||
|
expressed either in seconds or (when <cf/mult/ is used) in
|
||||||
|
multiples of <cf/max ra interval/. Note that RDNSS information
|
||||||
|
is also invalidated when <cf/default lifetime/ expires. 0
|
||||||
|
means these addresses are no longer valid DNS servers.
|
||||||
|
Default: 3 * <cf/max ra interval/.
|
||||||
|
</descrip>
|
||||||
|
|
||||||
|
|
||||||
|
<p>DNSSL specific options:
|
||||||
|
|
||||||
|
<descrip>
|
||||||
|
<tag>domain <m/address/</tag>
|
||||||
|
This option specifies one DNS search domain. Can be used
|
||||||
|
multiple times for multiple domains. It is mandatory to have
|
||||||
|
at least one <cf/domain/ option in <cf/dnssl/ definition.
|
||||||
|
|
||||||
|
<tag>lifetime [mult] <m/expr/</tag>
|
||||||
|
This option specifies the time how long the DNSSL information
|
||||||
|
may be used by clients after the receipt of RA. Details are
|
||||||
|
the same as for RDNSS <cf/lifetime/ option above.
|
||||||
|
Default: 3 * <cf/max ra interval/.
|
||||||
|
</descrip>
|
||||||
|
|
||||||
|
|
||||||
<sect1>Example
|
<sect1>Example
|
||||||
|
|
||||||
<p><code>
|
<p><code>
|
||||||
|
@ -2450,6 +2554,20 @@ protocol radv {
|
||||||
prefix 2001:0DB8:2000::/48 {
|
prefix 2001:0DB8:2000::/48 {
|
||||||
autonomous off; # Do not autoconfigure
|
autonomous off; # Do not autoconfigure
|
||||||
};
|
};
|
||||||
|
|
||||||
|
rdnss 2001:0DB8:1234::10; # Short form of RDNSS
|
||||||
|
|
||||||
|
rdnss {
|
||||||
|
lifetime mult 10;
|
||||||
|
ns 2001:0DB8:1234::11;
|
||||||
|
ns 2001:0DB8:1234::12;
|
||||||
|
};
|
||||||
|
|
||||||
|
dnssl {
|
||||||
|
lifetime 3600;
|
||||||
|
domain "abc.com";
|
||||||
|
domain "xyz.com";
|
||||||
|
};
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ f_new_inst(void)
|
||||||
ret = cfg_alloc(sizeof(struct f_inst));
|
ret = cfg_alloc(sizeof(struct f_inst));
|
||||||
ret->code = ret->aux = 0;
|
ret->code = ret->aux = 0;
|
||||||
ret->arg1 = ret->arg2 = ret->next = NULL;
|
ret->arg1 = ret->arg2 = ret->next = NULL;
|
||||||
ret->lineno = ifs->conf_lino;
|
ret->lineno = ifs->lino;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *a
|
||||||
{
|
{
|
||||||
struct f_inst_roa_check *ret = cfg_allocz(sizeof(struct f_inst_roa_check));
|
struct f_inst_roa_check *ret = cfg_allocz(sizeof(struct f_inst_roa_check));
|
||||||
ret->i.code = P('R','C');
|
ret->i.code = P('R','C');
|
||||||
ret->i.lineno = ifs->conf_lino;
|
ret->i.lineno = ifs->lino;
|
||||||
ret->i.arg1 = prefix;
|
ret->i.arg1 = prefix;
|
||||||
ret->i.arg2 = asn;
|
ret->i.arg2 = asn;
|
||||||
/* prefix == NULL <-> asn == NULL */
|
/* prefix == NULL <-> asn == NULL */
|
||||||
|
|
|
@ -3,4 +3,3 @@ print "Entering include";
|
||||||
print "Should be 2: ", 1+1;
|
print "Should be 2: ", 1+1;
|
||||||
print "Leaving include";
|
print "Leaving include";
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,8 @@ typedef struct list { /* In fact two overlayed nodes */
|
||||||
#define HEAD(list) ((void *)((list).head))
|
#define HEAD(list) ((void *)((list).head))
|
||||||
#define TAIL(list) ((void *)((list).tail))
|
#define TAIL(list) ((void *)((list).tail))
|
||||||
#define NODE_NEXT(n) ((void *)((NODE (n))->next))
|
#define NODE_NEXT(n) ((void *)((NODE (n))->next))
|
||||||
#define WALK_LIST(n,list) for(n=HEAD(list);(NODE (n))->next; n=NODE_NEXT(n))
|
#define NODE_VALID(n) ((NODE (n))->next)
|
||||||
|
#define WALK_LIST(n,list) for(n=HEAD(list); NODE_VALID(n); n=NODE_NEXT(n))
|
||||||
#define WALK_LIST_DELSAFE(n,nxt,list) \
|
#define WALK_LIST_DELSAFE(n,nxt,list) \
|
||||||
for(n=HEAD(list); nxt=NODE_NEXT(n); n=(void *) nxt)
|
for(n=HEAD(list); nxt=NODE_NEXT(n); n=(void *) nxt)
|
||||||
/* WALK_LIST_FIRST supposes that called code removes each processed node */
|
/* WALK_LIST_FIRST supposes that called code removes each processed node */
|
||||||
|
|
|
@ -48,7 +48,9 @@ typedef struct birdsock {
|
||||||
char *password; /* Password for MD5 authentication */
|
char *password; /* Password for MD5 authentication */
|
||||||
} sock;
|
} sock;
|
||||||
|
|
||||||
sock *sk_new(pool *); /* Allocate new socket */
|
sock *sock_new(pool *); /* Allocate new socket */
|
||||||
|
#define sk_new(X) sock_new(X) /* Wrapper to avoid name collision with OpenSSL */
|
||||||
|
|
||||||
int sk_open(sock *); /* Open socket */
|
int sk_open(sock *); /* Open socket */
|
||||||
int sk_send(sock *, unsigned len); /* Send data, <0=err, >0=ok, 0=sleep */
|
int sk_send(sock *, unsigned len); /* Send data, <0=err, >0=ok, 0=sleep */
|
||||||
int sk_send_to(sock *, unsigned len, ip_addr to, unsigned port); /* sk_send to given destination */
|
int sk_send_to(sock *, unsigned len, ip_addr to, unsigned port); /* sk_send to given destination */
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Summary: BIRD Internet Routing Daemon
|
Summary: BIRD Internet Routing Daemon
|
||||||
Name: bird
|
Name: bird
|
||||||
Version: 1.3.7
|
Version: 1.3.8
|
||||||
Release: 1
|
Release: 1
|
||||||
Copyright: GPL
|
Copyright: GPL
|
||||||
Group: Networking/Daemons
|
Group: Networking/Daemons
|
||||||
|
|
|
@ -251,7 +251,6 @@ cli_command(struct cli *c)
|
||||||
bzero(&f, sizeof(f));
|
bzero(&f, sizeof(f));
|
||||||
f.mem = c->parser_pool;
|
f.mem = c->parser_pool;
|
||||||
cf_read_hook = cli_cmd_read_hook;
|
cf_read_hook = cli_cmd_read_hook;
|
||||||
cf_open_hook = NULL;
|
|
||||||
cli_rh_pos = c->rx_buf;
|
cli_rh_pos = c->rx_buf;
|
||||||
cli_rh_len = strlen(c->rx_buf);
|
cli_rh_len = strlen(c->rx_buf);
|
||||||
cli_rh_trick_flag = 0;
|
cli_rh_trick_flag = 0;
|
||||||
|
|
|
@ -47,7 +47,7 @@ CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILT
|
||||||
CF_KEYWORDS(LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE)
|
CF_KEYWORDS(LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE)
|
||||||
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
|
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
|
||||||
CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, MAX, FLUSH)
|
CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, MAX, FLUSH)
|
||||||
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION)
|
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
|
||||||
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC)
|
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC)
|
||||||
|
|
||||||
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
|
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
|
||||||
|
@ -65,7 +65,7 @@ CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
|
||||||
%type <ro> roa_args
|
%type <ro> roa_args
|
||||||
%type <rot> roa_table_arg
|
%type <rot> roa_table_arg
|
||||||
%type <sd> sym_args
|
%type <sd> sym_args
|
||||||
%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport roa_mode limit_action
|
%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport roa_mode limit_action tab_sorted
|
||||||
%type <ps> proto_patt proto_patt2
|
%type <ps> proto_patt proto_patt2
|
||||||
%type <g> limit_spec
|
%type <g> limit_spec
|
||||||
|
|
||||||
|
@ -112,10 +112,17 @@ listen_opt:
|
||||||
|
|
||||||
/* Creation of routing tables */
|
/* Creation of routing tables */
|
||||||
|
|
||||||
|
tab_sorted:
|
||||||
|
{ $$ = 0; }
|
||||||
|
| SORTED { $$ = 1; }
|
||||||
|
;
|
||||||
|
|
||||||
CF_ADDTO(conf, newtab)
|
CF_ADDTO(conf, newtab)
|
||||||
|
|
||||||
newtab: TABLE SYM {
|
newtab: TABLE SYM tab_sorted {
|
||||||
rt_new_table($2);
|
struct rtable_config *cf;
|
||||||
|
cf = rt_new_table($2);
|
||||||
|
cf->sorted = $3;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
14
nest/proto.c
14
nest/proto.c
|
@ -512,20 +512,22 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
|
||||||
else if (!p->disabled && nc->disabled)
|
else if (!p->disabled && nc->disabled)
|
||||||
log(L_INFO "Disabling protocol %s", p->name);
|
log(L_INFO "Disabling protocol %s", p->name);
|
||||||
|
|
||||||
PD(p, "Restarting");
|
|
||||||
p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART;
|
p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART;
|
||||||
p->cf_new = nc;
|
p->cf_new = nc;
|
||||||
}
|
}
|
||||||
else
|
else if (!shutting_down)
|
||||||
{
|
{
|
||||||
if (!shutting_down)
|
log(L_INFO "Removing protocol %s", p->name);
|
||||||
log(L_INFO "Removing protocol %s", p->name);
|
|
||||||
PD(p, "Unconfigured");
|
|
||||||
p->down_code = PDC_CF_REMOVE;
|
p->down_code = PDC_CF_REMOVE;
|
||||||
p->cf_new = NULL;
|
p->cf_new = NULL;
|
||||||
}
|
}
|
||||||
p->reconfiguring = 1;
|
else /* global shutdown */
|
||||||
|
{
|
||||||
|
p->down_code = PDC_CMD_SHUTDOWN;
|
||||||
|
p->cf_new = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->reconfiguring = 1;
|
||||||
config_add_obstacle(old);
|
config_add_obstacle(old);
|
||||||
proto_rethink_goal(p);
|
proto_rethink_goal(p);
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,8 +222,9 @@ struct proto_spec {
|
||||||
#define PDC_CF_RESTART 0x03 /* Restart due to reconfiguration */
|
#define PDC_CF_RESTART 0x03 /* Restart due to reconfiguration */
|
||||||
#define PDC_CMD_DISABLE 0x11 /* Result of disable command */
|
#define PDC_CMD_DISABLE 0x11 /* Result of disable command */
|
||||||
#define PDC_CMD_RESTART 0x12 /* Result of restart command */
|
#define PDC_CMD_RESTART 0x12 /* Result of restart command */
|
||||||
|
#define PDC_CMD_SHUTDOWN 0x13 /* Result of global shutdown */
|
||||||
#define PDC_IN_LIMIT_HIT 0x21 /* Route import limit reached */
|
#define PDC_IN_LIMIT_HIT 0x21 /* Route import limit reached */
|
||||||
#define PDC_OUT_LIMIT_HIT 0x22 /* Route export limit reached - not implemented */
|
#define PDC_OUT_LIMIT_HIT 0x22 /* Route export limit reached */
|
||||||
|
|
||||||
|
|
||||||
void *proto_new(struct proto_config *, unsigned size);
|
void *proto_new(struct proto_config *, unsigned size);
|
||||||
|
|
|
@ -121,6 +121,7 @@ struct rtable_config {
|
||||||
struct proto_config *krt_attached; /* Kernel syncer attached to this table */
|
struct proto_config *krt_attached; /* Kernel syncer attached to this table */
|
||||||
int gc_max_ops; /* Maximum number of operations before GC is run */
|
int gc_max_ops; /* Maximum number of operations before GC is run */
|
||||||
int gc_min_time; /* Minimum time between two consecutive GC runs */
|
int gc_min_time; /* Minimum time between two consecutive GC runs */
|
||||||
|
byte sorted; /* Routes of network are sorted according to rte_better() */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct rtable {
|
typedef struct rtable {
|
||||||
|
@ -219,11 +220,12 @@ typedef struct rte {
|
||||||
} u;
|
} u;
|
||||||
} rte;
|
} rte;
|
||||||
|
|
||||||
#define REF_COW 1 /* Copy this rte on write */
|
#define REF_COW 1 /* Copy this rte on write */
|
||||||
|
|
||||||
/* Types of route announcement, also used as flags */
|
/* Types of route announcement, also used as flags */
|
||||||
#define RA_OPTIMAL 1 /* Announcement of optimal route change */
|
#define RA_OPTIMAL 1 /* Announcement of optimal route change */
|
||||||
#define RA_ANY 2 /* Announcement of any route change */
|
#define RA_ACCEPTED 2 /* Announcement of first accepted route */
|
||||||
|
#define RA_ANY 3 /* Announcement of any route change */
|
||||||
|
|
||||||
struct config;
|
struct config;
|
||||||
|
|
||||||
|
|
506
nest/rt-table.c
506
nest/rt-table.c
|
@ -182,97 +182,81 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg)
|
||||||
rte_trace(p, e, '<', msg);
|
rte_trace(p, e, '<', msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static rte *
|
||||||
* do_rte_announce - announce new rte to protocol
|
export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa, int silent)
|
||||||
* @ah: pointer to announce hook
|
|
||||||
* @type: announce type (RA_ANY or RA_OPTIMAL)
|
|
||||||
* @net: pointer to announced network
|
|
||||||
* @new: new rte or NULL
|
|
||||||
* @old: previous rte or NULL
|
|
||||||
* @tmpa: new rte attributes (possibly modified by filter)
|
|
||||||
* @refeed: whether we are refeeding protocol
|
|
||||||
*/
|
|
||||||
static inline void
|
|
||||||
do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
|
|
||||||
{
|
{
|
||||||
struct proto *p = ah->proto;
|
struct proto *p = ah->proto;
|
||||||
struct filter *filter = ah->out_filter;
|
struct filter *filter = ah->out_filter;
|
||||||
struct proto_stats *stats = ah->stats;
|
struct proto_stats *stats = ah->stats;
|
||||||
|
ea_list *tmpb = NULL;
|
||||||
|
rte *rt;
|
||||||
|
int v;
|
||||||
|
|
||||||
rte *new0 = new;
|
rt = rt0;
|
||||||
rte *old0 = old;
|
*rt_free = NULL;
|
||||||
int ok;
|
|
||||||
|
|
||||||
if (new)
|
/* If called does not care for eattrs, we prepare one internally */
|
||||||
|
if (!tmpa)
|
||||||
{
|
{
|
||||||
stats->exp_updates_received++;
|
struct proto *src = rt->attrs->proto;
|
||||||
|
tmpb = src->make_tmp_attrs ? src->make_tmp_attrs(rt, rte_update_pool) : NULL;
|
||||||
char *drop_reason = NULL;
|
tmpa = &tmpb;
|
||||||
if ((ok = p->import_control ? p->import_control(p, &new, &tmpa, rte_update_pool) : 0) < 0)
|
|
||||||
{
|
|
||||||
stats->exp_updates_rejected++;
|
|
||||||
drop_reason = "rejected by protocol";
|
|
||||||
}
|
|
||||||
else if (ok)
|
|
||||||
rte_trace_out(D_FILTERS, p, new, "forced accept by protocol");
|
|
||||||
else if ((filter == FILTER_REJECT) ||
|
|
||||||
(filter && f_run(filter, &new, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))
|
|
||||||
{
|
|
||||||
stats->exp_updates_filtered++;
|
|
||||||
drop_reason = "filtered out";
|
|
||||||
}
|
|
||||||
if (drop_reason)
|
|
||||||
{
|
|
||||||
rte_trace_out(D_FILTERS, p, new, drop_reason);
|
|
||||||
if (new != new0)
|
|
||||||
rte_free(new);
|
|
||||||
new = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
stats->exp_withdraws_received++;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is a tricky part - we don't know whether route 'old' was
|
|
||||||
* exported to protocol 'p' or was filtered by the export filter.
|
|
||||||
* We try tu run the export filter to know this to have a correct
|
|
||||||
* value in 'old' argument of rte_update (and proper filter value)
|
|
||||||
*
|
|
||||||
* FIXME - this is broken because 'configure soft' may change
|
|
||||||
* filters but keep routes. Refeed is expected to be called after
|
|
||||||
* change of the filters and with old == new, therefore we do not
|
|
||||||
* even try to run the filter on an old route, This may lead to
|
|
||||||
* 'spurious withdraws' but ensure that there are no 'missing
|
|
||||||
* withdraws'.
|
|
||||||
*
|
|
||||||
* This is not completely safe as there is a window between
|
|
||||||
* reconfiguration and the end of refeed - if a newly filtered
|
|
||||||
* route disappears during this period, proper withdraw is not
|
|
||||||
* sent (because old would be also filtered) and the route is
|
|
||||||
* not refeeded (because it disappeared before that).
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (old && !refeed)
|
|
||||||
{
|
|
||||||
if (filter == FILTER_REJECT)
|
|
||||||
old = NULL;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ea_list *tmpb = p->make_tmp_attrs ? p->make_tmp_attrs(old, rte_update_pool) : NULL;
|
|
||||||
ok = p->import_control ? p->import_control(p, &old, &tmpb, rte_update_pool) : 0;
|
|
||||||
if (ok < 0 || (!ok && filter && f_run(filter, &old, &tmpb, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))
|
|
||||||
{
|
|
||||||
if (old != old0)
|
|
||||||
rte_free(old);
|
|
||||||
old = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v = p->import_control ? p->import_control(p, &rt, tmpa, rte_update_pool) : 0;
|
||||||
|
if (v < 0)
|
||||||
|
{
|
||||||
|
if (silent)
|
||||||
|
goto reject;
|
||||||
|
|
||||||
|
stats->exp_updates_rejected++;
|
||||||
|
rte_trace_out(D_FILTERS, p, rt, "rejected by protocol");
|
||||||
|
goto reject;
|
||||||
|
}
|
||||||
|
if (v > 0)
|
||||||
|
{
|
||||||
|
if (!silent)
|
||||||
|
rte_trace_out(D_FILTERS, p, rt, "forced accept by protocol");
|
||||||
|
goto accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = filter && ((filter == FILTER_REJECT) ||
|
||||||
|
(f_run(filter, &rt, tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT));
|
||||||
|
if (v)
|
||||||
|
{
|
||||||
|
if (silent)
|
||||||
|
goto reject;
|
||||||
|
|
||||||
|
stats->exp_updates_filtered++;
|
||||||
|
rte_trace_out(D_FILTERS, p, rt, "filtered out");
|
||||||
|
goto reject;
|
||||||
|
}
|
||||||
|
|
||||||
|
accept:
|
||||||
|
if (rt != rt0)
|
||||||
|
*rt_free = rt;
|
||||||
|
return rt;
|
||||||
|
|
||||||
|
reject:
|
||||||
|
/* Discard temporary rte */
|
||||||
|
if (rt != rt0)
|
||||||
|
rte_free(rt);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
|
||||||
|
{
|
||||||
|
struct proto *p = ah->proto;
|
||||||
|
struct proto_stats *stats = ah->stats;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* First, apply export limit.
|
||||||
|
*
|
||||||
* Export route limits has several problems. Because exp_routes
|
* Export route limits has several problems. Because exp_routes
|
||||||
* counter is reset before refeed, we don't really know whether
|
* counter is reset before refeed, we don't really know whether
|
||||||
* limit is breached and whether the update is new or not Therefore
|
* limit is breached and whether the update is new or not. Therefore
|
||||||
* the number of really exported routes may exceed the limit
|
* the number of really exported routes may exceed the limit
|
||||||
* temporarily (routes exported before and new routes in refeed).
|
* temporarily (routes exported before and new routes in refeed).
|
||||||
*
|
*
|
||||||
|
@ -305,15 +289,13 @@ do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, r
|
||||||
stats->exp_routes++; /* see note above */
|
stats->exp_routes++; /* see note above */
|
||||||
stats->exp_updates_rejected++;
|
stats->exp_updates_rejected++;
|
||||||
rte_trace_out(D_FILTERS, p, new, "rejected [limit]");
|
rte_trace_out(D_FILTERS, p, new, "rejected [limit]");
|
||||||
if (new != new0)
|
|
||||||
rte_free(new);
|
|
||||||
new = NULL;
|
new = NULL;
|
||||||
|
|
||||||
|
if (!old)
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME - This is broken because of incorrect 'old' value (see above) */
|
|
||||||
if (!new && !old)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (new)
|
if (new)
|
||||||
stats->exp_updates_accepted++;
|
stats->exp_updates_accepted++;
|
||||||
|
@ -349,11 +331,172 @@ do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, r
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
p->rt_notify(p, ah->table, net, new, old, new->attrs->eattrs);
|
p->rt_notify(p, ah->table, net, new, old, new->attrs->eattrs);
|
||||||
|
}
|
||||||
|
|
||||||
if (new && new != new0) /* Discard temporary rte's */
|
static void
|
||||||
rte_free(new);
|
rt_notify_basic(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
|
||||||
if (old && old != old0)
|
{
|
||||||
rte_free(old);
|
// struct proto *p = ah->proto;
|
||||||
|
struct proto_stats *stats = ah->stats;
|
||||||
|
|
||||||
|
rte *new_free = NULL;
|
||||||
|
rte *old_free = NULL;
|
||||||
|
|
||||||
|
if (new)
|
||||||
|
stats->exp_updates_received++;
|
||||||
|
else
|
||||||
|
stats->exp_withdraws_received++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a tricky part - we don't know whether route 'old' was
|
||||||
|
* exported to protocol 'p' or was filtered by the export filter.
|
||||||
|
* We try to run the export filter to know this to have a correct
|
||||||
|
* value in 'old' argument of rte_update (and proper filter value)
|
||||||
|
*
|
||||||
|
* FIXME - this is broken because 'configure soft' may change
|
||||||
|
* filters but keep routes. Refeed is expected to be called after
|
||||||
|
* change of the filters and with old == new, therefore we do not
|
||||||
|
* even try to run the filter on an old route, This may lead to
|
||||||
|
* 'spurious withdraws' but ensure that there are no 'missing
|
||||||
|
* withdraws'.
|
||||||
|
*
|
||||||
|
* This is not completely safe as there is a window between
|
||||||
|
* reconfiguration and the end of refeed - if a newly filtered
|
||||||
|
* route disappears during this period, proper withdraw is not
|
||||||
|
* sent (because old would be also filtered) and the route is
|
||||||
|
* not refeeded (because it disappeared before that).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (new)
|
||||||
|
new = export_filter(ah, new, &new_free, &tmpa, 0);
|
||||||
|
|
||||||
|
if (old && !refeed)
|
||||||
|
old = export_filter(ah, old, &old_free, NULL, 1);
|
||||||
|
|
||||||
|
/* FIXME - This is broken because of incorrect 'old' value (see above) */
|
||||||
|
if (!new && !old)
|
||||||
|
return;
|
||||||
|
|
||||||
|
do_rt_notify(ah, net, new, old, tmpa, refeed);
|
||||||
|
|
||||||
|
/* Discard temporary rte's */
|
||||||
|
if (new_free)
|
||||||
|
rte_free(new_free);
|
||||||
|
if (old_free)
|
||||||
|
rte_free(old_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed, rte *before_old,
|
||||||
|
ea_list *tmpa, int feed)
|
||||||
|
{
|
||||||
|
// struct proto *p = ah->proto;
|
||||||
|
struct proto_stats *stats = ah->stats;
|
||||||
|
|
||||||
|
rte *new_best = NULL;
|
||||||
|
rte *old_best = NULL;
|
||||||
|
rte *new_free = NULL;
|
||||||
|
rte *old_free = NULL;
|
||||||
|
rte *r;
|
||||||
|
|
||||||
|
/* Used to track whether we met old_changed position. If it is NULL
|
||||||
|
it was the first and met it implicitly before current best route. */
|
||||||
|
int old_meet = (old_changed && !before_old) ? 1 : 0;
|
||||||
|
|
||||||
|
if (new_changed)
|
||||||
|
stats->exp_updates_received++;
|
||||||
|
else
|
||||||
|
stats->exp_withdraws_received++;
|
||||||
|
|
||||||
|
/* First, find the new_best route - first accepted by filters */
|
||||||
|
for (r=net->routes; r; r=r->next)
|
||||||
|
{
|
||||||
|
if (new_best = export_filter(ah, r, &new_free, &tmpa, 0))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Note if we walked around the position of old_changed route */
|
||||||
|
if (r == before_old)
|
||||||
|
old_meet = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Second, handle the feed case. That means we do not care for
|
||||||
|
* old_best. It is NULL for feed, and the new_best for refeed.
|
||||||
|
* For refeed, there is a hack similar to one in rt_notify_basic()
|
||||||
|
* to ensure withdraws in case of changed filters
|
||||||
|
*/
|
||||||
|
if (feed)
|
||||||
|
{
|
||||||
|
if (feed == 2) /* refeed */
|
||||||
|
old_best = new_best ? new_best : net->routes;
|
||||||
|
else
|
||||||
|
old_best = NULL;
|
||||||
|
|
||||||
|
if (!new_best && !old_best)
|
||||||
|
return;
|
||||||
|
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now, we find the old_best route. Generally, it is the same as the
|
||||||
|
* new_best, unless new_best is the same as new_changed or
|
||||||
|
* old_changed is accepted before new_best.
|
||||||
|
*
|
||||||
|
* There are four cases:
|
||||||
|
*
|
||||||
|
* - We would find and accept old_changed before new_best, therefore
|
||||||
|
* old_changed is old_best. In remaining cases we suppose this
|
||||||
|
* is not true.
|
||||||
|
*
|
||||||
|
* - We found no new_best, therefore there is also no old_best and
|
||||||
|
* we ignore this withdraw.
|
||||||
|
*
|
||||||
|
* - We found new_best different than new_changed, therefore
|
||||||
|
* old_best is the same as new_best and we ignore this update.
|
||||||
|
*
|
||||||
|
* - We found new_best the same as new_changed, therefore it cannot
|
||||||
|
* be old_best and we have to continue search for old_best.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* First case */
|
||||||
|
if (old_meet)
|
||||||
|
if (old_best = export_filter(ah, old_changed, &old_free, NULL, 1))
|
||||||
|
goto found;
|
||||||
|
|
||||||
|
/* Second case */
|
||||||
|
if (!new_best)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Third case, we use r instead of new_best, because export_filter() could change it */
|
||||||
|
if (r != new_changed)
|
||||||
|
{
|
||||||
|
if (new_free)
|
||||||
|
rte_free(new_free);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fourth case */
|
||||||
|
for (r=r->next; r; r=r->next)
|
||||||
|
{
|
||||||
|
if (old_best = export_filter(ah, r, &old_free, NULL, 1))
|
||||||
|
goto found;
|
||||||
|
|
||||||
|
if (r == before_old)
|
||||||
|
if (old_best = export_filter(ah, old_changed, &old_free, NULL, 1))
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implicitly, old_best is NULL and new_best is non-NULL */
|
||||||
|
|
||||||
|
found:
|
||||||
|
do_rt_notify(ah, net, new_best, old_best, tmpa, (feed == 2));
|
||||||
|
|
||||||
|
/* Discard temporary rte's */
|
||||||
|
if (new_free)
|
||||||
|
rte_free(new_free);
|
||||||
|
if (old_free)
|
||||||
|
rte_free(old_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -386,7 +529,7 @@ do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, r
|
||||||
* the protocol gets called.
|
* the protocol gets called.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, ea_list *tmpa)
|
rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *before_old, ea_list *tmpa)
|
||||||
{
|
{
|
||||||
struct announce_hook *a;
|
struct announce_hook *a;
|
||||||
|
|
||||||
|
@ -405,11 +548,13 @@ rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, ea_list *
|
||||||
{
|
{
|
||||||
ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING);
|
ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING);
|
||||||
if (a->proto->accept_ra_types == type)
|
if (a->proto->accept_ra_types == type)
|
||||||
do_rte_announce(a, type, net, new, old, tmpa, 0);
|
if (type == RA_ACCEPTED)
|
||||||
|
rt_notify_accepted(a, net, new, old, before_old, tmpa, 0);
|
||||||
|
else
|
||||||
|
rt_notify_basic(a, net, new, old, tmpa, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
rte_validate(rte *e)
|
rte_validate(rte *e)
|
||||||
{
|
{
|
||||||
|
@ -472,9 +617,10 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
||||||
struct proto *p = ah->proto;
|
struct proto *p = ah->proto;
|
||||||
struct rtable *table = ah->table;
|
struct rtable *table = ah->table;
|
||||||
struct proto_stats *stats = ah->stats;
|
struct proto_stats *stats = ah->stats;
|
||||||
|
rte *before_old = NULL;
|
||||||
rte *old_best = net->routes;
|
rte *old_best = net->routes;
|
||||||
rte *old = NULL;
|
rte *old = NULL;
|
||||||
rte **k, *r, *s;
|
rte **k;
|
||||||
|
|
||||||
k = &net->routes; /* Find and remove original route from the same protocol */
|
k = &net->routes; /* Find and remove original route from the same protocol */
|
||||||
while (old = *k)
|
while (old = *k)
|
||||||
|
@ -519,8 +665,12 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
k = &old->next;
|
k = &old->next;
|
||||||
|
before_old = old;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!old)
|
||||||
|
before_old = NULL;
|
||||||
|
|
||||||
if (!old && !new)
|
if (!old && !new)
|
||||||
{
|
{
|
||||||
stats->imp_withdraws_ignored++;
|
stats->imp_withdraws_ignored++;
|
||||||
|
@ -552,82 +702,93 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
||||||
if (old)
|
if (old)
|
||||||
stats->imp_routes--;
|
stats->imp_routes--;
|
||||||
|
|
||||||
rte_announce(table, RA_ANY, net, new, old, tmpa);
|
if (table->config->sorted)
|
||||||
|
|
||||||
if (src->rte_recalculate && src->rte_recalculate(table, net, new, old, old_best))
|
|
||||||
goto do_recalculate;
|
|
||||||
|
|
||||||
if (new && rte_better(new, old_best))
|
|
||||||
{
|
{
|
||||||
/* The first case - the new route is cleary optimal, we link it
|
/* If routes are sorted, just insert new route to appropriate position */
|
||||||
at the first position and announce it */
|
|
||||||
|
|
||||||
rte_trace_in(D_ROUTES, p, new, "added [best]");
|
|
||||||
rte_announce(table, RA_OPTIMAL, net, new, old_best, tmpa);
|
|
||||||
new->next = net->routes;
|
|
||||||
net->routes = new;
|
|
||||||
}
|
|
||||||
else if (old == old_best)
|
|
||||||
{
|
|
||||||
/* The second case - the old best route disappeared, we add the
|
|
||||||
new route (if we have any) to the list (we don't care about
|
|
||||||
position) and then we elect the new optimal route and relink
|
|
||||||
that route at the first position and announce it. New optimal
|
|
||||||
route might be NULL if there is no more routes */
|
|
||||||
|
|
||||||
do_recalculate:
|
|
||||||
/* Add the new route to the list */
|
|
||||||
if (new)
|
if (new)
|
||||||
{
|
{
|
||||||
rte_trace_in(D_ROUTES, p, new, "added");
|
if (before_old && !rte_better(new, before_old))
|
||||||
|
k = &before_old->next;
|
||||||
|
else
|
||||||
|
k = &net->routes;
|
||||||
|
|
||||||
|
for (; *k; k=&(*k)->next)
|
||||||
|
if (rte_better(new, *k))
|
||||||
|
break;
|
||||||
|
|
||||||
|
new->next = *k;
|
||||||
|
*k = new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* If routes are not sorted, find the best route and move it on
|
||||||
|
the first position. There are several optimized cases. */
|
||||||
|
|
||||||
|
if (src->rte_recalculate && src->rte_recalculate(table, net, new, old, old_best))
|
||||||
|
goto do_recalculate;
|
||||||
|
|
||||||
|
if (new && rte_better(new, old_best))
|
||||||
|
{
|
||||||
|
/* The first case - the new route is cleary optimal,
|
||||||
|
we link it at the first position */
|
||||||
|
|
||||||
new->next = net->routes;
|
new->next = net->routes;
|
||||||
net->routes = new;
|
net->routes = new;
|
||||||
}
|
}
|
||||||
|
else if (old == old_best)
|
||||||
/* Find new optimal route */
|
|
||||||
r = NULL;
|
|
||||||
for (s=net->routes; s; s=s->next)
|
|
||||||
if (rte_better(s, r))
|
|
||||||
r = s;
|
|
||||||
|
|
||||||
/* Announce optimal route */
|
|
||||||
rte_announce(table, RA_OPTIMAL, net, r, old_best, tmpa);
|
|
||||||
|
|
||||||
/* And relink it (if there is any) */
|
|
||||||
if (r)
|
|
||||||
{
|
{
|
||||||
k = &net->routes;
|
/* The second case - the old best route disappeared, we add the
|
||||||
while (s = *k)
|
new route (if we have any) to the list (we don't care about
|
||||||
|
position) and then we elect the new optimal route and relink
|
||||||
|
that route at the first position and announce it. New optimal
|
||||||
|
route might be NULL if there is no more routes */
|
||||||
|
|
||||||
|
do_recalculate:
|
||||||
|
/* Add the new route to the list */
|
||||||
|
if (new)
|
||||||
{
|
{
|
||||||
if (s == r)
|
new->next = net->routes;
|
||||||
{
|
net->routes = new;
|
||||||
*k = r->next;
|
}
|
||||||
break;
|
|
||||||
}
|
/* Find a new optimal route (if there is any) */
|
||||||
k = &s->next;
|
if (net->routes)
|
||||||
|
{
|
||||||
|
rte **bp = &net->routes;
|
||||||
|
for (k=&(*bp)->next; *k; k=&(*k)->next)
|
||||||
|
if (rte_better(*k, *bp))
|
||||||
|
bp = k;
|
||||||
|
|
||||||
|
/* And relink it */
|
||||||
|
rte *best = *bp;
|
||||||
|
*bp = best->next;
|
||||||
|
best->next = net->routes;
|
||||||
|
net->routes = best;
|
||||||
}
|
}
|
||||||
r->next = net->routes;
|
|
||||||
net->routes = r;
|
|
||||||
}
|
}
|
||||||
else if (table->gc_counter++ >= table->config->gc_max_ops &&
|
else if (new)
|
||||||
table->gc_time + table->config->gc_min_time <= now)
|
{
|
||||||
rt_schedule_gc(table);
|
/* The third case - the new route is not better than the old
|
||||||
}
|
best route (therefore old_best != NULL) and the old best
|
||||||
else if (new)
|
route was not removed (therefore old_best == net->routes).
|
||||||
{
|
We just link the new route after the old best route. */
|
||||||
/* The third case - the new route is not better than the old
|
|
||||||
best route (therefore old_best != NULL) and the old best
|
|
||||||
route was not removed (therefore old_best == net->routes).
|
|
||||||
We just link the new route after the old best route. */
|
|
||||||
|
|
||||||
ASSERT(net->routes != NULL);
|
ASSERT(net->routes != NULL);
|
||||||
new->next = net->routes->next;
|
new->next = net->routes->next;
|
||||||
net->routes->next = new;
|
net->routes->next = new;
|
||||||
rte_trace_in(D_ROUTES, p, new, "added");
|
}
|
||||||
|
/* The fourth (empty) case - suboptimal route was removed, nothing to do */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Log the route removal */
|
if (new)
|
||||||
if (!new && old && (p->debug & D_ROUTES))
|
new->lastmod = now;
|
||||||
|
|
||||||
|
/* Log the route change */
|
||||||
|
if (new)
|
||||||
|
rte_trace_in(D_ROUTES, p, new, net->routes == new ? "added [best]" : "added");
|
||||||
|
|
||||||
|
if (!new && (p->debug & D_ROUTES))
|
||||||
{
|
{
|
||||||
if (old != old_best)
|
if (old != old_best)
|
||||||
rte_trace_in(D_ROUTES, p, old, "removed");
|
rte_trace_in(D_ROUTES, p, old, "removed");
|
||||||
|
@ -637,6 +798,18 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
||||||
rte_trace_in(D_ROUTES, p, old, "removed [sole]");
|
rte_trace_in(D_ROUTES, p, old, "removed [sole]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Propagate the route change */
|
||||||
|
rte_announce(table, RA_ANY, net, new, old, NULL, tmpa);
|
||||||
|
if (net->routes != old_best)
|
||||||
|
rte_announce(table, RA_OPTIMAL, net, net->routes, old_best, NULL, tmpa);
|
||||||
|
if (table->config->sorted)
|
||||||
|
rte_announce(table, RA_ACCEPTED, net, new, old, before_old, tmpa);
|
||||||
|
|
||||||
|
if (!net->routes &&
|
||||||
|
(table->gc_counter++ >= table->config->gc_max_ops) &&
|
||||||
|
(table->gc_time + table->config->gc_min_time <= now))
|
||||||
|
rt_schedule_gc(table);
|
||||||
|
|
||||||
if (old)
|
if (old)
|
||||||
{
|
{
|
||||||
if (p->rte_remove)
|
if (p->rte_remove)
|
||||||
|
@ -645,7 +818,6 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
||||||
}
|
}
|
||||||
if (new)
|
if (new)
|
||||||
{
|
{
|
||||||
new->lastmod = now;
|
|
||||||
if (p->rte_insert)
|
if (p->rte_insert)
|
||||||
p->rte_insert(net, new);
|
p->rte_insert(net, new);
|
||||||
}
|
}
|
||||||
|
@ -777,7 +949,7 @@ rte_announce_i(rtable *tab, unsigned type, net *n, rte *new, rte *old)
|
||||||
rte_update_lock();
|
rte_update_lock();
|
||||||
src = new->attrs->proto;
|
src = new->attrs->proto;
|
||||||
tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(new, rte_update_pool) : NULL;
|
tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(new, rte_update_pool) : NULL;
|
||||||
rte_announce(tab, type, n, new, old, tmpa);
|
rte_announce(tab, type, n, new, old, NULL, tmpa);
|
||||||
rte_update_unlock();
|
rte_update_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1214,6 +1386,10 @@ rt_next_hop_update(rtable *tab)
|
||||||
struct rtable_config *
|
struct rtable_config *
|
||||||
rt_new_table(struct symbol *s)
|
rt_new_table(struct symbol *s)
|
||||||
{
|
{
|
||||||
|
/* Hack that allows to 'redefine' the master table */
|
||||||
|
if ((s->class == SYM_TABLE) && (s->def == new_config->master_rtc))
|
||||||
|
return s->def;
|
||||||
|
|
||||||
struct rtable_config *c = cfg_allocz(sizeof(struct rtable_config));
|
struct rtable_config *c = cfg_allocz(sizeof(struct rtable_config));
|
||||||
|
|
||||||
cf_define_symbol(s, SYM_TABLE, c);
|
cf_define_symbol(s, SYM_TABLE, c);
|
||||||
|
@ -1296,6 +1472,8 @@ rt_commit(struct config *new, struct config *old)
|
||||||
r->table = ot;
|
r->table = ot;
|
||||||
ot->name = r->name;
|
ot->name = r->name;
|
||||||
ot->config = r;
|
ot->config = r;
|
||||||
|
if (o->sorted != r->sorted)
|
||||||
|
log(L_WARN "Reconfiguration of rtable sorted flag not implemented");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1324,12 +1502,15 @@ rt_commit(struct config *new, struct config *old)
|
||||||
static inline void
|
static inline void
|
||||||
do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e)
|
do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e)
|
||||||
{
|
{
|
||||||
struct proto *q = e->attrs->proto;
|
struct proto *src = e->attrs->proto;
|
||||||
ea_list *tmpa;
|
ea_list *tmpa;
|
||||||
|
|
||||||
rte_update_lock();
|
rte_update_lock();
|
||||||
tmpa = q->make_tmp_attrs ? q->make_tmp_attrs(e, rte_update_pool) : NULL;
|
tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(e, rte_update_pool) : NULL;
|
||||||
do_rte_announce(h, type, n, e, p->refeeding ? e : NULL, tmpa, p->refeeding);
|
if (type == RA_ACCEPTED)
|
||||||
|
rt_notify_accepted(h, n, e, NULL, NULL, tmpa, p->refeeding ? 2 : 1);
|
||||||
|
else
|
||||||
|
rt_notify_basic(h, n, e, p->refeeding ? e : NULL, tmpa, p->refeeding);
|
||||||
rte_update_unlock();
|
rte_update_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1372,12 +1553,13 @@ again:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p->accept_ra_types == RA_OPTIMAL)
|
if ((p->accept_ra_types == RA_OPTIMAL) ||
|
||||||
|
(p->accept_ra_types == RA_ACCEPTED))
|
||||||
if (e)
|
if (e)
|
||||||
{
|
{
|
||||||
if (p->core_state != FS_FEEDING)
|
if (p->core_state != FS_FEEDING)
|
||||||
return 1; /* In the meantime, the protocol fell down. */
|
return 1; /* In the meantime, the protocol fell down. */
|
||||||
do_feed_baby(p, RA_OPTIMAL, h, n, e);
|
do_feed_baby(p, p->accept_ra_types, h, n, e);
|
||||||
max_feed--;
|
max_feed--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1258,7 +1258,8 @@ same_group(rte *r, u32 lpref, u32 lasn)
|
||||||
static inline int
|
static inline int
|
||||||
use_deterministic_med(rte *r)
|
use_deterministic_med(rte *r)
|
||||||
{
|
{
|
||||||
return ((struct bgp_proto *) r->attrs->proto)->cf->deterministic_med;
|
struct proto *P = r->attrs->proto;
|
||||||
|
return (P->proto == &proto_bgp) && ((struct bgp_proto *) P)->cf->deterministic_med;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
@ -870,6 +870,7 @@ bgp_shutdown(struct proto *P)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PDC_CMD_DISABLE:
|
case PDC_CMD_DISABLE:
|
||||||
|
case PDC_CMD_SHUTDOWN:
|
||||||
subcode = 2; // Errcode 6, 2 - administrative shutdown
|
subcode = 2; // Errcode 6, 2 - administrative shutdown
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -923,7 +924,7 @@ bgp_init(struct proto_config *C)
|
||||||
struct proto *P = proto_new(C, sizeof(struct bgp_proto));
|
struct proto *P = proto_new(C, sizeof(struct bgp_proto));
|
||||||
struct bgp_proto *p = (struct bgp_proto *) P;
|
struct bgp_proto *p = (struct bgp_proto *) P;
|
||||||
|
|
||||||
P->accept_ra_types = RA_OPTIMAL;
|
P->accept_ra_types = c->secondary ? RA_ACCEPTED : RA_OPTIMAL;
|
||||||
P->rt_notify = bgp_rt_notify;
|
P->rt_notify = bgp_rt_notify;
|
||||||
P->rte_better = bgp_rte_better;
|
P->rte_better = bgp_rte_better;
|
||||||
P->import_control = bgp_import_control;
|
P->import_control = bgp_import_control;
|
||||||
|
@ -969,6 +970,7 @@ bgp_check_config(struct bgp_config *c)
|
||||||
if (internal && c->rs_client)
|
if (internal && c->rs_client)
|
||||||
cf_error("Only external neighbor can be RS client");
|
cf_error("Only external neighbor can be RS client");
|
||||||
|
|
||||||
|
|
||||||
if (c->multihop && (c->gw_mode == GW_DIRECT))
|
if (c->multihop && (c->gw_mode == GW_DIRECT))
|
||||||
cf_error("Multihop BGP cannot use direct gateway mode");
|
cf_error("Multihop BGP cannot use direct gateway mode");
|
||||||
|
|
||||||
|
@ -976,6 +978,7 @@ bgp_check_config(struct bgp_config *c)
|
||||||
ipa_has_link_scope(c->source_addr)))
|
ipa_has_link_scope(c->source_addr)))
|
||||||
cf_error("Multihop BGP cannot be used with link-local addresses");
|
cf_error("Multihop BGP cannot be used with link-local addresses");
|
||||||
|
|
||||||
|
|
||||||
/* Different default based on rs_client */
|
/* Different default based on rs_client */
|
||||||
if (!c->missing_lladdr)
|
if (!c->missing_lladdr)
|
||||||
c->missing_lladdr = c->rs_client ? MLL_IGNORE : MLL_SELF;
|
c->missing_lladdr = c->rs_client ? MLL_IGNORE : MLL_SELF;
|
||||||
|
@ -987,6 +990,16 @@ bgp_check_config(struct bgp_config *c)
|
||||||
/* Disable after error incompatible with restart limit action */
|
/* Disable after error incompatible with restart limit action */
|
||||||
if (c->c.in_limit && (c->c.in_limit->action == PLA_RESTART) && c->disable_after_error)
|
if (c->c.in_limit && (c->c.in_limit->action == PLA_RESTART) && c->disable_after_error)
|
||||||
c->c.in_limit->action = PLA_DISABLE;
|
c->c.in_limit->action = PLA_DISABLE;
|
||||||
|
|
||||||
|
|
||||||
|
if ((c->gw_mode == GW_RECURSIVE) && c->c.table->sorted)
|
||||||
|
cf_error("BGP in recursive mode prohibits sorted table");
|
||||||
|
|
||||||
|
if (c->deterministic_med && c->c.table->sorted)
|
||||||
|
cf_error("BGP with deterministic MED prohibits sorted table");
|
||||||
|
|
||||||
|
if (c->secondary && !c->c.table->sorted)
|
||||||
|
cf_error("BGP with secondary option requires sorted table");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
|
@ -42,6 +42,7 @@ struct bgp_config {
|
||||||
int advertise_ipv4; /* Whether we should add IPv4 capability advertisement to OPEN message */
|
int advertise_ipv4; /* Whether we should add IPv4 capability advertisement to OPEN message */
|
||||||
int passive; /* Do not initiate outgoing connection */
|
int passive; /* Do not initiate outgoing connection */
|
||||||
int interpret_communities; /* Hardwired handling of well-known communities */
|
int interpret_communities; /* Hardwired handling of well-known communities */
|
||||||
|
int secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */
|
||||||
unsigned connect_retry_time;
|
unsigned connect_retry_time;
|
||||||
unsigned hold_time, initial_hold_time;
|
unsigned hold_time, initial_hold_time;
|
||||||
unsigned keepalive_time;
|
unsigned keepalive_time;
|
||||||
|
|
|
@ -25,7 +25,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
|
||||||
CLUSTER, ID, AS4, ADVERTISE, IPV4, CAPABILITIES, LIMIT, PASSIVE,
|
CLUSTER, ID, AS4, ADVERTISE, IPV4, CAPABILITIES, LIMIT, PASSIVE,
|
||||||
PREFER, OLDER, MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH,
|
PREFER, OLDER, MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH,
|
||||||
INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
|
INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
|
||||||
TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC)
|
TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC,
|
||||||
|
SECONDARY)
|
||||||
|
|
||||||
CF_GRAMMAR
|
CF_GRAMMAR
|
||||||
|
|
||||||
|
@ -105,6 +106,7 @@ bgp_proto:
|
||||||
}
|
}
|
||||||
| bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
|
| bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
|
||||||
| bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
|
| bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
|
||||||
|
| bgp_proto SECONDARY bool ';' { BGP_CFG->secondary = $3; }
|
||||||
| bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
|
| bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
|
||||||
| bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; }
|
| bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; }
|
||||||
;
|
;
|
||||||
|
|
|
@ -502,15 +502,17 @@ ospf_lsupd_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#else /* OSPFv3 */
|
#else /* OSPFv3 */
|
||||||
|
u16 scope = ntoht(lsa->type) & LSA_SCOPE_MASK;
|
||||||
|
|
||||||
/* 4.5.1 (2) */
|
/* 4.5.1 (2) */
|
||||||
if ((LSA_SCOPE(lsa) == LSA_SCOPE_AS) && !oa_is_ext(ifa->oa))
|
if ((scope == LSA_SCOPE_AS) && !oa_is_ext(ifa->oa))
|
||||||
{
|
{
|
||||||
log(L_WARN "Received LSA with AS scope in stub area from %I", n->ip);
|
log(L_WARN "Received LSA with AS scope in stub area from %I", n->ip);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 4.5.1 (3) */
|
/* 4.5.1 (3) */
|
||||||
if ((LSA_SCOPE(lsa) == LSA_SCOPE_RES))
|
if (scope == LSA_SCOPE_RES)
|
||||||
{
|
{
|
||||||
log(L_WARN "Received LSA with invalid scope from %I", n->ip);
|
log(L_WARN "Received LSA with invalid scope from %I", n->ip);
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -575,7 +575,7 @@ ospf_rt_notify(struct proto *p, rtable *tbl UNUSED, net * n, rte * new, rte * ol
|
||||||
if (fn->x1 != EXT_EXPORT)
|
if (fn->x1 != EXT_EXPORT)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
flush_ext_lsa(oa, fn);
|
flush_ext_lsa(oa, fn, oa_is_nssa(oa));
|
||||||
|
|
||||||
/* Old external route might blocked some NSSA translation */
|
/* Old external route might blocked some NSSA translation */
|
||||||
if (po->areano > 1)
|
if (po->areano > 1)
|
||||||
|
|
|
@ -1066,7 +1066,7 @@ check_nssa_lsa(struct proto_ospf *po, ort *nf)
|
||||||
originate_ext_lsa(po->backbone, fn, EXT_NSSA, rt_metric, rt_fwaddr, rt_tag, 0);
|
originate_ext_lsa(po->backbone, fn, EXT_NSSA, rt_metric, rt_fwaddr, rt_tag, 0);
|
||||||
|
|
||||||
else if (fn->x1 == EXT_NSSA)
|
else if (fn->x1 == EXT_NSSA)
|
||||||
flush_ext_lsa(po->backbone, fn);
|
flush_ext_lsa(po->backbone, fn, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RFC 2328 16.7. p2 - find new/lost vlink endpoints */
|
/* RFC 2328 16.7. p2 - find new/lost vlink endpoints */
|
||||||
|
@ -1189,7 +1189,7 @@ ospf_rt_abr1(struct proto_ospf *po)
|
||||||
if (oa_is_nssa(oa) && oa->ac->default_nssa)
|
if (oa_is_nssa(oa) && oa->ac->default_nssa)
|
||||||
originate_ext_lsa(oa, &default_nf->fn, 0, oa->ac->default_cost, IPA_NONE, 0, 0);
|
originate_ext_lsa(oa, &default_nf->fn, 0, oa->ac->default_cost, IPA_NONE, 0, 0);
|
||||||
else
|
else
|
||||||
flush_ext_lsa(oa, &default_nf->fn);
|
flush_ext_lsa(oa, &default_nf->fn, 1);
|
||||||
|
|
||||||
|
|
||||||
/* RFC 2328 16.4. (3) - precompute preferred ASBR entries */
|
/* RFC 2328 16.4. (3) - precompute preferred ASBR entries */
|
||||||
|
|
|
@ -862,6 +862,9 @@ flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type)
|
||||||
|
|
||||||
if ((en = ospf_hash_find_header(po->gr, oa->areaid, &lsa)) != NULL)
|
if ((en = ospf_hash_find_header(po->gr, oa->areaid, &lsa)) != NULL)
|
||||||
{
|
{
|
||||||
|
OSPF_TRACE(D_EVENTS, "Flushing summary-LSA (id=%R, type=%d)",
|
||||||
|
en->lsa.id, en->lsa.type);
|
||||||
|
|
||||||
if ((type == ORT_NET) && check_sum_net_lsaid_collision(fn, en))
|
if ((type == ORT_NET) && check_sum_net_lsaid_collision(fn, en))
|
||||||
{
|
{
|
||||||
log(L_ERR "%s: LSAID collision for %I/%d",
|
log(L_ERR "%s: LSAID collision for %I/%d",
|
||||||
|
@ -873,9 +876,6 @@ flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type)
|
||||||
en->lsa.age = LSA_MAXAGE;
|
en->lsa.age = LSA_MAXAGE;
|
||||||
en->lsa.sn = LSA_MAXSEQNO;
|
en->lsa.sn = LSA_MAXSEQNO;
|
||||||
lsasum_calculate(&en->lsa, sum);
|
lsasum_calculate(&en->lsa, sum);
|
||||||
|
|
||||||
OSPF_TRACE(D_EVENTS, "Flushing summary-LSA (id=%R, type=%d)",
|
|
||||||
en->lsa.id, en->lsa.type);
|
|
||||||
ospf_lsupd_flood(po, NULL, NULL, &en->lsa, oa->areaid, 1);
|
ospf_lsupd_flood(po, NULL, NULL, &en->lsa, oa->areaid, 1);
|
||||||
if (can_flush_lsa(po)) flush_lsa(en, po);
|
if (can_flush_lsa(po)) flush_lsa(en, po);
|
||||||
}
|
}
|
||||||
|
@ -1131,15 +1131,11 @@ originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int src,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn)
|
flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int nssa)
|
||||||
{
|
{
|
||||||
struct proto_ospf *po = oa->po;
|
struct proto_ospf *po = oa->po;
|
||||||
struct proto *p = &po->proto;
|
struct proto *p = &po->proto;
|
||||||
struct top_hash_entry *en;
|
struct top_hash_entry *en;
|
||||||
int nssa = oa_is_nssa(oa);
|
|
||||||
|
|
||||||
OSPF_TRACE(D_EVENTS, "Flushing %s-LSA for %I/%d",
|
|
||||||
nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen);
|
|
||||||
|
|
||||||
u32 dom = nssa ? oa->areaid : 0;
|
u32 dom = nssa ? oa->areaid : 0;
|
||||||
u32 type = nssa ? LSA_T_NSSA : LSA_T_EXT;
|
u32 type = nssa ? LSA_T_NSSA : LSA_T_EXT;
|
||||||
|
@ -1147,6 +1143,9 @@ flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn)
|
||||||
|
|
||||||
if (en = ospf_hash_find(po->gr, dom, lsaid, po->router_id, type))
|
if (en = ospf_hash_find(po->gr, dom, lsaid, po->router_id, type))
|
||||||
{
|
{
|
||||||
|
OSPF_TRACE(D_EVENTS, "Flushing %s-LSA for %I/%d",
|
||||||
|
nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen);
|
||||||
|
|
||||||
if (check_ext_lsa(en, fn, 0, IPA_NONE, 0) < 0)
|
if (check_ext_lsa(en, fn, 0, IPA_NONE, 0) < 0)
|
||||||
{
|
{
|
||||||
log(L_ERR "%s: LSAID collision for %I/%d",
|
log(L_ERR "%s: LSAID collision for %I/%d",
|
||||||
|
|
|
@ -72,7 +72,7 @@ void originate_sum_net_lsa(struct ospf_area *oa, struct fib_node *fn, int metric
|
||||||
void originate_sum_rt_lsa(struct ospf_area *oa, struct fib_node *fn, int metric, u32 options UNUSED);
|
void originate_sum_rt_lsa(struct ospf_area *oa, struct fib_node *fn, int metric, u32 options UNUSED);
|
||||||
void flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type);
|
void flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type);
|
||||||
void originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int src, u32 metric, ip_addr fwaddr, u32 tag, int pbit);
|
void originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int src, u32 metric, ip_addr fwaddr, u32 tag, int pbit);
|
||||||
void flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn);
|
void flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int nssa);
|
||||||
|
|
||||||
|
|
||||||
#ifdef OSPFv2
|
#ifdef OSPFv2
|
||||||
|
|
|
@ -14,32 +14,45 @@ CF_DEFINES
|
||||||
#define RADV_CFG ((struct radv_config *) this_proto)
|
#define RADV_CFG ((struct radv_config *) this_proto)
|
||||||
#define RADV_IFACE ((struct radv_iface_config *) this_ipatt)
|
#define RADV_IFACE ((struct radv_iface_config *) this_ipatt)
|
||||||
#define RADV_PREFIX this_radv_prefix
|
#define RADV_PREFIX this_radv_prefix
|
||||||
|
#define RADV_RDNSS (&this_radv_rdnss)
|
||||||
|
#define RADV_DNSSL (&this_radv_dnssl)
|
||||||
|
|
||||||
static struct radv_prefix_config *this_radv_prefix;
|
static struct radv_prefix_config *this_radv_prefix;
|
||||||
|
static struct radv_rdnss_config this_radv_rdnss;
|
||||||
|
static struct radv_dnssl_config this_radv_dnssl;
|
||||||
|
static list radv_dns_list; /* Used by radv_rdnss and radv_dnssl */
|
||||||
|
static u8 radv_mult_val; /* Used by radv_mult for second return value */
|
||||||
|
|
||||||
|
|
||||||
CF_DECLS
|
CF_DECLS
|
||||||
|
|
||||||
CF_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL,
|
CF_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL,
|
||||||
MANAGED, OTHER, CONFIG, LINK, MTU, REACHABLE, TIME, RETRANS,
|
MANAGED, OTHER, CONFIG, LINK, MTU, REACHABLE, TIME, RETRANS,
|
||||||
TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED,
|
TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT,
|
||||||
LIFETIME, SKIP, ONLINK, AUTONOMOUS)
|
LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN,
|
||||||
|
LOCAL)
|
||||||
|
|
||||||
|
%type<i> radv_mult
|
||||||
|
|
||||||
CF_GRAMMAR
|
CF_GRAMMAR
|
||||||
|
|
||||||
CF_ADDTO(proto, radv_proto '}')
|
CF_ADDTO(proto, radv_proto)
|
||||||
|
|
||||||
radv_proto_start: proto_start RADV
|
radv_proto_start: proto_start RADV
|
||||||
{
|
{
|
||||||
this_proto = proto_config_new(&proto_radv, sizeof(struct radv_config), $1);
|
this_proto = proto_config_new(&proto_radv, sizeof(struct radv_config), $1);
|
||||||
init_list(&RADV_CFG->patt_list);
|
init_list(&RADV_CFG->patt_list);
|
||||||
init_list(&RADV_CFG->pref_list);
|
init_list(&RADV_CFG->pref_list);
|
||||||
|
init_list(&RADV_CFG->rdnss_list);
|
||||||
|
init_list(&RADV_CFG->dnssl_list);
|
||||||
};
|
};
|
||||||
|
|
||||||
radv_proto_item:
|
radv_proto_item:
|
||||||
proto_item
|
proto_item
|
||||||
| PREFIX radv_prefix { add_tail(&RADV_CFG->pref_list, NODE this_radv_prefix); }
|
|
||||||
| INTERFACE radv_iface
|
| INTERFACE radv_iface
|
||||||
|
| PREFIX radv_prefix { add_tail(&RADV_CFG->pref_list, NODE this_radv_prefix); }
|
||||||
|
| RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_CFG->rdnss_list, &radv_dns_list); }
|
||||||
|
| DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_CFG->dnssl_list, &radv_dns_list); }
|
||||||
;
|
;
|
||||||
|
|
||||||
radv_proto_opts:
|
radv_proto_opts:
|
||||||
|
@ -48,7 +61,7 @@ radv_proto_opts:
|
||||||
;
|
;
|
||||||
|
|
||||||
radv_proto:
|
radv_proto:
|
||||||
radv_proto_start proto_name '{' radv_proto_opts;
|
radv_proto_start proto_name '{' radv_proto_opts '}';
|
||||||
|
|
||||||
|
|
||||||
radv_iface_start:
|
radv_iface_start:
|
||||||
|
@ -57,6 +70,8 @@ radv_iface_start:
|
||||||
add_tail(&RADV_CFG->patt_list, NODE this_ipatt);
|
add_tail(&RADV_CFG->patt_list, NODE this_ipatt);
|
||||||
init_list(&this_ipatt->ipn_list);
|
init_list(&this_ipatt->ipn_list);
|
||||||
init_list(&RADV_IFACE->pref_list);
|
init_list(&RADV_IFACE->pref_list);
|
||||||
|
init_list(&RADV_IFACE->rdnss_list);
|
||||||
|
init_list(&RADV_IFACE->dnssl_list);
|
||||||
|
|
||||||
RADV_IFACE->min_ra_int = -1; /* undefined */
|
RADV_IFACE->min_ra_int = -1; /* undefined */
|
||||||
RADV_IFACE->max_ra_int = DEFAULT_MAX_RA_INT;
|
RADV_IFACE->max_ra_int = DEFAULT_MAX_RA_INT;
|
||||||
|
@ -77,6 +92,10 @@ radv_iface_item:
|
||||||
| CURRENT HOP LIMIT expr { RADV_IFACE->current_hop_limit = $4; if (($4 < 0) || ($4 > 255)) cf_error("Current hop limit must be in range 0-255"); }
|
| CURRENT HOP LIMIT expr { RADV_IFACE->current_hop_limit = $4; if (($4 < 0) || ($4 > 255)) cf_error("Current hop limit must be in range 0-255"); }
|
||||||
| DEFAULT LIFETIME expr { RADV_IFACE->default_lifetime = $3; if (($3 < 0) || ($3 > 9000)) cf_error("Default lifetime must be in range 0-9000"); }
|
| DEFAULT LIFETIME expr { RADV_IFACE->default_lifetime = $3; if (($3 < 0) || ($3 > 9000)) cf_error("Default lifetime must be in range 0-9000"); }
|
||||||
| PREFIX radv_prefix { add_tail(&RADV_IFACE->pref_list, NODE this_radv_prefix); }
|
| PREFIX radv_prefix { add_tail(&RADV_IFACE->pref_list, NODE this_radv_prefix); }
|
||||||
|
| RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_IFACE->rdnss_list, &radv_dns_list); }
|
||||||
|
| DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_IFACE->dnssl_list, &radv_dns_list); }
|
||||||
|
| RDNSS LOCAL bool { RADV_IFACE->rdnss_local = $3; }
|
||||||
|
| DNSSL LOCAL bool { RADV_IFACE->dnssl_local = $3; }
|
||||||
;
|
;
|
||||||
|
|
||||||
radv_iface_finish:
|
radv_iface_finish:
|
||||||
|
@ -152,6 +171,103 @@ radv_prefix:
|
||||||
radv_prefix_start radv_prefix_opt_list radv_prefix_finish;
|
radv_prefix_start radv_prefix_opt_list radv_prefix_finish;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
radv_rdnss_node: ipa
|
||||||
|
{
|
||||||
|
struct radv_rdnss_config *cf = cfg_allocz(sizeof(struct radv_rdnss_config));
|
||||||
|
add_tail(&radv_dns_list, NODE cf);
|
||||||
|
|
||||||
|
cf->server = $1;
|
||||||
|
cf->lifetime_mult = DEFAULT_DNS_LIFETIME_MULT;
|
||||||
|
};
|
||||||
|
|
||||||
|
radv_rdnss_start:
|
||||||
|
{
|
||||||
|
RADV_RDNSS->lifetime = 0;
|
||||||
|
RADV_RDNSS->lifetime_mult = DEFAULT_DNS_LIFETIME_MULT;
|
||||||
|
};
|
||||||
|
|
||||||
|
radv_rdnss_item:
|
||||||
|
| NS radv_rdnss_node
|
||||||
|
| LIFETIME radv_mult { RADV_RDNSS->lifetime = $2; RADV_RDNSS->lifetime_mult = radv_mult_val; }
|
||||||
|
;
|
||||||
|
|
||||||
|
radv_rdnss_finish:
|
||||||
|
{
|
||||||
|
if (EMPTY_LIST(radv_dns_list))
|
||||||
|
cf_error("No nameserver in RDNSS section");
|
||||||
|
|
||||||
|
struct radv_rdnss_config *cf;
|
||||||
|
WALK_LIST(cf, radv_dns_list)
|
||||||
|
{
|
||||||
|
cf->lifetime = RADV_RDNSS->lifetime;
|
||||||
|
cf->lifetime_mult = RADV_RDNSS->lifetime_mult;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
radv_rdnss_opts:
|
||||||
|
/* empty */
|
||||||
|
| radv_rdnss_opts radv_rdnss_item ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
radv_rdnss:
|
||||||
|
radv_rdnss_node
|
||||||
|
| '{' radv_rdnss_start radv_rdnss_opts '}' radv_rdnss_finish
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
radv_dnssl_node: TEXT
|
||||||
|
{
|
||||||
|
struct radv_dnssl_config *cf = cfg_allocz(sizeof(struct radv_dnssl_config));
|
||||||
|
add_tail(&radv_dns_list, NODE cf);
|
||||||
|
|
||||||
|
cf->domain = $1;
|
||||||
|
cf->lifetime_mult = DEFAULT_DNS_LIFETIME_MULT;
|
||||||
|
|
||||||
|
if (radv_process_domain(cf) < 0)
|
||||||
|
cf_error("Invalid domain dame");
|
||||||
|
};
|
||||||
|
|
||||||
|
radv_dnssl_start:
|
||||||
|
{
|
||||||
|
RADV_DNSSL->lifetime = 0;
|
||||||
|
RADV_DNSSL->lifetime_mult = DEFAULT_DNS_LIFETIME_MULT;
|
||||||
|
};
|
||||||
|
|
||||||
|
radv_dnssl_item:
|
||||||
|
| DOMAIN radv_dnssl_node
|
||||||
|
| LIFETIME radv_mult { RADV_DNSSL->lifetime = $2; RADV_DNSSL->lifetime_mult = radv_mult_val; }
|
||||||
|
;
|
||||||
|
|
||||||
|
radv_dnssl_finish:
|
||||||
|
{
|
||||||
|
if (EMPTY_LIST(radv_dns_list))
|
||||||
|
cf_error("No domain in DNSSL section");
|
||||||
|
|
||||||
|
struct radv_dnssl_config *cf;
|
||||||
|
WALK_LIST(cf, radv_dns_list)
|
||||||
|
{
|
||||||
|
cf->lifetime = RADV_DNSSL->lifetime;
|
||||||
|
cf->lifetime_mult = RADV_DNSSL->lifetime_mult;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
radv_dnssl_opts:
|
||||||
|
/* empty */
|
||||||
|
| radv_dnssl_opts radv_dnssl_item ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
radv_dnssl:
|
||||||
|
radv_dnssl_node
|
||||||
|
| '{' radv_dnssl_start radv_dnssl_opts '}' radv_dnssl_finish
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
radv_mult:
|
||||||
|
expr { $$ = $1; radv_mult_val = 0; }
|
||||||
|
| MULT expr { $$ = 0; radv_mult_val = $2; if (($2 < 1) || ($2 > 254)) cf_error("Multiplier must be in range 1-254"); }
|
||||||
|
;
|
||||||
|
|
||||||
CF_CODE
|
CF_CODE
|
||||||
|
|
||||||
CF_END
|
CF_END
|
||||||
|
|
|
@ -24,8 +24,10 @@ struct radv_ra_packet
|
||||||
#define OPT_RA_MANAGED 0x80
|
#define OPT_RA_MANAGED 0x80
|
||||||
#define OPT_RA_OTHER_CFG 0x40
|
#define OPT_RA_OTHER_CFG 0x40
|
||||||
|
|
||||||
#define OPT_PREFIX 3
|
#define OPT_PREFIX 3
|
||||||
#define OPT_MTU 5
|
#define OPT_MTU 5
|
||||||
|
#define OPT_RDNSS 25
|
||||||
|
#define OPT_DNSSL 31
|
||||||
|
|
||||||
struct radv_opt_prefix
|
struct radv_opt_prefix
|
||||||
{
|
{
|
||||||
|
@ -50,6 +52,25 @@ struct radv_opt_mtu
|
||||||
u32 mtu;
|
u32 mtu;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct radv_opt_rdnss
|
||||||
|
{
|
||||||
|
u8 type;
|
||||||
|
u8 length;
|
||||||
|
u16 reserved;
|
||||||
|
u32 lifetime;
|
||||||
|
ip_addr servers[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct radv_opt_dnssl
|
||||||
|
{
|
||||||
|
u8 type;
|
||||||
|
u8 length;
|
||||||
|
u16 reserved;
|
||||||
|
u32 lifetime;
|
||||||
|
char domain[];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static struct radv_prefix_config default_prefix = {
|
static struct radv_prefix_config default_prefix = {
|
||||||
.onlink = 1,
|
.onlink = 1,
|
||||||
.autonomous = 1,
|
.autonomous = 1,
|
||||||
|
@ -57,6 +78,7 @@ static struct radv_prefix_config default_prefix = {
|
||||||
.preferred_lifetime = DEFAULT_PREFERRED_LIFETIME
|
.preferred_lifetime = DEFAULT_PREFERRED_LIFETIME
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static struct radv_prefix_config *
|
static struct radv_prefix_config *
|
||||||
radv_prefix_match(struct radv_iface *ifa, struct ifa *a)
|
radv_prefix_match(struct radv_iface *ifa, struct ifa *a)
|
||||||
{
|
{
|
||||||
|
@ -78,10 +100,146 @@ radv_prefix_match(struct radv_iface *ifa, struct ifa *a)
|
||||||
return &default_prefix;
|
return &default_prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *bufend)
|
||||||
|
{
|
||||||
|
struct radv_rdnss_config *rcf = HEAD(*rdnss_list);
|
||||||
|
|
||||||
|
while(NODE_VALID(rcf))
|
||||||
|
{
|
||||||
|
struct radv_rdnss_config *rcf_base = rcf;
|
||||||
|
struct radv_opt_rdnss *op = (void *) *buf;
|
||||||
|
int max_i = (bufend - *buf - sizeof(struct radv_opt_rdnss)) / sizeof(ip_addr);
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (max_i < 1)
|
||||||
|
goto too_much;
|
||||||
|
|
||||||
|
op->type = OPT_RDNSS;
|
||||||
|
op->reserved = 0;
|
||||||
|
|
||||||
|
if (rcf->lifetime_mult)
|
||||||
|
op->lifetime = htonl(rcf->lifetime_mult * ifa->cf->max_ra_int);
|
||||||
|
else
|
||||||
|
op->lifetime = htonl(rcf->lifetime);
|
||||||
|
|
||||||
|
while(NODE_VALID(rcf) &&
|
||||||
|
(rcf->lifetime == rcf_base->lifetime) &&
|
||||||
|
(rcf->lifetime_mult == rcf_base->lifetime_mult))
|
||||||
|
{
|
||||||
|
if (i >= max_i)
|
||||||
|
goto too_much;
|
||||||
|
|
||||||
|
op->servers[i] = rcf->server;
|
||||||
|
ipa_hton(op->servers[i]);
|
||||||
|
i++;
|
||||||
|
|
||||||
|
rcf = NODE_NEXT(rcf);
|
||||||
|
}
|
||||||
|
|
||||||
|
op->length = 1+2*i;
|
||||||
|
*buf += 8 * op->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
too_much:
|
||||||
|
log(L_WARN "%s: Too many RA options on interface %s",
|
||||||
|
ifa->ra->p.name, ifa->iface->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
radv_process_domain(struct radv_dnssl_config *cf)
|
||||||
|
{
|
||||||
|
/* Format of domain in search list is <size> <label> <size> <label> ... 0 */
|
||||||
|
|
||||||
|
char *dom = cf->domain;
|
||||||
|
char *dom_end = dom; /* Just to */
|
||||||
|
u8 *dlen_save = &cf->dlen_first;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
while (dom_end)
|
||||||
|
{
|
||||||
|
dom_end = strchr(dom, '.');
|
||||||
|
len = dom_end ? (dom_end - dom) : strlen(dom);
|
||||||
|
|
||||||
|
if (len < 1 || len > 63)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*dlen_save = len;
|
||||||
|
dlen_save = (u8 *) dom_end;
|
||||||
|
|
||||||
|
dom += len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = dom - cf->domain;
|
||||||
|
if (len > 254)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
cf->dlen_all = len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
radv_prepare_dnssl(struct radv_iface *ifa, list *dnssl_list, char **buf, char *bufend)
|
||||||
|
{
|
||||||
|
struct radv_dnssl_config *dcf = HEAD(*dnssl_list);
|
||||||
|
|
||||||
|
while(NODE_VALID(dcf))
|
||||||
|
{
|
||||||
|
struct radv_dnssl_config *dcf_base = dcf;
|
||||||
|
struct radv_opt_dnssl *op = (void *) *buf;
|
||||||
|
int bsize = bufend - *buf - sizeof(struct radv_opt_dnssl);
|
||||||
|
int bpos = 0;
|
||||||
|
|
||||||
|
if (bsize < 0)
|
||||||
|
goto too_much;
|
||||||
|
|
||||||
|
bsize = bsize & ~7; /* Round down to multiples of 8 */
|
||||||
|
|
||||||
|
op->type = OPT_DNSSL;
|
||||||
|
op->reserved = 0;
|
||||||
|
|
||||||
|
if (dcf->lifetime_mult)
|
||||||
|
op->lifetime = htonl(dcf->lifetime_mult * ifa->cf->max_ra_int);
|
||||||
|
else
|
||||||
|
op->lifetime = htonl(dcf->lifetime);
|
||||||
|
|
||||||
|
while(NODE_VALID(dcf) &&
|
||||||
|
(dcf->lifetime == dcf_base->lifetime) &&
|
||||||
|
(dcf->lifetime_mult == dcf_base->lifetime_mult))
|
||||||
|
{
|
||||||
|
if (bpos + dcf->dlen_all + 1 > bsize)
|
||||||
|
goto too_much;
|
||||||
|
|
||||||
|
op->domain[bpos++] = dcf->dlen_first;
|
||||||
|
memcpy(op->domain + bpos, dcf->domain, dcf->dlen_all);
|
||||||
|
bpos += dcf->dlen_all;
|
||||||
|
|
||||||
|
dcf = NODE_NEXT(dcf);
|
||||||
|
}
|
||||||
|
|
||||||
|
int blen = (bpos + 7) / 8;
|
||||||
|
bzero(op->domain + bpos, 8 * blen - bpos);
|
||||||
|
op->length = 1 + blen;
|
||||||
|
*buf += 8 * op->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
too_much:
|
||||||
|
log(L_WARN "%s: Too many RA options on interface %s",
|
||||||
|
ifa->ra->p.name, ifa->iface->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
radv_prepare_ra(struct radv_iface *ifa)
|
radv_prepare_ra(struct radv_iface *ifa)
|
||||||
{
|
{
|
||||||
struct proto_radv *ra = ifa->ra;
|
struct proto_radv *ra = ifa->ra;
|
||||||
|
struct radv_config *cf = (struct radv_config *) (ra->p.cf);
|
||||||
|
|
||||||
char *buf = ifa->sk->tbuf;
|
char *buf = ifa->sk->tbuf;
|
||||||
char *bufstart = buf;
|
char *bufstart = buf;
|
||||||
|
@ -121,7 +279,7 @@ radv_prepare_ra(struct radv_iface *ifa)
|
||||||
if (buf + sizeof(struct radv_opt_prefix) > bufend)
|
if (buf + sizeof(struct radv_opt_prefix) > bufend)
|
||||||
{
|
{
|
||||||
log(L_WARN "%s: Too many prefixes on interface %s", ra->p.name, ifa->iface->name);
|
log(L_WARN "%s: Too many prefixes on interface %s", ra->p.name, ifa->iface->name);
|
||||||
break;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct radv_opt_prefix *op = (void *) buf;
|
struct radv_opt_prefix *op = (void *) buf;
|
||||||
|
@ -137,7 +295,22 @@ radv_prepare_ra(struct radv_iface *ifa)
|
||||||
ipa_hton(op->prefix);
|
ipa_hton(op->prefix);
|
||||||
buf += sizeof(*op);
|
buf += sizeof(*op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! ifa->cf->rdnss_local)
|
||||||
|
if (radv_prepare_rdnss(ifa, &cf->rdnss_list, &buf, bufend) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (radv_prepare_rdnss(ifa, &ifa->cf->rdnss_list, &buf, bufend) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (! ifa->cf->dnssl_local)
|
||||||
|
if (radv_prepare_dnssl(ifa, &cf->dnssl_list, &buf, bufend) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (radv_prepare_dnssl(ifa, &ifa->cf->dnssl_list, &buf, bufend) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
done:
|
||||||
ifa->plen = buf - bufstart;
|
ifa->plen = buf - bufstart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,10 @@
|
||||||
* radv_iface_notify(), which processes asynchronous events (specified
|
* radv_iface_notify(), which processes asynchronous events (specified
|
||||||
* by RA_EV_* codes), and radv_timer(), which triggers sending RAs and
|
* by RA_EV_* codes), and radv_timer(), which triggers sending RAs and
|
||||||
* computes the next timeout.
|
* computes the next timeout.
|
||||||
|
*
|
||||||
|
* Supported standards:
|
||||||
|
* - RFC 4861 - main RA standard
|
||||||
|
* - RFC 6106 - DNS extensions (RDDNS, DNSSL)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -42,24 +42,33 @@
|
||||||
#define DEFAULT_VALID_LIFETIME 86400
|
#define DEFAULT_VALID_LIFETIME 86400
|
||||||
#define DEFAULT_PREFERRED_LIFETIME 14400
|
#define DEFAULT_PREFERRED_LIFETIME 14400
|
||||||
|
|
||||||
|
#define DEFAULT_DNS_LIFETIME_MULT 3
|
||||||
|
|
||||||
|
|
||||||
struct radv_config
|
struct radv_config
|
||||||
{
|
{
|
||||||
struct proto_config c;
|
struct proto_config c;
|
||||||
list patt_list; /* List of iface configs (struct radv_iface_config) */
|
list patt_list; /* List of iface configs (struct radv_iface_config) */
|
||||||
list pref_list; /* Global list of prefix configs (struct radv_prefix_config) */
|
list pref_list; /* Global list of prefix configs (struct radv_prefix_config) */
|
||||||
|
list rdnss_list; /* Global list of RDNSS configs (struct radv_rdnss_config) */
|
||||||
|
list dnssl_list; /* Global list of DNSSL configs (struct radv_dnssl_config) */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct radv_iface_config
|
struct radv_iface_config
|
||||||
{
|
{
|
||||||
struct iface_patt i;
|
struct iface_patt i;
|
||||||
list pref_list; /* Local list of prefix configs (struct radv_prefix_config) */
|
list pref_list; /* Local list of prefix configs (struct radv_prefix_config) */
|
||||||
|
list rdnss_list; /* Local list of RDNSS configs (struct radv_rdnss_config) */
|
||||||
|
list dnssl_list; /* Local list of DNSSL configs (struct radv_dnssl_config) */
|
||||||
|
|
||||||
u32 min_ra_int; /* Standard options from RFC 4261 */
|
u32 min_ra_int; /* Standard options from RFC 4261 */
|
||||||
u32 max_ra_int;
|
u32 max_ra_int;
|
||||||
u32 min_delay;
|
u32 min_delay;
|
||||||
|
|
||||||
u8 managed;
|
u8 rdnss_local; /* Global list is not used for RDNSS */
|
||||||
|
u8 dnssl_local; /* Global list is not used for DNSSL */
|
||||||
|
|
||||||
|
u8 managed; /* Standard options from RFC 4261 */
|
||||||
u8 other_config;
|
u8 other_config;
|
||||||
u32 link_mtu;
|
u32 link_mtu;
|
||||||
u32 reachable_time;
|
u32 reachable_time;
|
||||||
|
@ -81,6 +90,25 @@ struct radv_prefix_config
|
||||||
u32 preferred_lifetime;
|
u32 preferred_lifetime;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct radv_rdnss_config
|
||||||
|
{
|
||||||
|
node n;
|
||||||
|
u32 lifetime; /* Valid if lifetime_mult is 0 */
|
||||||
|
u16 lifetime_mult; /* Lifetime specified as multiple of max_ra_int */
|
||||||
|
ip_addr server; /* IP address of recursive DNS server */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct radv_dnssl_config
|
||||||
|
{
|
||||||
|
node n;
|
||||||
|
u32 lifetime; /* Valid if lifetime_mult is 0 */
|
||||||
|
u16 lifetime_mult; /* Lifetime specified as multiple of max_ra_int */
|
||||||
|
u8 dlen_first; /* Length of first label in domain */
|
||||||
|
u8 dlen_all; /* Both dlen_ filled in radv_process_domain() */
|
||||||
|
char *domain; /* Domain for DNS search list, in processed form */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct proto_radv
|
struct proto_radv
|
||||||
{
|
{
|
||||||
struct proto p;
|
struct proto p;
|
||||||
|
@ -123,6 +151,7 @@ struct radv_iface
|
||||||
void radv_iface_notify(struct radv_iface *ifa, int event);
|
void radv_iface_notify(struct radv_iface *ifa, int event);
|
||||||
|
|
||||||
/* packets.c */
|
/* packets.c */
|
||||||
|
int radv_process_domain(struct radv_dnssl_config *cf);
|
||||||
void radv_send_ra(struct radv_iface *ifa, int shutdown);
|
void radv_send_ra(struct radv_iface *ifa, int shutdown);
|
||||||
int radv_sk_open(struct radv_iface *ifa);
|
int radv_sk_open(struct radv_iface *ifa);
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#define _BIRD_CONFIG_H_
|
#define _BIRD_CONFIG_H_
|
||||||
|
|
||||||
/* BIRD version */
|
/* BIRD version */
|
||||||
#define BIRD_VERSION "1.3.7"
|
#define BIRD_VERSION "1.3.8"
|
||||||
|
|
||||||
/* Include parameters determined by configure script */
|
/* Include parameters determined by configure script */
|
||||||
#include "sysdep/autoconf.h"
|
#include "sysdep/autoconf.h"
|
||||||
|
|
|
@ -737,7 +737,7 @@ nl_parse_route(struct nlmsghdr *h, int scan)
|
||||||
(a[RTA_GATEWAY] && RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr)) ||
|
(a[RTA_GATEWAY] && RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr)) ||
|
||||||
(a[RTA_PRIORITY] && RTA_PAYLOAD(a[RTA_PRIORITY]) != 4) ||
|
(a[RTA_PRIORITY] && RTA_PAYLOAD(a[RTA_PRIORITY]) != 4) ||
|
||||||
(a[RTA_PREFSRC] && RTA_PAYLOAD(a[RTA_PREFSRC]) != sizeof(ip_addr)) ||
|
(a[RTA_PREFSRC] && RTA_PAYLOAD(a[RTA_PREFSRC]) != sizeof(ip_addr)) ||
|
||||||
(a[RTA_FLOW] && RTA_PAYLOAD(a[RTA_OIF]) != 4))
|
(a[RTA_FLOW] && RTA_PAYLOAD(a[RTA_FLOW]) != 4))
|
||||||
{
|
{
|
||||||
log(L_ERR "KRT: Malformed message received");
|
log(L_ERR "KRT: Malformed message received");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -588,9 +588,12 @@ static struct resclass sk_class = {
|
||||||
* This function creates a new socket resource. If you want to use it,
|
* This function creates a new socket resource. If you want to use it,
|
||||||
* you need to fill in all the required fields of the structure and
|
* you need to fill in all the required fields of the structure and
|
||||||
* call sk_open() to do the actual opening of the socket.
|
* call sk_open() to do the actual opening of the socket.
|
||||||
|
*
|
||||||
|
* The real function name is sock_new(), sk_new() is a macro wrapper
|
||||||
|
* to avoid collision with OpenSSL.
|
||||||
*/
|
*/
|
||||||
sock *
|
sock *
|
||||||
sk_new(pool *p)
|
sock_new(pool *p)
|
||||||
{
|
{
|
||||||
sock *s = ralloc(p, &sk_class);
|
sock *s = ralloc(p, &sk_class);
|
||||||
s->pool = p;
|
s->pool = p;
|
||||||
|
|
|
@ -162,29 +162,6 @@ cf_read(byte *dest, unsigned int len, int fd)
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
cf_open(char *filename)
|
|
||||||
{
|
|
||||||
char full_name[BIRD_FNAME_MAX];
|
|
||||||
char *cur = filename;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (*filename != '/') {
|
|
||||||
char dir[BIRD_FNAME_MAX];
|
|
||||||
strncpy(dir, config_name, sizeof(dir));
|
|
||||||
dir[sizeof(dir)-1] = 0;
|
|
||||||
snprintf(full_name, sizeof(full_name), "%s/%s", dirname(dir), filename);
|
|
||||||
full_name[sizeof(full_name)-1] = 0;
|
|
||||||
cur = full_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ret = open(cur, O_RDONLY)) == -1)
|
|
||||||
cf_error("Unable to open included configuration file: %s", cur);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
sysdep_preconfig(struct config *c)
|
sysdep_preconfig(struct config *c)
|
||||||
{
|
{
|
||||||
|
@ -216,7 +193,6 @@ unix_read_config(struct config **cp, char *name)
|
||||||
if (conf->file_fd < 0)
|
if (conf->file_fd < 0)
|
||||||
return 0;
|
return 0;
|
||||||
cf_read_hook = cf_read;
|
cf_read_hook = cf_read;
|
||||||
cf_open_hook = cf_open;
|
|
||||||
ret = config_parse(conf);
|
ret = config_parse(conf);
|
||||||
close(conf->file_fd);
|
close(conf->file_fd);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Reference in a new issue