Merge branch 'master' of ssh://git.nic.cz/bird

This commit is contained in:
Ondrej Filip 2012-08-07 11:15:23 +02:00
commit 60c412b936
33 changed files with 1073 additions and 327 deletions

9
NEWS
View file

@ -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)
o Route Origin Authorization basics.

View file

@ -31,6 +31,12 @@
#include <stdlib.h>
#include <stdarg.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
@ -64,27 +70,23 @@ struct sym_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 struct symbol *cf_find_sym(byte *c, unsigned int h0);
linpool *cfg_mem;
int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
int (*cf_open_hook)(char *filename);
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_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 struct include_file_stack *new_stack(struct include_file_stack *old);
%}
@ -103,7 +105,23 @@ WHITE [ \t]
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}+ {
#ifdef IPV6
@ -200,11 +218,11 @@ else: {
["][^"\n]*\n cf_error("Unterminated string");
<INITIAL,COMMENT><<EOF>> { if(check_eof()) return END; }
<INITIAL,COMMENT><<EOF>> { if (check_eof()) return END; }
{WHITE}+
\n ifs->conf_lino++;
\n ifs->lino++;
# BEGIN(COMMENT);
@ -213,14 +231,14 @@ else: {
. cf_error("Unknown character");
<COMMENT>\n {
ifs->conf_lino++;
ifs->lino++;
BEGIN(INITIAL);
}
<COMMENT>.
<CCOMM>\*\/ BEGIN(INITIAL);
<CCOMM>\n ifs->conf_lino++;
<CCOMM>\n ifs->lino++;
<CCOMM>\/\* cf_error("Comment nesting not supported");
<CCOMM><<EOF>> cf_error("Unterminated comment");
<CCOMM>.
@ -246,47 +264,140 @@ cf_hash(byte *c)
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';
if (ifs_depth >= MAX_INCLUDE_DEPTH)
cf_error("Max include depth reached.");
/* Save current stack */
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
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);
}
new->buffer = yy_create_buffer(NULL, YY_BUF_SIZE);
}
yy_switch_to_buffer(new->buffer);
}
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
check_eof(void)
{
if (ifs == ifs_head) {
if (ifs == ifs_head)
{
/* EOF in main config file */
ifs->conf_lino = 1;
ifs->lino = 1; /* Why this? */
return 1;
}
ifs_depth--;
close(ifs->conf_fd);
ifs = ifs->prev;
ifs->next = NULL;
yy_delete_buffer(YY_CURRENT_BUFFER);
yy_switch_to_buffer(ifs->stack);
ifs = pop_ifs(ifs);
enter_ifs(ifs);
return 0;
}
@ -415,16 +526,6 @@ cf_lex_init_kh(void)
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
* @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)
cf_lex_init_kh();
ifs_head = new_stack(NULL);
ifs = ifs_head;
ifs_depth = 0;
if (!is_cli) {
ifs->conf_fd = c->file_fd;
ifs_depth = 1;
strcpy(ifs->conf_fname, c->file_name);
ifs_head = ifs = push_ifs(NULL);
if (!is_cli)
{
ifs->file_name = c->file_name;
ifs->fd = c->file_fd;
ifs->depth = 1;
}
yyrestart(NULL);
ifs->buffer = YY_CURRENT_BUFFER;
if (is_cli)
BEGIN(CLI);
else
BEGIN(INITIAL);
conf_this_scope = cfg_allocz(sizeof(struct sym_scope));
conf_this_scope->active = 1;
}

View file

@ -357,8 +357,8 @@ cf_error(char *msg, ...)
if (bvsnprintf(buf, sizeof(buf), msg, args) < 0)
strcpy(buf, "<bug: error message too long>");
new_config->err_msg = cfg_strdup(buf);
new_config->err_lino = ifs->conf_lino;
new_config->err_file_name = ifs->conf_fname;
new_config->err_lino = ifs->lino;
new_config->err_file_name = ifs->file_name;
longjmp(conf_jmpbuf, 1);
}

View file

@ -12,7 +12,6 @@
#include "lib/resource.h"
#include "lib/timer.h"
#define BIRD_FNAME_MAX 255 /* Would be better to use some UNIX define */
/* Configuration structure */
@ -91,7 +90,6 @@ void cfg_copy_list(list *dest, list *src, unsigned node_size);
/* Lexer */
extern int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
extern int (*cf_open_hook)(char *filename);
struct symbol {
struct symbol *next;
@ -117,12 +115,14 @@ struct symbol {
#define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */
struct include_file_stack {
void *stack; /* Internal lexer state */
unsigned int conf_lino; /* Current file lineno (at include) */
char conf_fname[BIRD_FNAME_MAX]; /* Current file name */
int conf_fd; /* Current file descriptor */
struct include_file_stack *prev;
struct include_file_stack *next;
void *buffer; /* Internal lexer state */
char *file_name; /* File name */
int fd; /* File descriptor */
int lino; /* Current line num */
int depth; /* Include depth, 0 = cannot include */
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;

View file

@ -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
the selected route and broadcasts it to all protocols connected to
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
@ -354,11 +374,14 @@ protocol rip {
defaults are here for a compatibility with older versions
and might change in the future.
<tag>table <m/name/</tag> Create a new routing table. The default
routing table is created implicitly, other routing tables have
to be added by this command.
<tag>table <m/name/ [sorted]</tag>
Create a new routing table. The default routing table is
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
tables can be used to validate route origination of BGP
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
routes can temporarily exceed the limit without triggering it
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
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 simpler, does not require any routes in a routing
table, and was used in older versions of BIRD, but does not
handle well nontrivial iBGP setups and multihop. Default:
<cf/direct/ for singlehop eBGP, <cf/recursive/ otherwise.
handle well nontrivial iBGP setups and multihop. Recursive
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
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
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
changes its import filter, it has to re-examine all routes
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
implementing proper RFC 4271 route selection, which is
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
distances to boundary routers during best route selection. Default: on.
@ -2321,12 +2358,15 @@ advertisement packets to connected networks. These packets contain
basic information about a local network (e.g. a list of network
prefixes), which allows network hosts to autoconfigure network
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
<p>There are two classes of definitions in RAdv configuration --
interface definitions and prefix definitions:
<p>There are several classes of definitions in RAdv configuration --
interface definitions, prefix definitions and DNS definitions:
<descrip>
<tag>interface <m/pattern [, ...]/ { <m/options/ }</tag>
@ -2336,7 +2376,7 @@ interface definitions and prefix definitions:
detailed description.
<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
the network prefixes assigned to the interface. For each
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
the network prefix is a subnet of the prefix in prefix
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>
<p>Interface specific options:
@ -2362,8 +2420,8 @@ interface definitions and prefix definitions:
<tag>min ra interval <m/expr/</tag>
This option specifies the minimum length of that intervals, in
seconds. Must be at least 3 and at most 3/4 * max ra interval.
Default: about 1/3 * max ra interval.
seconds. Must be at least 3 and at most 3/4 * <cf/max ra interval/.
Default: about 1/3 * <cf/max ra interval/.
<tag>min delay <m/expr/</tag>
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
the receipt of RA) hosts may use the router as a default
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>
@ -2429,6 +2497,42 @@ interface definitions and prefix definitions:
14400 (4 hours)
</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
<p><code>
@ -2450,6 +2554,20 @@ protocol radv {
prefix 2001:0DB8:2000::/48 {
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>

View file

@ -19,7 +19,7 @@ f_new_inst(void)
ret = cfg_alloc(sizeof(struct f_inst));
ret->code = ret->aux = 0;
ret->arg1 = ret->arg2 = ret->next = NULL;
ret->lineno = ifs->conf_lino;
ret->lineno = ifs->lino;
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));
ret->i.code = P('R','C');
ret->i.lineno = ifs->conf_lino;
ret->i.lineno = ifs->lino;
ret->i.arg1 = prefix;
ret->i.arg2 = asn;
/* prefix == NULL <-> asn == NULL */

View file

@ -3,4 +3,3 @@ print "Entering include";
print "Should be 2: ", 1+1;
print "Leaving include";

View file

@ -34,7 +34,8 @@ typedef struct list { /* In fact two overlayed nodes */
#define HEAD(list) ((void *)((list).head))
#define TAIL(list) ((void *)((list).tail))
#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) \
for(n=HEAD(list); nxt=NODE_NEXT(n); n=(void *) nxt)
/* WALK_LIST_FIRST supposes that called code removes each processed node */

View file

@ -48,7 +48,9 @@ typedef struct birdsock {
char *password; /* Password for MD5 authentication */
} 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_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 */

View file

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

View file

@ -251,7 +251,6 @@ cli_command(struct cli *c)
bzero(&f, sizeof(f));
f.mem = c->parser_pool;
cf_read_hook = cli_cmd_read_hook;
cf_open_hook = NULL;
cli_rh_pos = c->rx_buf;
cli_rh_len = strlen(c->rx_buf);
cli_rh_trick_flag = 0;

View file

@ -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(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
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_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 <rot> roa_table_arg
%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 <g> limit_spec
@ -112,10 +112,17 @@ listen_opt:
/* Creation of routing tables */
tab_sorted:
{ $$ = 0; }
| SORTED { $$ = 1; }
;
CF_ADDTO(conf, newtab)
newtab: TABLE SYM {
rt_new_table($2);
newtab: TABLE SYM tab_sorted {
struct rtable_config *cf;
cf = rt_new_table($2);
cf->sorted = $3;
}
;

View file

@ -512,20 +512,22 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
else if (!p->disabled && nc->disabled)
log(L_INFO "Disabling protocol %s", p->name);
PD(p, "Restarting");
p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART;
p->cf_new = nc;
}
else
else if (!shutting_down)
{
if (!shutting_down)
log(L_INFO "Removing protocol %s", p->name);
PD(p, "Unconfigured");
p->down_code = PDC_CF_REMOVE;
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);
proto_rethink_goal(p);
}

View file

@ -222,8 +222,9 @@ struct proto_spec {
#define PDC_CF_RESTART 0x03 /* Restart due to reconfiguration */
#define PDC_CMD_DISABLE 0x11 /* Result of disable 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_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);

View file

@ -121,6 +121,7 @@ struct rtable_config {
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_min_time; /* Minimum time between two consecutive GC runs */
byte sorted; /* Routes of network are sorted according to rte_better() */
};
typedef struct rtable {
@ -223,7 +224,8 @@ typedef struct rte {
/* Types of route announcement, also used as flags */
#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;

View file

@ -182,97 +182,81 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg)
rte_trace(p, e, '<', msg);
}
/**
* do_rte_announce - announce new rte to protocol
* @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)
static rte *
export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa, int silent)
{
struct proto *p = ah->proto;
struct filter *filter = ah->out_filter;
struct proto_stats *stats = ah->stats;
ea_list *tmpb = NULL;
rte *rt;
int v;
rte *new0 = new;
rte *old0 = old;
int ok;
rt = rt0;
*rt_free = NULL;
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;
tmpa = &tmpb;
}
char *drop_reason = NULL;
if ((ok = p->import_control ? p->import_control(p, &new, &tmpa, rte_update_pool) : 0) < 0)
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++;
drop_reason = "rejected by protocol";
rte_trace_out(D_FILTERS, p, rt, "rejected by protocol");
goto reject;
}
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))
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++;
drop_reason = "filtered out";
rte_trace_out(D_FILTERS, p, rt, "filtered out");
goto reject;
}
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++;
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;
/*
* 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)
* First, apply export limit.
*
* 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;
}
}
}
/*
* Export route limits has several problems. Because exp_routes
* 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
* 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_updates_rejected++;
rte_trace_out(D_FILTERS, p, new, "rejected [limit]");
if (new != new0)
rte_free(new);
new = NULL;
if (!old)
return;
}
}
/* FIXME - This is broken because of incorrect 'old' value (see above) */
if (!new && !old)
return;
if (new)
stats->exp_updates_accepted++;
@ -349,11 +331,172 @@ do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, r
}
else
p->rt_notify(p, ah->table, net, new, old, new->attrs->eattrs);
}
if (new && new != new0) /* Discard temporary rte's */
rte_free(new);
if (old && old != old0)
rte_free(old);
static void
rt_notify_basic(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;
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.
*/
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;
@ -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);
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
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 rtable *table = ah->table;
struct proto_stats *stats = ah->stats;
rte *before_old = NULL;
rte *old_best = net->routes;
rte *old = NULL;
rte **k, *r, *s;
rte **k;
k = &net->routes; /* Find and remove original route from the same protocol */
while (old = *k)
@ -519,8 +665,12 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
break;
}
k = &old->next;
before_old = old;
}
if (!old)
before_old = NULL;
if (!old && !new)
{
stats->imp_withdraws_ignored++;
@ -552,18 +702,37 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
if (old)
stats->imp_routes--;
rte_announce(table, RA_ANY, net, new, old, tmpa);
if (table->config->sorted)
{
/* If routes are sorted, just insert new route to appropriate position */
if (new)
{
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 and announce it */
/* The first case - the new route is cleary optimal,
we link it at the first position */
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;
}
@ -579,39 +748,24 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
/* Add the new route to the list */
if (new)
{
rte_trace_in(D_ROUTES, p, new, "added");
new->next = net->routes;
net->routes = new;
}
/* Find new optimal route */
r = NULL;
for (s=net->routes; s; s=s->next)
if (rte_better(s, r))
r = s;
/* Find a new optimal route (if there is any) */
if (net->routes)
{
rte **bp = &net->routes;
for (k=&(*bp)->next; *k; k=&(*k)->next)
if (rte_better(*k, *bp))
bp = k;
/* 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;
while (s = *k)
{
if (s == r)
{
*k = r->next;
break;
/* And relink it */
rte *best = *bp;
*bp = best->next;
best->next = net->routes;
net->routes = best;
}
k = &s->next;
}
r->next = net->routes;
net->routes = r;
}
else if (table->gc_counter++ >= table->config->gc_max_ops &&
table->gc_time + table->config->gc_min_time <= now)
rt_schedule_gc(table);
}
else if (new)
{
@ -623,11 +777,18 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
ASSERT(net->routes != NULL);
new->next = net->routes->next;
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 && old && (p->debug & D_ROUTES))
if (new)
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)
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]");
}
/* 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 (p->rte_remove)
@ -645,7 +818,6 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
}
if (new)
{
new->lastmod = now;
if (p->rte_insert)
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();
src = new->attrs->proto;
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();
}
@ -1214,6 +1386,10 @@ rt_next_hop_update(rtable *tab)
struct rtable_config *
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));
cf_define_symbol(s, SYM_TABLE, c);
@ -1296,6 +1472,8 @@ rt_commit(struct config *new, struct config *old)
r->table = ot;
ot->name = r->name;
ot->config = r;
if (o->sorted != r->sorted)
log(L_WARN "Reconfiguration of rtable sorted flag not implemented");
}
else
{
@ -1324,12 +1502,15 @@ rt_commit(struct config *new, struct config *old)
static inline void
do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e)
{
struct proto *q = e->attrs->proto;
struct proto *src = e->attrs->proto;
ea_list *tmpa;
rte_update_lock();
tmpa = q->make_tmp_attrs ? q->make_tmp_attrs(e, rte_update_pool) : NULL;
do_rte_announce(h, type, n, e, p->refeeding ? e : NULL, tmpa, p->refeeding);
tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(e, rte_update_pool) : NULL;
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();
}
@ -1372,12 +1553,13 @@ again:
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 (p->core_state != FS_FEEDING)
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--;
}

View file

@ -1258,7 +1258,8 @@ same_group(rte *r, u32 lpref, u32 lasn)
static inline int
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

View file

@ -870,6 +870,7 @@ bgp_shutdown(struct proto *P)
break;
case PDC_CMD_DISABLE:
case PDC_CMD_SHUTDOWN:
subcode = 2; // Errcode 6, 2 - administrative shutdown
break;
@ -923,7 +924,7 @@ bgp_init(struct proto_config *C)
struct proto *P = proto_new(C, sizeof(struct bgp_proto));
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->rte_better = bgp_rte_better;
P->import_control = bgp_import_control;
@ -969,6 +970,7 @@ bgp_check_config(struct bgp_config *c)
if (internal && c->rs_client)
cf_error("Only external neighbor can be RS client");
if (c->multihop && (c->gw_mode == GW_DIRECT))
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)))
cf_error("Multihop BGP cannot be used with link-local addresses");
/* Different default based on rs_client */
if (!c->missing_lladdr)
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 */
if (c->c.in_limit && (c->c.in_limit->action == PLA_RESTART) && c->disable_after_error)
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

View file

@ -42,6 +42,7 @@ struct bgp_config {
int advertise_ipv4; /* Whether we should add IPv4 capability advertisement to OPEN message */
int passive; /* Do not initiate outgoing connection */
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 hold_time, initial_hold_time;
unsigned keepalive_time;

View file

@ -25,7 +25,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
CLUSTER, ID, AS4, ADVERTISE, IPV4, CAPABILITIES, LIMIT, PASSIVE,
PREFER, OLDER, MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH,
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
@ -105,6 +106,7 @@ bgp_proto:
}
| bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
| 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 TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; }
;

View file

@ -502,15 +502,17 @@ ospf_lsupd_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa,
continue;
}
#else /* OSPFv3 */
u16 scope = ntoht(lsa->type) & LSA_SCOPE_MASK;
/* 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);
continue;
}
/* 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);
continue;

View file

@ -575,7 +575,7 @@ ospf_rt_notify(struct proto *p, rtable *tbl UNUSED, net * n, rte * new, rte * ol
if (fn->x1 != EXT_EXPORT)
return;
flush_ext_lsa(oa, fn);
flush_ext_lsa(oa, fn, oa_is_nssa(oa));
/* Old external route might blocked some NSSA translation */
if (po->areano > 1)

View file

@ -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);
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 */
@ -1189,7 +1189,7 @@ ospf_rt_abr1(struct proto_ospf *po)
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);
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 */

View file

@ -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)
{
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))
{
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.sn = LSA_MAXSEQNO;
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);
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
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 *p = &po->proto;
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 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))
{
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)
{
log(L_ERR "%s: LSAID collision for %I/%d",

View file

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

View file

@ -14,32 +14,45 @@ CF_DEFINES
#define RADV_CFG ((struct radv_config *) this_proto)
#define RADV_IFACE ((struct radv_iface_config *) this_ipatt)
#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_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_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL,
MANAGED, OTHER, CONFIG, LINK, MTU, REACHABLE, TIME, RETRANS,
TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED,
LIFETIME, SKIP, ONLINK, AUTONOMOUS)
TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT,
LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN,
LOCAL)
%type<i> radv_mult
CF_GRAMMAR
CF_ADDTO(proto, radv_proto '}')
CF_ADDTO(proto, radv_proto)
radv_proto_start: proto_start RADV
{
this_proto = proto_config_new(&proto_radv, sizeof(struct radv_config), $1);
init_list(&RADV_CFG->patt_list);
init_list(&RADV_CFG->pref_list);
init_list(&RADV_CFG->rdnss_list);
init_list(&RADV_CFG->dnssl_list);
};
radv_proto_item:
proto_item
| PREFIX radv_prefix { add_tail(&RADV_CFG->pref_list, NODE this_radv_prefix); }
| 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:
@ -48,7 +61,7 @@ radv_proto_opts:
;
radv_proto:
radv_proto_start proto_name '{' radv_proto_opts;
radv_proto_start proto_name '{' radv_proto_opts '}';
radv_iface_start:
@ -57,6 +70,8 @@ radv_iface_start:
add_tail(&RADV_CFG->patt_list, NODE this_ipatt);
init_list(&this_ipatt->ipn_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->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"); }
| 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); }
| 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:
@ -152,6 +171,103 @@ radv_prefix:
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_END

View file

@ -26,6 +26,8 @@ struct radv_ra_packet
#define OPT_PREFIX 3
#define OPT_MTU 5
#define OPT_RDNSS 25
#define OPT_DNSSL 31
struct radv_opt_prefix
{
@ -50,6 +52,25 @@ struct radv_opt_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 = {
.onlink = 1,
.autonomous = 1,
@ -57,6 +78,7 @@ static struct radv_prefix_config default_prefix = {
.preferred_lifetime = DEFAULT_PREFERRED_LIFETIME
};
static struct radv_prefix_config *
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;
}
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
radv_prepare_ra(struct radv_iface *ifa)
{
struct proto_radv *ra = ifa->ra;
struct radv_config *cf = (struct radv_config *) (ra->p.cf);
char *buf = ifa->sk->tbuf;
char *bufstart = buf;
@ -121,7 +279,7 @@ radv_prepare_ra(struct radv_iface *ifa)
if (buf + sizeof(struct radv_opt_prefix) > bufend)
{
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;
@ -138,6 +296,21 @@ radv_prepare_ra(struct radv_iface *ifa)
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;
}

View file

@ -29,6 +29,10 @@
* radv_iface_notify(), which processes asynchronous events (specified
* by RA_EV_* codes), and radv_timer(), which triggers sending RAs and
* computes the next timeout.
*
* Supported standards:
* - RFC 4861 - main RA standard
* - RFC 6106 - DNS extensions (RDDNS, DNSSL)
*/
static void

View file

@ -42,24 +42,33 @@
#define DEFAULT_VALID_LIFETIME 86400
#define DEFAULT_PREFERRED_LIFETIME 14400
#define DEFAULT_DNS_LIFETIME_MULT 3
struct radv_config
{
struct proto_config c;
list patt_list; /* List of iface configs (struct radv_iface_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 iface_patt i;
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 max_ra_int;
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;
u32 link_mtu;
u32 reachable_time;
@ -81,6 +90,25 @@ struct radv_prefix_config
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 p;
@ -123,6 +151,7 @@ struct radv_iface
void radv_iface_notify(struct radv_iface *ifa, int event);
/* packets.c */
int radv_process_domain(struct radv_dnssl_config *cf);
void radv_send_ra(struct radv_iface *ifa, int shutdown);
int radv_sk_open(struct radv_iface *ifa);

View file

@ -7,7 +7,7 @@
#define _BIRD_CONFIG_H_
/* BIRD version */
#define BIRD_VERSION "1.3.7"
#define BIRD_VERSION "1.3.8"
/* Include parameters determined by configure script */
#include "sysdep/autoconf.h"

View file

@ -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_PRIORITY] && RTA_PAYLOAD(a[RTA_PRIORITY]) != 4) ||
(a[RTA_PREFSRC] && RTA_PAYLOAD(a[RTA_PREFSRC]) != sizeof(ip_addr)) ||
(a[RTA_FLOW] && RTA_PAYLOAD(a[RTA_OIF]) != 4))
(a[RTA_FLOW] && RTA_PAYLOAD(a[RTA_FLOW]) != 4))
{
log(L_ERR "KRT: Malformed message received");
return;

View file

@ -588,9 +588,12 @@ static struct resclass sk_class = {
* 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
* 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 *
sk_new(pool *p)
sock_new(pool *p)
{
sock *s = ralloc(p, &sk_class);
s->pool = p;

View file

@ -162,29 +162,6 @@ cf_read(byte *dest, unsigned int len, int fd)
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
sysdep_preconfig(struct config *c)
{
@ -216,7 +193,6 @@ unix_read_config(struct config **cp, char *name)
if (conf->file_fd < 0)
return 0;
cf_read_hook = cf_read;
cf_open_hook = cf_open;
ret = config_parse(conf);
close(conf->file_fd);
return ret;