Channels - explicit links between protocols and tables
The patch adds support for channels, structures connecting protocols and tables and handling most interactions between them. The documentation is missing yet.
This commit is contained in:
parent
9f5782d969
commit
f4a60a9bc4
37 changed files with 2050 additions and 1778 deletions
|
@ -136,12 +136,14 @@ config_parse(struct config *c)
|
|||
protos_preconfig(c);
|
||||
rt_preconfig(c);
|
||||
cf_parse();
|
||||
protos_postconfig(c);
|
||||
|
||||
if (EMPTY_LIST(c->protos))
|
||||
cf_error("No protocol is specified in the config file");
|
||||
/* XXXX */
|
||||
|
||||
/*
|
||||
if (!c->router_id)
|
||||
cf_error("Router ID must be configured manually");
|
||||
*/
|
||||
|
||||
done = 1;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ struct config {
|
|||
|
||||
int mrtdump_file; /* Configured MRTDump file (sysdep, fd in unix) */
|
||||
char *syslog_name; /* Name used for syslog (NULL -> no syslog) */
|
||||
struct rtable_config *master_rtc; /* Configuration of master routing table */
|
||||
struct rtable_config *def_tables[NET_MAX]; /* Default routing tables for each network */
|
||||
struct iface_patt *router_id_from; /* Configured list of router ID iface patterns */
|
||||
|
||||
u32 router_id; /* Our Router ID */
|
||||
|
|
|
@ -46,6 +46,7 @@ CF_DECLS
|
|||
struct symbol *s;
|
||||
char *t;
|
||||
struct rtable_config *r;
|
||||
struct channel_config *cc;
|
||||
struct f_inst *x;
|
||||
struct filter *f;
|
||||
struct f_tree *e;
|
||||
|
@ -61,6 +62,7 @@ CF_DECLS
|
|||
bird_clock_t time;
|
||||
struct f_prefix px;
|
||||
struct proto_spec ps;
|
||||
struct channel_limit cl;
|
||||
struct timeformat *tf;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#define ABS(a) ((a)>=0 ? (a) : -(a))
|
||||
#define DELTA(a,b) (((a)>=(b))?(a)-(b):(b)-(a))
|
||||
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
|
||||
#define CALL(fn, args...) ({ if (fn) fn(args); })
|
||||
|
||||
static inline int uint_cmp(uint i1, uint i2)
|
||||
{ return (int)(i1 > i2) - (int)(i1 < i2); }
|
||||
|
|
|
@ -39,7 +39,10 @@ typedef struct list { /* In fact two overlayed nodes */
|
|||
#define WALK_LIST2(n,nn,list,pos) \
|
||||
for(nn=(list).head; NODE_VALID(nn) && (n=SKIP_BACK(typeof(*n),pos,nn)); nn=nn->next)
|
||||
#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)
|
||||
#define WALK_LIST2_DELSAFE(n,nn,nxt,list,pos) \
|
||||
for(nn=HEAD(list); (nxt=nn->next) && (n=SKIP_BACK(typeof(*n),pos,nn)); nn=nxt)
|
||||
|
||||
/* WALK_LIST_FIRST supposes that called code removes each processed node */
|
||||
#define WALK_LIST_FIRST(n,list) \
|
||||
while(n=HEAD(list), (NODE (n))->next)
|
||||
|
|
|
@ -4,6 +4,13 @@
|
|||
#include "lib/net.h"
|
||||
|
||||
|
||||
const char * const net_label[] = {
|
||||
[NET_IP4] = "ipv4",
|
||||
[NET_IP6] = "ipv6",
|
||||
[NET_VPN4] = "vpn4",
|
||||
[NET_VPN6] = "vpn6"
|
||||
};
|
||||
|
||||
const u16 net_addr_length[] = {
|
||||
[NET_IP4] = sizeof(net_addr_ip4),
|
||||
[NET_IP6] = sizeof(net_addr_ip6),
|
||||
|
|
17
lib/net.h
17
lib/net.h
|
@ -21,6 +21,15 @@
|
|||
#define NET_ROA6 6
|
||||
#define NET_MAX 7
|
||||
|
||||
#define NB_IP4 (1 << NET_IP4)
|
||||
#define NB_IP6 (1 << NET_IP6)
|
||||
#define NB_VPN4 (1 << NET_VPN4)
|
||||
#define NB_VPN6 (1 << NET_VPN6)
|
||||
|
||||
#define NB_IP (NB_IP4 | NB_IP6)
|
||||
#define NB_ANY 0xffffffff
|
||||
|
||||
|
||||
typedef struct net_addr {
|
||||
u8 type;
|
||||
u8 pxlen;
|
||||
|
@ -88,6 +97,7 @@ typedef union net_addr_union {
|
|||
} net_addr_union;
|
||||
|
||||
|
||||
extern const char * const net_label[];
|
||||
extern const u16 net_addr_length[];
|
||||
extern const u8 net_max_prefix_length[];
|
||||
extern const u16 net_max_text_length[];
|
||||
|
@ -149,6 +159,13 @@ static inline void net_fill_ip_host(net_addr *a, ip_addr prefix)
|
|||
net_fill_ip6(a, ipa_to_ip6(prefix), IP6_MAX_PREFIX_LENGTH);
|
||||
}
|
||||
|
||||
|
||||
static inline int net_val_match(u8 type, u32 mask)
|
||||
{ return !!((1 << type) & mask); }
|
||||
|
||||
static inline int net_type_match(const net_addr *a, u32 mask)
|
||||
{ return net_val_match(a->type, mask); }
|
||||
|
||||
static inline int net_is_ip(const net_addr *a)
|
||||
{ return (a->type == NET_IP4) || (a->type == NET_IP6); }
|
||||
|
||||
|
|
113
nest/config.Y
113
nest/config.Y
|
@ -17,6 +17,7 @@ CF_HDR
|
|||
CF_DEFINES
|
||||
|
||||
static struct proto_config *this_proto;
|
||||
static struct channel_config *this_channel;
|
||||
static struct iface_patt *this_ipatt;
|
||||
static struct iface_patt_node *this_ipn;
|
||||
/* static struct roa_table_config *this_roa_table; */
|
||||
|
@ -49,6 +50,15 @@ get_passwords(void)
|
|||
return rv;
|
||||
}
|
||||
|
||||
static void
|
||||
proto_postconfig(void)
|
||||
{
|
||||
CALL(this_proto->protocol->postconfig, this_proto);
|
||||
this_channel = NULL;
|
||||
this_proto = NULL;
|
||||
}
|
||||
|
||||
|
||||
#define DIRECT_CFG ((struct rt_dev_config *) this_proto)
|
||||
|
||||
CF_DECLS
|
||||
|
@ -76,9 +86,10 @@ CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
|
|||
%type <s> optsym
|
||||
%type <ra> r_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_mode limit_action table_type table_sorted tos
|
||||
%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type table_sorted tos
|
||||
%type <ps> proto_patt proto_patt2
|
||||
%type <g> limit_spec
|
||||
%type <cc> channel_start proto_channel
|
||||
%type <cl> limit_spec
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
|
@ -115,7 +126,7 @@ listen_opts:
|
|||
| listen_opts listen_opt
|
||||
;
|
||||
|
||||
listen_opt:
|
||||
listen_opt:
|
||||
ADDRESS ipa { new_config->listen_bgp_addr = $2; }
|
||||
| PORT expr { new_config->listen_bgp_port = $2; }
|
||||
| V6ONLY { new_config->listen_bgp_flags = 0; }
|
||||
|
@ -128,13 +139,10 @@ CF_ADDTO(conf, gr_opts)
|
|||
gr_opts: GRACEFUL RESTART WAIT expr ';' { new_config->gr_wait = $4; } ;
|
||||
|
||||
|
||||
/* Creation of routing tables */
|
||||
/* Network types (for tables, channels) */
|
||||
|
||||
CF_ADDTO(conf, table)
|
||||
|
||||
table_type:
|
||||
/* empty */ { $$ = NET_IP4; }
|
||||
| IPV4 { $$ = NET_IP4; }
|
||||
net_type:
|
||||
IPV4 { $$ = NET_IP4; }
|
||||
| IPV6 { $$ = NET_IP6; }
|
||||
| VPN4 { $$ = NET_VPN4; }
|
||||
| VPN6 { $$ = NET_VPN6; }
|
||||
|
@ -142,21 +150,27 @@ table_type:
|
|||
| ROA6 { $$ = NET_ROA6; }
|
||||
;
|
||||
|
||||
|
||||
/* Creation of routing tables */
|
||||
|
||||
CF_ADDTO(conf, table)
|
||||
|
||||
table_sorted:
|
||||
{ $$ = 0; }
|
||||
| SORTED { $$ = 1; }
|
||||
;
|
||||
|
||||
table: table_type TABLE SYM table_sorted {
|
||||
table: net_type TABLE SYM table_sorted {
|
||||
struct rtable_config *cf;
|
||||
cf = rt_new_table($3, $1);
|
||||
cf->sorted = $4;
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
/* Definition of protocols */
|
||||
|
||||
CF_ADDTO(conf, proto)
|
||||
CF_ADDTO(conf, proto { proto_postconfig(); })
|
||||
|
||||
proto_start:
|
||||
PROTOCOL { $$ = SYM_PROTO; }
|
||||
|
@ -194,24 +208,62 @@ proto_name:
|
|||
|
||||
proto_item:
|
||||
/* EMPTY */
|
||||
| PREFERENCE expr {
|
||||
if ($2 < 0 || $2 > 0xFFFF) cf_error("Invalid preference");
|
||||
this_proto->preference = $2;
|
||||
}
|
||||
| DISABLED bool { this_proto->disabled = $2; }
|
||||
| DEBUG debug_mask { this_proto->debug = $2; }
|
||||
| MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; }
|
||||
| IMPORT imexport { this_proto->in_filter = $2; }
|
||||
| EXPORT imexport { this_proto->out_filter = $2; }
|
||||
| RECEIVE LIMIT limit_spec { this_proto->rx_limit = $3; }
|
||||
| IMPORT LIMIT limit_spec { this_proto->in_limit = $3; }
|
||||
| EXPORT LIMIT limit_spec { this_proto->out_limit = $3; }
|
||||
| IMPORT KEEP FILTERED bool { this_proto->in_keep_filtered = $4; }
|
||||
| TABLE rtable { this_proto->table = $2; }
|
||||
| ROUTER ID idval { this_proto->router_id = $3; }
|
||||
| DESCRIPTION text { this_proto->dsc = $2; }
|
||||
;
|
||||
|
||||
|
||||
channel_start: net_type
|
||||
{
|
||||
$$ = this_channel = channel_config_new(NULL, $1, this_proto);
|
||||
};
|
||||
|
||||
channel_item:
|
||||
TABLE rtable {
|
||||
if (this_channel->net_type && ($2->addr_type != this_channel->net_type))
|
||||
cf_error("Incompatible table type");
|
||||
this_channel->table = $2;
|
||||
}
|
||||
| IMPORT imexport { this_channel->in_filter = $2; }
|
||||
| EXPORT imexport { this_channel->out_filter = $2; }
|
||||
| RECEIVE LIMIT limit_spec { this_channel->rx_limit = $3; }
|
||||
| IMPORT LIMIT limit_spec { this_channel->in_limit = $3; }
|
||||
| EXPORT LIMIT limit_spec { this_channel->out_limit = $3; }
|
||||
| PREFERENCE expr { this_channel->preference = $2; check_u16($2); }
|
||||
| IMPORT KEEP FILTERED bool { this_channel->in_keep_filtered = $4; }
|
||||
;
|
||||
|
||||
channel_opts:
|
||||
/* empty */
|
||||
| channel_opts channel_item ';'
|
||||
;
|
||||
|
||||
channel_opt_list:
|
||||
/* empty */
|
||||
| '{' channel_opts '}'
|
||||
;
|
||||
|
||||
channel_end:
|
||||
{
|
||||
if (!this_channel->table)
|
||||
cf_error("Routing table not specified");
|
||||
|
||||
this_channel = NULL;
|
||||
};
|
||||
|
||||
proto_channel: channel_start channel_opt_list channel_end;
|
||||
|
||||
|
||||
rtable:
|
||||
SYM {
|
||||
if ($1->class != SYM_TABLE) cf_error("Table expected");
|
||||
$$ = $1->def;
|
||||
}
|
||||
;
|
||||
|
||||
imexport:
|
||||
FILTER filter { $$ = $2; }
|
||||
| where_filter
|
||||
|
@ -228,20 +280,8 @@ limit_action:
|
|||
;
|
||||
|
||||
limit_spec:
|
||||
expr limit_action {
|
||||
struct proto_limit *l = cfg_allocz(sizeof(struct proto_limit));
|
||||
l->limit = $1;
|
||||
l->action = $2;
|
||||
$$ = l;
|
||||
}
|
||||
| OFF { $$ = NULL; }
|
||||
;
|
||||
|
||||
rtable:
|
||||
SYM {
|
||||
if ($1->class != SYM_TABLE) cf_error("Table name expected");
|
||||
$$ = $1->def;
|
||||
}
|
||||
expr limit_action { $$ = (struct channel_limit){ .limit = $1, $$.action = $2 }; }
|
||||
| OFF { $$ = (struct channel_limit){}; }
|
||||
;
|
||||
|
||||
CF_ADDTO(conf, debug_default)
|
||||
|
@ -315,6 +355,7 @@ dev_proto_start: proto_start DIRECT {
|
|||
dev_proto:
|
||||
dev_proto_start proto_name '{'
|
||||
| dev_proto proto_item ';'
|
||||
| dev_proto proto_channel ';'
|
||||
| dev_proto dev_iface_patt ';'
|
||||
;
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ if_copy(struct iface *to, struct iface *from)
|
|||
static inline void
|
||||
ifa_send_notify(struct proto *p, unsigned c, struct ifa *a)
|
||||
{
|
||||
if (p->ifa_notify)
|
||||
if (p->ifa_notify && (p->proto_state != PS_DOWN))
|
||||
{
|
||||
if (p->debug & D_IFACES)
|
||||
log(L_TRACE "%s < %s address %N on interface %s %s",
|
||||
|
@ -155,7 +155,7 @@ ifa_notify_change_(unsigned c, struct ifa *a)
|
|||
|
||||
DBG("IFA change notification (%x) for %s:%I\n", c, a->iface->name, a->ip);
|
||||
|
||||
WALK_LIST(p, active_proto_list)
|
||||
WALK_LIST(p, proto_list)
|
||||
ifa_send_notify(p, c, a);
|
||||
}
|
||||
|
||||
|
@ -174,7 +174,7 @@ ifa_notify_change(unsigned c, struct ifa *a)
|
|||
static inline void
|
||||
if_send_notify(struct proto *p, unsigned c, struct iface *i)
|
||||
{
|
||||
if (p->if_notify)
|
||||
if (p->if_notify && (p->proto_state != PS_DOWN))
|
||||
{
|
||||
if (p->debug & D_IFACES)
|
||||
log(L_TRACE "%s < interface %s %s", p->name, i->name,
|
||||
|
@ -215,7 +215,7 @@ if_notify_change(unsigned c, struct iface *i)
|
|||
ifa_notify_change_(IF_CHANGE_DOWN, a);
|
||||
}
|
||||
|
||||
WALK_LIST(p, active_proto_list)
|
||||
WALK_LIST(p, proto_list)
|
||||
if_send_notify(p, c, i);
|
||||
|
||||
if (c & IF_CHANGE_UP)
|
||||
|
|
|
@ -239,7 +239,7 @@ neigh_up(neighbor *n, struct iface *i, int scope, struct ifa *a)
|
|||
rem_node(&n->n);
|
||||
add_tail(&neigh_hash_table[neigh_hash(n->proto, &n->addr)], &n->n);
|
||||
DBG("Waking up sticky neighbor %I\n", n->addr);
|
||||
if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING)
|
||||
if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
|
||||
n->proto->neigh_notify(n);
|
||||
}
|
||||
|
||||
|
@ -252,7 +252,7 @@ neigh_down(neighbor *n)
|
|||
n->iface = NULL;
|
||||
n->ifa = NULL;
|
||||
n->scope = -1;
|
||||
if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING)
|
||||
if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
|
||||
n->proto->neigh_notify(n);
|
||||
rem_node(&n->n);
|
||||
if (n->flags & NEF_STICKY)
|
||||
|
@ -333,7 +333,7 @@ neigh_if_link(struct iface *i)
|
|||
WALK_LIST_DELSAFE(x, y, i->neighbors)
|
||||
{
|
||||
neighbor *n = SKIP_BACK(neighbor, if_n, x);
|
||||
if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING)
|
||||
if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
|
||||
n->proto->neigh_notify(n);
|
||||
}
|
||||
}
|
||||
|
|
1936
nest/proto.c
1936
nest/proto.c
File diff suppressed because it is too large
Load diff
|
@ -69,23 +69,6 @@ its state by calling the <func/proto_notify_state/ function.
|
|||
|
||||
<p>At any time, the core code can ask the protocol to shut itself down by calling its stop() hook.
|
||||
|
||||
<p>The <em/core state machine/ takes care of the core view of protocol state.
|
||||
The states are traversed according to changes of the protocol state machine, but
|
||||
sometimes the transitions are delayed if the core needs to finish some actions
|
||||
(for example sending of new routes to the protocol) before proceeding to the
|
||||
new state. There are the following core states:
|
||||
|
||||
<descrip>
|
||||
<tag/FS_HUNGRY/ The protocol is down, it doesn't have any routes and
|
||||
doesn't want them.
|
||||
<tag/FS_FEEDING/ The protocol has reached the <tt/PS_UP/ state, but
|
||||
we are still busy sending the initial set of routes to it.
|
||||
<tag/FS_HAPPY/ The protocol is up and has complete routing information.
|
||||
<tag/FS_FLUSHING/ The protocol is shutting down (it's in either <tt/PS_STOP/
|
||||
or <tt/PS_DOWN/ state) and we're flushing all of its routes from the
|
||||
routing tables.
|
||||
</descrip>
|
||||
|
||||
<sect1>Functions of the protocol module
|
||||
|
||||
<p>The protocol module provides the following functions:
|
||||
|
|
291
nest/protocol.h
291
nest/protocol.h
|
@ -11,7 +11,9 @@
|
|||
|
||||
#include "lib/lists.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/event.h"
|
||||
#include "lib/timer.h"
|
||||
#include "nest/route.h"
|
||||
#include "conf/conf.h"
|
||||
|
||||
struct iface;
|
||||
|
@ -22,13 +24,16 @@ struct neighbor;
|
|||
struct rta;
|
||||
struct network;
|
||||
struct proto_config;
|
||||
struct channel_limit;
|
||||
struct channel_config;
|
||||
struct config;
|
||||
struct proto;
|
||||
struct event;
|
||||
struct channel;
|
||||
struct ea_list;
|
||||
struct eattr;
|
||||
struct symbol;
|
||||
|
||||
|
||||
/*
|
||||
* Routing Protocol
|
||||
*/
|
||||
|
@ -39,9 +44,10 @@ struct protocol {
|
|||
char *template; /* Template for automatic generation of names */
|
||||
int name_counter; /* Counter for automatic name generation */
|
||||
int attr_class; /* Attribute class known to this protocol */
|
||||
int multitable; /* Protocol handles all announce hooks itself */
|
||||
uint preference; /* Default protocol preference */
|
||||
uint config_size; /* Size of protocol config */
|
||||
uint channel_mask; /* Mask of accepted channel types (NB_*) */
|
||||
uint proto_size; /* Size of protocol data structure */
|
||||
uint config_size; /* Size of protocol config data structure */
|
||||
|
||||
void (*preconfig)(struct protocol *, struct config *); /* Just before configuring */
|
||||
void (*postconfig)(struct proto_config *); /* After configuring each instance */
|
||||
|
@ -62,7 +68,6 @@ struct protocol {
|
|||
void protos_build(void);
|
||||
void proto_build(struct protocol *);
|
||||
void protos_preconfig(struct config *);
|
||||
void protos_postconfig(struct config *);
|
||||
void protos_commit(struct config *new, struct config *old, int force_restart, int type);
|
||||
void protos_dump_all(void);
|
||||
|
||||
|
@ -90,16 +95,12 @@ struct proto_config {
|
|||
char *name;
|
||||
char *dsc;
|
||||
int class; /* SYM_PROTO or SYM_TEMPLATE */
|
||||
u8 net_type; /* Protocol network type (NET_*), 0 for undefined */
|
||||
u8 disabled; /* Protocol enabled/disabled by default */
|
||||
u32 debug, mrtdump; /* Debugging bitfields, both use D_* constants */
|
||||
unsigned preference, disabled; /* Generic parameters */
|
||||
int in_keep_filtered; /* Routes rejected in import filter are kept */
|
||||
u32 router_id; /* Protocol specific router ID */
|
||||
struct rtable_config *table; /* Table we're attached to */
|
||||
struct filter *in_filter, *out_filter; /* Attached filters */
|
||||
struct proto_limit *rx_limit; /* Limit for receiving routes from protocol
|
||||
(relevant when in_keep_filtered is active) */
|
||||
struct proto_limit *in_limit; /* Limit for importing routes from protocol */
|
||||
struct proto_limit *out_limit; /* Limit for exporting routes to protocol */
|
||||
|
||||
list channels; /* List of channel configs (struct channel_config) */
|
||||
|
||||
/* Check proto_reconfigure() and proto_copy_config() after changing struct proto_config */
|
||||
|
||||
|
@ -111,7 +112,6 @@ struct proto_stats {
|
|||
/* Import - from protocol to core */
|
||||
u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */
|
||||
u32 filt_routes; /* Number of routes rejected in import filter but kept in the routing table */
|
||||
u32 pref_routes; /* Number of routes that are preferred, sum over all routing tables */
|
||||
u32 imp_updates_received; /* Number of route updates received */
|
||||
u32 imp_updates_invalid; /* Number of route updates rejected as invalid */
|
||||
u32 imp_updates_filtered; /* Number of route updates rejected by filters */
|
||||
|
@ -133,36 +133,34 @@ struct proto_stats {
|
|||
};
|
||||
|
||||
struct proto {
|
||||
node n; /* Node in *_proto_list */
|
||||
node glob_node; /* Node in global proto_list */
|
||||
node n; /* Node in global proto_list */
|
||||
struct protocol *proto; /* Protocol */
|
||||
struct proto_config *cf; /* Configuration data */
|
||||
struct proto_config *cf_new; /* Configuration we want to switch to after shutdown (NULL=delete) */
|
||||
pool *pool; /* Pool containing local objects */
|
||||
struct event *attn; /* "Pay attention" event */
|
||||
event *event; /* Protocol event */
|
||||
|
||||
list channels; /* List of channels to rtables (struct channel) */
|
||||
struct channel *main_channel; /* Primary channel */
|
||||
struct rte_src *main_source; /* Primary route source */
|
||||
|
||||
char *name; /* Name of this instance (== cf->name) */
|
||||
u32 debug; /* Debugging flags */
|
||||
u32 mrtdump; /* MRTDump flags */
|
||||
unsigned preference; /* Default route preference */
|
||||
byte accept_ra_types; /* Which types of route announcements are accepted (RA_OPTIMAL or RA_ANY) */
|
||||
uint active_channels; /* Number of active channels */
|
||||
byte net_type; /* Protocol network type (NET_*), 0 for undefined */
|
||||
byte disabled; /* Manually disabled */
|
||||
byte proto_state; /* Protocol state machine (PS_*, see below) */
|
||||
byte core_state; /* Core state machine (FS_*, see below) */
|
||||
byte export_state; /* Route export state (ES_*, see below) */
|
||||
byte active; /* From PS_START to cleanup after PS_STOP */
|
||||
byte do_start; /* Start actions are scheduled */
|
||||
byte do_stop; /* Stop actions are scheduled */
|
||||
byte reconfiguring; /* We're shutting down due to reconfiguration */
|
||||
byte refeeding; /* We are refeeding (valid only if export_state == ES_FEEDING) */
|
||||
byte flushing; /* Protocol is flushed in current flush loop round */
|
||||
byte gr_recovery; /* Protocol should participate in graceful restart recovery */
|
||||
byte gr_lock; /* Graceful restart mechanism should wait for this proto */
|
||||
byte gr_wait; /* Route export to protocol is postponed until graceful restart */
|
||||
byte down_sched; /* Shutdown is scheduled for later (PDS_*) */
|
||||
byte down_code; /* Reason for shutdown (PDC_* codes) */
|
||||
byte merge_limit; /* Maximal number of nexthops for RA_MERGED */
|
||||
u32 hash_key; /* Random key used for hashing of neighbors */
|
||||
bird_clock_t last_state_change; /* Time of last state transition */
|
||||
char *last_state_name_announced; /* Last state name we've announced to the user */
|
||||
struct proto_stats stats; /* Current protocol statistics */
|
||||
|
||||
/*
|
||||
* General protocol hooks:
|
||||
|
@ -177,11 +175,11 @@ struct proto {
|
|||
* It can construct a new rte, add private attributes and
|
||||
* decide whether the route shall be imported: 1=yes, -1=no,
|
||||
* 0=process it through the import filter set by the user.
|
||||
* reload_routes Request protocol to reload all its routes to the core
|
||||
* reload_routes Request channel to reload all its routes to the core
|
||||
* (using rte_update()). Returns: 0=reload cannot be done,
|
||||
* 1= reload is scheduled and will happen (asynchronously).
|
||||
* feed_begin Notify protocol about beginning of route feeding.
|
||||
* feed_end Notify protocol about finish of route feeding.
|
||||
* feed_begin Notify channel about beginning of route feeding.
|
||||
* feed_end Notify channel about finish of route feeding.
|
||||
*/
|
||||
|
||||
void (*if_notify)(struct proto *, unsigned flags, struct iface *i);
|
||||
|
@ -191,9 +189,9 @@ struct proto {
|
|||
struct ea_list *(*make_tmp_attrs)(struct rte *rt, struct linpool *pool);
|
||||
void (*store_tmp_attrs)(struct rte *rt, struct ea_list *attrs);
|
||||
int (*import_control)(struct proto *, struct rte **rt, struct ea_list **attrs, struct linpool *pool);
|
||||
int (*reload_routes)(struct proto *);
|
||||
void (*feed_begin)(struct proto *, int initial);
|
||||
void (*feed_end)(struct proto *);
|
||||
void (*reload_routes)(struct channel *);
|
||||
void (*feed_begin)(struct channel *, int initial);
|
||||
void (*feed_end)(struct channel *);
|
||||
|
||||
/*
|
||||
* Routing entry hooks (called only for routes belonging to this protocol):
|
||||
|
@ -213,14 +211,6 @@ struct proto {
|
|||
void (*rte_insert)(struct network *, struct rte *);
|
||||
void (*rte_remove)(struct network *, struct rte *);
|
||||
|
||||
struct rtable *table; /* Our primary routing table */
|
||||
struct rte_src *main_source; /* Primary route source */
|
||||
struct announce_hook *main_ahook; /* Primary announcement hook */
|
||||
struct announce_hook *ahooks; /* Announcement hooks for this protocol */
|
||||
|
||||
struct fib_iterator *feed_iterator; /* Routing table iterator used during protocol feeding */
|
||||
struct announce_hook *feed_ahook; /* Announce hook we currently feed */
|
||||
|
||||
/* Hic sunt protocol-specific data */
|
||||
};
|
||||
|
||||
|
@ -244,25 +234,20 @@ struct proto_spec {
|
|||
#define PDC_OUT_LIMIT_HIT 0x23 /* Route export limit reached */
|
||||
|
||||
|
||||
void *proto_new(struct proto_config *, unsigned size);
|
||||
void *proto_new(struct proto_config *);
|
||||
void *proto_config_new(struct protocol *, int class);
|
||||
void proto_copy_config(struct proto_config *dest, struct proto_config *src);
|
||||
void proto_request_feeding(struct proto *p);
|
||||
|
||||
static inline void
|
||||
proto_copy_rest(struct proto_config *dest, struct proto_config *src, unsigned size)
|
||||
{ memcpy(dest + 1, src + 1, size - sizeof(struct proto_config)); }
|
||||
|
||||
void graceful_restart_recovery(void);
|
||||
void graceful_restart_init(void);
|
||||
void graceful_restart_show_status(void);
|
||||
void proto_graceful_restart_lock(struct proto *p);
|
||||
void proto_graceful_restart_unlock(struct proto *p);
|
||||
void channel_graceful_restart_lock(struct channel *c);
|
||||
void channel_graceful_restart_unlock(struct channel *c);
|
||||
|
||||
#define DEFAULT_GR_WAIT 240
|
||||
|
||||
void proto_show_limit(struct proto_limit *l, const char *dsc);
|
||||
void proto_show_basic_info(struct proto *p);
|
||||
void channel_show_limit(struct channel_limit *l, const char *dsc);
|
||||
void channel_show_info(struct channel *c);
|
||||
|
||||
void proto_cmd_show(struct proto *, uint, int);
|
||||
void proto_cmd_disable(struct proto *, uint, int);
|
||||
|
@ -285,7 +270,10 @@ proto_get_router_id(struct proto_config *pc)
|
|||
return pc->router_id ? pc->router_id : pc->global->router_id;
|
||||
}
|
||||
|
||||
extern list active_proto_list;
|
||||
/* Moved from route.h to avoid dependency conflicts */
|
||||
static inline void rte_update(struct proto *p, net *net, rte *new) { rte_update2(p->main_channel, net, new, p->main_source); }
|
||||
|
||||
extern list proto_list;
|
||||
|
||||
/*
|
||||
* Each protocol instance runs two different state machines:
|
||||
|
@ -361,16 +349,6 @@ void proto_notify_state(struct proto *p, unsigned state);
|
|||
* as a result of received ROUTE-REFRESH request).
|
||||
*/
|
||||
|
||||
#define FS_HUNGRY 0
|
||||
#define FS_FEEDING 1 /* obsolete */
|
||||
#define FS_HAPPY 2
|
||||
#define FS_FLUSHING 3
|
||||
|
||||
|
||||
#define ES_DOWN 0
|
||||
#define ES_FEEDING 1
|
||||
#define ES_READY 2
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
@ -413,6 +391,7 @@ extern struct proto_config *cf_dev_proto;
|
|||
#define PLD_OUT 2 /* Export limit */
|
||||
#define PLD_MAX 3
|
||||
|
||||
#define PLA_NONE 0 /* No limit */
|
||||
#define PLA_WARN 1 /* Issue log warning */
|
||||
#define PLA_BLOCK 2 /* Block new routes */
|
||||
#define PLA_RESTART 4 /* Force protocol restart */
|
||||
|
@ -422,42 +401,176 @@ extern struct proto_config *cf_dev_proto;
|
|||
#define PLS_ACTIVE 1 /* Limit was hit */
|
||||
#define PLS_BLOCKED 2 /* Limit is active and blocking new routes */
|
||||
|
||||
struct proto_limit {
|
||||
struct channel_limit {
|
||||
u32 limit; /* Maximum number of prefixes */
|
||||
byte action; /* Action to take (PLA_*) */
|
||||
byte state; /* State of limit (PLS_*) */
|
||||
u8 action; /* Action to take (PLA_*) */
|
||||
u8 state; /* State of limit (PLS_*) */
|
||||
};
|
||||
|
||||
void proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 rt_count);
|
||||
void proto_verify_limits(struct announce_hook *ah);
|
||||
|
||||
static inline void
|
||||
proto_reset_limit(struct proto_limit *l)
|
||||
{
|
||||
if (l)
|
||||
l->state = PLS_INITIAL;
|
||||
}
|
||||
void channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count);
|
||||
|
||||
|
||||
/*
|
||||
* Route Announcement Hook
|
||||
* Channels
|
||||
*/
|
||||
|
||||
struct announce_hook {
|
||||
node n;
|
||||
struct rtable *table;
|
||||
struct proto *proto;
|
||||
struct filter *in_filter; /* Input filter */
|
||||
struct filter *out_filter; /* Output filter */
|
||||
struct proto_limit *rx_limit; /* Receive limit (for in_keep_filtered) */
|
||||
struct proto_limit *in_limit; /* Input limit */
|
||||
struct proto_limit *out_limit; /* Output limit */
|
||||
struct proto_stats *stats; /* Per-table protocol statistics */
|
||||
struct announce_hook *next; /* Next hook for the same protocol */
|
||||
int in_keep_filtered; /* Routes rejected in import filter are kept */
|
||||
};
|
||||
struct channel_class {
|
||||
uint channel_size; /* Size of channel data structure */
|
||||
uint config_size; /* Size of channel config data structure */
|
||||
|
||||
struct channel * (*init)(struct channel *, struct channel_config *); /* Create new instance */
|
||||
int (*reconfigure)(struct channel *, struct channel_config *); /* Try to reconfigure instance, returns success */
|
||||
int (*start)(struct channel *); /* Start the instance */
|
||||
int (*shutdown)(struct channel *); /* Stop the instance */
|
||||
|
||||
void (*copy_config)(struct channel_config *, struct channel_config *); /* Copy config from given channel instance */
|
||||
#if 0
|
||||
void (*preconfig)(struct protocol *, struct config *); /* Just before configuring */
|
||||
void (*postconfig)(struct proto_config *); /* After configuring each instance */
|
||||
|
||||
|
||||
void (*dump)(struct proto *); /* Debugging dump */
|
||||
void (*dump_attrs)(struct rte *); /* Dump protocol-dependent attributes */
|
||||
void (*cleanup)(struct proto *); /* Called after shutdown when protocol became hungry/down */
|
||||
void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */
|
||||
void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs); /* Get route information (for `show route' command) */
|
||||
int (*get_attr)(struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */
|
||||
void (*show_proto_info)(struct proto *); /* Show protocol info (for `show protocols all' command) */
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
struct channel_config {
|
||||
node n;
|
||||
const char *name;
|
||||
const struct channel_class *channel;
|
||||
|
||||
struct rtable_config *table; /* Table we're attached to */
|
||||
struct filter *in_filter, *out_filter; /* Attached filters */
|
||||
struct channel_limit rx_limit; /* Limit for receiving routes from protocol
|
||||
(relevant when in_keep_filtered is active) */
|
||||
struct channel_limit in_limit; /* Limit for importing routes from protocol */
|
||||
struct channel_limit out_limit; /* Limit for exporting routes to protocol */
|
||||
|
||||
u8 net_type; /* Routing table network type (NET_*), 0 for undefined */
|
||||
u8 ra_mode; /* Mode of received route advertisements (RA_*) */
|
||||
u16 preference; /* Default route preference */
|
||||
u8 merge_limit; /* Maximal number of nexthops for RA_MERGED */
|
||||
u8 in_keep_filtered; /* Routes rejected in import filter are kept */
|
||||
};
|
||||
|
||||
struct channel {
|
||||
node n; /* Node in proto->channels */
|
||||
node table_node; /* Node in table->channels */
|
||||
|
||||
const char *name; /* Channel name (may be NULL) */
|
||||
const struct channel_class *channel;
|
||||
struct proto *proto;
|
||||
|
||||
struct rtable *table;
|
||||
struct filter *in_filter; /* Input filter */
|
||||
struct filter *out_filter; /* Output filter */
|
||||
struct channel_limit rx_limit; /* Receive limit (for in_keep_filtered) */
|
||||
struct channel_limit in_limit; /* Input limit */
|
||||
struct channel_limit out_limit; /* Output limit */
|
||||
|
||||
struct event *feed_event; /* Event responsible for feeding */
|
||||
struct fib_iterator feed_fit; /* Routing table iterator used during feeding */
|
||||
struct proto_stats stats; /* Per-channel protocol statistics */
|
||||
|
||||
u8 net_type; /* Routing table network type (NET_*), 0 for undefined */
|
||||
u8 ra_mode; /* Mode of received route advertisements (RA_*) */
|
||||
u16 preference; /* Default route preference */
|
||||
u8 merge_limit; /* Maximal number of nexthops for RA_MERGED */
|
||||
u8 in_keep_filtered; /* Routes rejected in import filter are kept */
|
||||
u8 disabled;
|
||||
|
||||
u8 channel_state;
|
||||
u8 export_state; /* Route export state (ES_*, see below) */
|
||||
u8 feed_active;
|
||||
u8 flush_active;
|
||||
u8 refeeding; /* We are refeeding (valid only if export_state == ES_FEEDING) */
|
||||
u8 reloadable; /* Hook reload_routes() is allowed on the channel */
|
||||
u8 gr_lock; /* Graceful restart mechanism should wait for this channel */
|
||||
u8 gr_wait; /* Route export to channel is postponed until graceful restart */
|
||||
|
||||
bird_clock_t last_state_change; /* Time of last state transition */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Channel states
|
||||
*
|
||||
* CS_DOWN - The initial and the final state of a channel. There is no route
|
||||
* exchange between the protocol and the table. Channel is not counted as
|
||||
* active. Channel keeps a ptr to the table, but do not lock the table and is
|
||||
* not linked in the table. Generally, new closed channels are created in
|
||||
* protocols' init() hooks. The protocol is expected to explicitly activate its
|
||||
* channels (by calling channel_init() or channel_open()).
|
||||
*
|
||||
* CS_START - The channel as a connection between the protocol and the table is
|
||||
* initialized (counted as active by the protocol, linked in the table and keeps
|
||||
* the table locked), but there is no current route exchange. There still may be
|
||||
* routes associated with the channel in the routing table if the channel falls
|
||||
* to CS_START from CS_UP. Generally, channels are initialized in protocols'
|
||||
* start() hooks when going to PS_START.
|
||||
*
|
||||
* CS_UP - The channel is initialized and the route exchange is allowed. Note
|
||||
* that even in CS_UP state, route export may still be down (ES_DOWN) by the
|
||||
* core decision (e.g. waiting for table convergence after graceful restart).
|
||||
* I.e., the protocol decides to open the channel but the core decides to start
|
||||
* route export. Route import (caused by rte_update() from the protocol) is not
|
||||
* restricted by that and is on volition of the protocol. Generally, channels
|
||||
* are opened in protocols' start() hooks when going to PS_UP.
|
||||
*
|
||||
* CS_FLUSHING - The transitional state between initialized channel and closed
|
||||
* channel. The channel is still initialized, but no route exchange is allowed.
|
||||
* Instead, the associated table is running flush loop to remove routes imported
|
||||
* through the channel. After that, the channel changes state to CS_DOWN and
|
||||
* is detached from the table (the table is unlocked and the channel is unlinked
|
||||
* from it). Unlike other states, the CS_FLUSHING state is not explicitly
|
||||
* entered or left by the protocol. A protocol may request to close a channel
|
||||
* (by calling channel_close()), which causes the channel to change state to
|
||||
* CS_FLUSHING and later to CS_DOWN. Also note that channels are closed
|
||||
* automatically by the core when the protocol is going down.
|
||||
*
|
||||
* Allowed transitions:
|
||||
*
|
||||
* CS_DOWN -> CS_START / CS_UP
|
||||
* CS_START -> CS_UP / CS_FLUSHING
|
||||
* CS_UP -> CS_START / CS_FLUSHING
|
||||
* CS_FLUSHING -> CS_DOWN (automatic)
|
||||
*/
|
||||
|
||||
#define CS_DOWN 0
|
||||
#define CS_START 1
|
||||
#define CS_UP 2
|
||||
#define CS_FLUSHING 3
|
||||
|
||||
#define ES_DOWN 0
|
||||
#define ES_FEEDING 1
|
||||
#define ES_READY 2
|
||||
|
||||
|
||||
struct channel_config *proto_cf_find_channel(struct proto_config *p, uint net_type);
|
||||
static inline struct channel_config *proto_cf_main_channel(struct proto_config *pc)
|
||||
{ struct channel_config *cc = HEAD(pc->channels); return NODE_VALID(cc) ? cc : NULL; }
|
||||
|
||||
struct channel *proto_find_channel_by_table(struct proto *p, struct rtable *t);
|
||||
struct channel *proto_add_channel(struct proto *p, struct channel_config *cf);
|
||||
int proto_configure_channel(struct proto *p, struct channel **c, struct channel_config *cf);
|
||||
|
||||
void channel_set_state(struct channel *c, uint state);
|
||||
|
||||
/*
|
||||
static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); }
|
||||
static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP); }
|
||||
static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); }
|
||||
*/
|
||||
|
||||
void channel_request_feeding(struct channel *c);
|
||||
void *channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto);
|
||||
int channel_reconfigure(struct channel *c, struct channel_config *cf);
|
||||
|
||||
struct announce_hook *proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats);
|
||||
struct announce_hook *proto_find_announce_hook(struct proto *p, struct rtable *t);
|
||||
|
||||
#endif
|
||||
|
|
43
nest/route.h
43
nest/route.h
|
@ -12,10 +12,12 @@
|
|||
#include "lib/lists.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/timer.h"
|
||||
#include "nest/protocol.h"
|
||||
//#include "nest/protocol.h"
|
||||
|
||||
struct ea_list;
|
||||
struct protocol;
|
||||
struct proto;
|
||||
struct rte_src;
|
||||
struct symbol;
|
||||
struct filter;
|
||||
struct cli;
|
||||
|
@ -57,8 +59,8 @@ struct fib {
|
|||
uint hash_order; /* Binary logarithm of hash_size */
|
||||
uint hash_shift; /* 32 - hash_order */
|
||||
uint addr_type; /* Type of address data stored in fib (NET_*) */
|
||||
uint node_size; /* XXXX */
|
||||
uint node_offset; /* XXXX */
|
||||
uint node_size; /* FIB node size, 0 for nonuniform */
|
||||
uint node_offset; /* Offset of fib_node struct inside of user data */
|
||||
uint entries; /* Number of entries */
|
||||
uint entries_min, entries_max; /* Entry count limits (else start rehashing) */
|
||||
fib_init_fn init; /* Constructor */
|
||||
|
@ -146,7 +148,7 @@ typedef struct rtable {
|
|||
node n; /* Node in list of all tables */
|
||||
struct fib fib;
|
||||
char *name; /* Name of this table */
|
||||
list hooks; /* List of announcement hooks */
|
||||
list channels; /* List of attached channels (struct channel) */
|
||||
uint addr_type; /* Type of address data stored in table (NET_*) */
|
||||
int pipe_busy; /* Pipe loop detection */
|
||||
int use_count; /* Number of protocols using this table */
|
||||
|
@ -159,7 +161,6 @@ typedef struct rtable {
|
|||
struct event *rt_event; /* Routing table event */
|
||||
int gc_counter; /* Number of operations since last GC */
|
||||
bird_clock_t gc_time; /* Time of last GC */
|
||||
byte gc_scheduled; /* GC is scheduled */
|
||||
byte prune_state; /* Table prune state, 1 -> scheduled, 2-> running */
|
||||
byte hcu_scheduled; /* Hostcache update is scheduled */
|
||||
byte nhu_state; /* Next Hop Update state */
|
||||
|
@ -167,10 +168,6 @@ typedef struct rtable {
|
|||
struct fib_iterator nhu_fit; /* Next Hop Update FIB iterator */
|
||||
} rtable;
|
||||
|
||||
#define RPS_NONE 0
|
||||
#define RPS_SCHEDULED 1
|
||||
#define RPS_RUNNING 2
|
||||
|
||||
typedef struct network {
|
||||
struct rte *routes; /* Available routes for this network */
|
||||
struct fib_node n; /* FIB flags reserved for kernel syncer */
|
||||
|
@ -206,7 +203,7 @@ struct hostentry {
|
|||
typedef struct rte {
|
||||
struct rte *next;
|
||||
net *net; /* Network this RTE belongs to */
|
||||
struct announce_hook *sender; /* Announce hook used to send the route to the routing table */
|
||||
struct channel *sender; /* Channel used to send the route to the routing table */
|
||||
struct rta *attrs; /* Attributes of this route */
|
||||
byte flags; /* Flags (REF_...) */
|
||||
byte pflags; /* Protocol-specific flags */
|
||||
|
@ -279,13 +276,14 @@ static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) f
|
|||
|
||||
rte *rte_find(net *net, struct rte_src *src);
|
||||
rte *rte_get_temp(struct rta *);
|
||||
void rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src);
|
||||
static inline void rte_update(struct proto *p, net *net, rte *new) { rte_update2(p->main_ahook, net, new, p->main_source); }
|
||||
void rte_update2(struct channel *c, net *net, rte *new, struct rte_src *src);
|
||||
/* rte_update() moved to protocol.h to avoid dependency conflicts */
|
||||
void rte_discard(rtable *tab, rte *old);
|
||||
int rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter);
|
||||
rte *rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, struct ea_list **tmpa, int silent);
|
||||
void rt_refresh_begin(rtable *t, struct announce_hook *ah);
|
||||
void rt_refresh_end(rtable *t, struct announce_hook *ah);
|
||||
rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, struct ea_list **tmpa, int silent);
|
||||
void rt_refresh_begin(rtable *t, struct channel *c);
|
||||
void rt_refresh_end(rtable *t, struct channel *c);
|
||||
void rt_schedule_prune(rtable *t);
|
||||
void rte_dump(rte *);
|
||||
void rte_free(rte *);
|
||||
rte *rte_do_cow(rte *);
|
||||
|
@ -293,19 +291,10 @@ static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r
|
|||
rte *rte_cow_rta(rte *r, linpool *lp);
|
||||
void rt_dump(rtable *);
|
||||
void rt_dump_all(void);
|
||||
int rt_feed_baby(struct proto *p);
|
||||
void rt_feed_baby_abort(struct proto *p);
|
||||
int rt_prune_loop(void);
|
||||
int rt_feed_channel(struct channel *c);
|
||||
void rt_feed_channel_abort(struct channel *c);
|
||||
struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
|
||||
|
||||
static inline void
|
||||
rt_mark_for_prune(rtable *tab)
|
||||
{
|
||||
if (tab->prune_state == RPS_RUNNING)
|
||||
fit_get(&tab->fib, &tab->prune_fit);
|
||||
|
||||
tab->prune_state = RPS_SCHEDULED;
|
||||
}
|
||||
|
||||
struct rt_show_data {
|
||||
net_addr *addr;
|
||||
|
@ -315,6 +304,7 @@ struct rt_show_data {
|
|||
struct fib_iterator fit;
|
||||
struct proto *show_protocol;
|
||||
struct proto *export_protocol;
|
||||
struct channel *export_channel;
|
||||
int export_mode, primary_only, filtered;
|
||||
struct config *running_on_config;
|
||||
int net_counter, rt_counter, show_counter;
|
||||
|
@ -561,7 +551,6 @@ extern struct protocol *attr_class_to_protocol[EAP_MAX];
|
|||
#define DEF_PREF_OSPF 150 /* OSPF intra-area, inter-area and type 1 external routes */
|
||||
#define DEF_PREF_RIP 120 /* RIP */
|
||||
#define DEF_PREF_BGP 100 /* BGP */
|
||||
#define DEF_PREF_PIPE 70 /* Routes piped from other tables */
|
||||
#define DEF_PREF_INHERITED 10 /* Routes inherited from other routing daemons */
|
||||
|
||||
/*
|
||||
|
|
|
@ -24,13 +24,16 @@
|
|||
#include "lib/resource.h"
|
||||
#include "lib/string.h"
|
||||
|
||||
static void
|
||||
dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
|
||||
{
|
||||
struct rt_dev_config *P = (void *) p->cf;
|
||||
|
||||
if (!EMPTY_LIST(P->iface_list) &&
|
||||
!iface_patt_find(&P->iface_list, ad->iface, ad->iface->addr))
|
||||
static void
|
||||
dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
|
||||
{
|
||||
struct rt_dev_proto *p = (void *) P;
|
||||
struct rt_dev_config *cf = (void *) P->cf;
|
||||
struct channel *c;
|
||||
|
||||
if (!EMPTY_LIST(cf->iface_list) &&
|
||||
!iface_patt_find(&cf->iface_list, ad->iface, ad->iface->addr))
|
||||
/* Empty list is automagically treated as "*" */
|
||||
return;
|
||||
|
||||
|
@ -40,12 +43,22 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
|
|||
if (ad->scope <= SCOPE_LINK)
|
||||
return;
|
||||
|
||||
if (c & IF_CHANGE_DOWN)
|
||||
if (ad->prefix.type == NET_IP4)
|
||||
c = p->ip4_channel;
|
||||
else if (ad->prefix.type == NET_IP6)
|
||||
c = p->ip6_channel;
|
||||
else
|
||||
return;
|
||||
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
if (flags & IF_CHANGE_DOWN)
|
||||
{
|
||||
net *n;
|
||||
|
||||
DBG("dev_if_notify: %s:%I going down\n", ad->iface->name, ad->ip);
|
||||
n = net_find(p->table, &ad->prefix);
|
||||
n = net_find(c->table, &ad->prefix);
|
||||
if (!n)
|
||||
{
|
||||
DBG("dev_if_notify: device shutdown: prefix not found\n");
|
||||
|
@ -53,10 +66,10 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
|
|||
}
|
||||
|
||||
/* Use iface ID as local source ID */
|
||||
struct rte_src *src = rt_get_source(p, ad->iface->index);
|
||||
rte_update2(p->main_ahook, n, NULL, src);
|
||||
struct rte_src *src = rt_get_source(P, ad->iface->index);
|
||||
rte_update2(c, n, NULL, src);
|
||||
}
|
||||
else if (c & IF_CHANGE_UP)
|
||||
else if (flags & IF_CHANGE_UP)
|
||||
{
|
||||
rta *a;
|
||||
net *n;
|
||||
|
@ -65,7 +78,7 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
|
|||
DBG("dev_if_notify: %s:%I going up\n", ad->iface->name, ad->ip);
|
||||
|
||||
/* Use iface ID as local source ID */
|
||||
struct rte_src *src = rt_get_source(p, ad->iface->index);
|
||||
struct rte_src *src = rt_get_source(P, ad->iface->index);
|
||||
|
||||
rta a0 = {
|
||||
.src = src,
|
||||
|
@ -77,37 +90,51 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
|
|||
};
|
||||
|
||||
a = rta_lookup(&a0);
|
||||
n = net_get(p->table, &ad->prefix);
|
||||
n = net_get(c->table, &ad->prefix);
|
||||
e = rte_get_temp(a);
|
||||
e->net = n;
|
||||
e->pflags = 0;
|
||||
rte_update2(p->main_ahook, n, e, src);
|
||||
rte_update2(c, n, e, src);
|
||||
}
|
||||
}
|
||||
|
||||
static struct proto *
|
||||
dev_init(struct proto_config *c)
|
||||
dev_init(struct proto_config *CF)
|
||||
{
|
||||
struct proto *p = proto_new(c, sizeof(struct proto));
|
||||
struct proto *P = proto_new(CF);
|
||||
struct rt_dev_proto *p = (void *) P;
|
||||
// struct rt_dev_config *cf = (void *) CF;
|
||||
|
||||
p->ifa_notify = dev_ifa_notify;
|
||||
return p;
|
||||
proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4));
|
||||
proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6));
|
||||
|
||||
P->ifa_notify = dev_ifa_notify;
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
static int
|
||||
dev_reconfigure(struct proto *p, struct proto_config *new)
|
||||
dev_reconfigure(struct proto *P, struct proto_config *CF)
|
||||
{
|
||||
struct rt_dev_config *o = (struct rt_dev_config *) p->cf;
|
||||
struct rt_dev_config *n = (struct rt_dev_config *) new;
|
||||
struct rt_dev_proto *p = (void *) P;
|
||||
struct rt_dev_config *o = (void *) P->cf;
|
||||
struct rt_dev_config *n = (void *) CF;
|
||||
|
||||
return iface_patts_equal(&o->iface_list, &n->iface_list, NULL);
|
||||
if (!iface_patts_equal(&o->iface_list, &n->iface_list, NULL))
|
||||
return 0;
|
||||
|
||||
return
|
||||
proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)) &&
|
||||
proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
dev_copy_config(struct proto_config *dest, struct proto_config *src)
|
||||
{
|
||||
struct rt_dev_config *d = (struct rt_dev_config *) dest;
|
||||
struct rt_dev_config *s = (struct rt_dev_config *) src;
|
||||
struct rt_dev_config *d = (void *) dest;
|
||||
struct rt_dev_config *s = (void *) src;
|
||||
|
||||
/*
|
||||
* We copy iface_list as ifaces can be shared by more direct protocols.
|
||||
|
@ -120,7 +147,9 @@ dev_copy_config(struct proto_config *dest, struct proto_config *src)
|
|||
struct protocol proto_device = {
|
||||
.name = "Direct",
|
||||
.template = "direct%d",
|
||||
.preference = DEF_PREF_DIRECT,
|
||||
.preference = DEF_PREF_DIRECT,
|
||||
.channel_mask = NB_IP,
|
||||
.proto_size = sizeof(struct rt_dev_proto),
|
||||
.config_size = sizeof(struct rt_dev_config),
|
||||
.init = dev_init,
|
||||
.reconfigure = dev_reconfigure,
|
||||
|
|
|
@ -14,4 +14,10 @@ struct rt_dev_config {
|
|||
list iface_list; /* list of struct iface_patt */
|
||||
};
|
||||
|
||||
struct rt_dev_proto {
|
||||
struct proto p;
|
||||
struct channel *ip4_channel;
|
||||
struct channel *ip6_channel;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
579
nest/rt-table.c
579
nest/rt-table.c
File diff suppressed because it is too large
Load diff
|
@ -952,7 +952,7 @@ bfd_init_all(void)
|
|||
static struct proto *
|
||||
bfd_init(struct proto_config *c)
|
||||
{
|
||||
struct proto *p = proto_new(c, sizeof(struct bfd_proto));
|
||||
struct proto *p = proto_new(c);
|
||||
|
||||
p->neigh_notify = bfd_neigh_notify;
|
||||
|
||||
|
@ -1118,6 +1118,7 @@ bfd_show_sessions(struct proto *P)
|
|||
struct protocol proto_bfd = {
|
||||
.name = "BFD",
|
||||
.template = "bfd%d",
|
||||
.proto_size = sizeof(struct bfd_proto),
|
||||
.config_size = sizeof(struct bfd_config),
|
||||
.init = bfd_init,
|
||||
.start = bfd_start,
|
||||
|
|
|
@ -1372,7 +1372,6 @@ static void
|
|||
bgp_copy_config(struct proto_config *dest, struct proto_config *src)
|
||||
{
|
||||
/* Just a shallow copy */
|
||||
proto_copy_rest(dest, src, sizeof(struct bgp_config));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -112,12 +112,6 @@ bgp_proto:
|
|||
| bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
|
||||
| bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; }
|
||||
| bgp_proto PASSWORD text ';' { BGP_CFG->password = $3; }
|
||||
| bgp_proto ROUTE LIMIT expr ';' {
|
||||
this_proto->in_limit = cfg_allocz(sizeof(struct proto_limit));
|
||||
this_proto->in_limit->limit = $4;
|
||||
this_proto->in_limit->action = PLA_RESTART;
|
||||
log(L_WARN "%s: Route limit option is deprecated, use import limit", this_proto->name);
|
||||
}
|
||||
| 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; }
|
||||
|
|
|
@ -68,6 +68,10 @@ ospf_proto_finish(void)
|
|||
if (EMPTY_LIST(cf->area_list))
|
||||
cf_error( "No configured areas in OSPF");
|
||||
|
||||
/* Define default channel */
|
||||
if (EMPTY_LIST(this_proto->channels))
|
||||
channel_config_new(NULL, this_proto->net_type, this_proto);
|
||||
|
||||
int areano = 0;
|
||||
int backbone = 0;
|
||||
int nssa = 0;
|
||||
|
@ -84,7 +88,7 @@ ospf_proto_finish(void)
|
|||
cf->abr = areano > 1;
|
||||
|
||||
/* Route export or NSSA translation (RFC 3101 3.1) */
|
||||
cf->asbr = (this_proto->out_filter != FILTER_REJECT) || (nssa && cf->abr);
|
||||
cf->asbr = (proto_cf_main_channel(this_proto)->out_filter != FILTER_REJECT) || (nssa && cf->abr);
|
||||
|
||||
if (cf->abr && !backbone)
|
||||
{
|
||||
|
@ -145,14 +149,16 @@ ospf_variant:
|
|||
| OSPF3 { $$ = 0; }
|
||||
;
|
||||
|
||||
ospf_proto_start: proto_start ospf_variant {
|
||||
this_proto = proto_config_new(&proto_ospf, $1);
|
||||
init_list(&OSPF_CFG->area_list);
|
||||
init_list(&OSPF_CFG->vlink_list);
|
||||
OSPF_CFG->tick = OSPF_DEFAULT_TICK;
|
||||
OSPF_CFG->ospf2 = $2;
|
||||
}
|
||||
;
|
||||
ospf_proto_start: proto_start ospf_variant
|
||||
{
|
||||
this_proto = proto_config_new(&proto_ospf, $1);
|
||||
this_proto->net_type = $2 ? NET_IP4 : NET_IP6;
|
||||
|
||||
init_list(&OSPF_CFG->area_list);
|
||||
init_list(&OSPF_CFG->vlink_list);
|
||||
OSPF_CFG->tick = OSPF_DEFAULT_TICK;
|
||||
OSPF_CFG->ospf2 = $2;
|
||||
};
|
||||
|
||||
ospf_proto:
|
||||
ospf_proto_start proto_name '{'
|
||||
|
@ -161,6 +167,7 @@ ospf_proto:
|
|||
|
||||
ospf_proto_item:
|
||||
proto_item
|
||||
| proto_channel
|
||||
| RFC1583COMPAT bool { OSPF_CFG->rfc1583 = $2; }
|
||||
| STUB ROUTER bool { OSPF_CFG->stub_router = $3; }
|
||||
| ECMP bool { OSPF_CFG->ecmp = $2 ? OSPF_DEFAULT_ECMP_LIMIT : 0; }
|
||||
|
|
|
@ -102,7 +102,7 @@
|
|||
static int ospf_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool);
|
||||
static struct ea_list *ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool);
|
||||
static void ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs);
|
||||
static int ospf_reload_routes(struct proto *P);
|
||||
static void ospf_reload_routes(struct channel *C);
|
||||
static int ospf_rte_better(struct rte *new, struct rte *old);
|
||||
static int ospf_rte_same(struct rte *new, struct rte *old);
|
||||
static void ospf_disp(timer *timer);
|
||||
|
@ -297,15 +297,16 @@ ospf_dump(struct proto *P)
|
|||
}
|
||||
|
||||
static struct proto *
|
||||
ospf_init(struct proto_config *c)
|
||||
ospf_init(struct proto_config *CF)
|
||||
{
|
||||
struct ospf_config *oc = (struct ospf_config *) c;
|
||||
struct proto *P = proto_new(c, sizeof(struct ospf_proto));
|
||||
struct ospf_config *cf = (struct ospf_config *) CF;
|
||||
struct proto *P = proto_new(CF);
|
||||
|
||||
P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
|
||||
|
||||
P->accept_ra_types = RA_OPTIMAL;
|
||||
P->rt_notify = ospf_rt_notify;
|
||||
P->if_notify = ospf_if_notify;
|
||||
P->ifa_notify = oc->ospf2 ? ospf_ifa_notify2 : ospf_ifa_notify3;
|
||||
P->ifa_notify = cf->ospf2 ? ospf_ifa_notify2 : ospf_ifa_notify3;
|
||||
P->import_control = ospf_import_control;
|
||||
P->reload_routes = ospf_reload_routes;
|
||||
P->make_tmp_attrs = ospf_make_tmp_attrs;
|
||||
|
@ -389,17 +390,16 @@ ospf_schedule_rtcalc(struct ospf_proto *p)
|
|||
p->calcrt = 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ospf_reload_routes(struct proto *P)
|
||||
static void
|
||||
ospf_reload_routes(struct channel *C)
|
||||
{
|
||||
struct ospf_proto *p = (struct ospf_proto *) P;
|
||||
struct ospf_proto *p = (struct ospf_proto *) C->proto;
|
||||
|
||||
if (p->calcrt != 2)
|
||||
OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation with route reload");
|
||||
if (p->calcrt == 2)
|
||||
return;
|
||||
|
||||
OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation with route reload");
|
||||
p->calcrt = 2;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -637,17 +637,17 @@ ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac)
|
|||
* nonbroadcast network, cost of interface, etc.
|
||||
*/
|
||||
static int
|
||||
ospf_reconfigure(struct proto *P, struct proto_config *c)
|
||||
ospf_reconfigure(struct proto *P, struct proto_config *CF)
|
||||
{
|
||||
struct ospf_proto *p = (struct ospf_proto *) P;
|
||||
struct ospf_config *old = (struct ospf_config *) (P->cf);
|
||||
struct ospf_config *new = (struct ospf_config *) c;
|
||||
struct ospf_config *new = (struct ospf_config *) CF;
|
||||
struct ospf_area_config *nac;
|
||||
struct ospf_area *oa, *oax;
|
||||
struct ospf_iface *ifa, *ifx;
|
||||
struct ospf_iface_patt *ip;
|
||||
|
||||
if (proto_get_router_id(c) != p->router_id)
|
||||
if (proto_get_router_id(CF) != p->router_id)
|
||||
return 0;
|
||||
|
||||
if (p->ospf2 != new->ospf2)
|
||||
|
@ -659,6 +659,9 @@ ospf_reconfigure(struct proto *P, struct proto_config *c)
|
|||
if (old->abr != new->abr)
|
||||
return 0;
|
||||
|
||||
if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
|
||||
return 0;
|
||||
|
||||
p->stub_router = new->stub_router;
|
||||
p->merge_external = new->merge_external;
|
||||
p->asbr = new->asbr;
|
||||
|
@ -1465,6 +1468,8 @@ struct protocol proto_ospf = {
|
|||
.template = "ospf%d",
|
||||
.attr_class = EAP_OSPF,
|
||||
.preference = DEF_PREF_OSPF,
|
||||
.channel_mask = NB_IP,
|
||||
.proto_size = sizeof(struct ospf_proto),
|
||||
.config_size = sizeof(struct ospf_config),
|
||||
.init = ospf_init,
|
||||
.dump = ospf_dump,
|
||||
|
|
|
@ -1973,7 +1973,7 @@ again1:
|
|||
|
||||
if (reload || ort_changed(nf, &a0))
|
||||
{
|
||||
net *ne = net_get(p->p.table, nf->fn.addr);
|
||||
net *ne = net_get(p->p.main_channel->table, nf->fn.addr);
|
||||
rta *a = rta_lookup(&a0);
|
||||
rte *e = rte_get_temp(a);
|
||||
|
||||
|
@ -1985,7 +1985,6 @@ again1:
|
|||
e->u.ospf.router_id = nf->old_rid = nf->n.rid;
|
||||
e->pflags = 0;
|
||||
e->net = ne;
|
||||
e->pref = p->p.preference;
|
||||
|
||||
DBG("Mod rte type %d - %N via %I on iface %s, met %d\n",
|
||||
a0.source, nf->fn.addr, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1);
|
||||
|
@ -1998,7 +1997,7 @@ again1:
|
|||
rta_free(nf->old_rta);
|
||||
nf->old_rta = NULL;
|
||||
|
||||
net *ne = net_get(p->p.table, nf->fn.addr);
|
||||
net *ne = net_get(p->p.main_channel->table, nf->fn.addr);
|
||||
rte_update(&p->p, ne, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,28 +16,25 @@ CF_DEFINES
|
|||
|
||||
CF_DECLS
|
||||
|
||||
CF_KEYWORDS(PIPE, PEER, TABLE, MODE, OPAQUE, TRANSPARENT)
|
||||
CF_KEYWORDS(PIPE, PEER, TABLE)
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
CF_ADDTO(proto, pipe_proto '}')
|
||||
CF_ADDTO(proto, pipe_proto '}' { this_channel = NULL; } )
|
||||
|
||||
pipe_proto_start: proto_start PIPE {
|
||||
this_proto = proto_config_new(&proto_pipe, $1);
|
||||
PIPE_CFG->mode = PIPE_TRANSPARENT;
|
||||
}
|
||||
;
|
||||
pipe_proto_start: proto_start PIPE
|
||||
{
|
||||
this_proto = proto_config_new(&proto_pipe, $1);
|
||||
this_channel = channel_config_new(NULL, 0, this_proto);
|
||||
this_channel->in_filter = FILTER_ACCEPT;
|
||||
this_channel->out_filter = FILTER_ACCEPT;
|
||||
};
|
||||
|
||||
pipe_proto:
|
||||
pipe_proto_start proto_name '{'
|
||||
| pipe_proto proto_item ';'
|
||||
| pipe_proto PEER TABLE SYM ';' {
|
||||
if ($4->class != SYM_TABLE)
|
||||
cf_error("Routing table name expected");
|
||||
PIPE_CFG->peer = $4->def;
|
||||
}
|
||||
| pipe_proto MODE OPAQUE ';' { PIPE_CFG->mode = PIPE_OPAQUE; }
|
||||
| pipe_proto MODE TRANSPARENT ';' { PIPE_CFG->mode = PIPE_TRANSPARENT; }
|
||||
| pipe_proto channel_item ';'
|
||||
| pipe_proto PEER TABLE rtable ';' { PIPE_CFG->peer = $4; }
|
||||
;
|
||||
|
||||
CF_CODE
|
||||
|
|
|
@ -46,9 +46,8 @@
|
|||
static void
|
||||
pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, ea_list *attrs)
|
||||
{
|
||||
struct pipe_proto *p = (struct pipe_proto *) P;
|
||||
struct announce_hook *ah = (src_table == P->table) ? p->peer_ahook : P->main_ahook;
|
||||
rtable *dst_table = ah->table;
|
||||
struct pipe_proto *p = (void *) P;
|
||||
struct channel *dst = (src_table == p->pri->table) ? p->sec : p->pri;
|
||||
struct rte_src *src;
|
||||
|
||||
net *nn;
|
||||
|
@ -58,24 +57,18 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
|
|||
if (!new && !old)
|
||||
return;
|
||||
|
||||
if (dst_table->pipe_busy)
|
||||
if (dst->table->pipe_busy)
|
||||
{
|
||||
log(L_ERR "Pipe loop detected when sending %N to table %s",
|
||||
n->n.addr, dst_table->name);
|
||||
n->n.addr, dst->table->name);
|
||||
return;
|
||||
}
|
||||
|
||||
nn = net_get(dst_table, n->n.addr);
|
||||
nn = net_get(dst->table, n->n.addr);
|
||||
if (new)
|
||||
{
|
||||
memcpy(&a, new->attrs, sizeof(rta));
|
||||
|
||||
if (p->mode == PIPE_OPAQUE)
|
||||
{
|
||||
a.src = P->main_source;
|
||||
a.source = RTS_PIPE;
|
||||
}
|
||||
|
||||
a.aflags = 0;
|
||||
a.eattrs = attrs;
|
||||
a.hostentry = NULL;
|
||||
|
@ -83,13 +76,10 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
|
|||
e->net = nn;
|
||||
e->pflags = 0;
|
||||
|
||||
if (p->mode == PIPE_TRANSPARENT)
|
||||
{
|
||||
/* Copy protocol specific embedded attributes. */
|
||||
memcpy(&(e->u), &(new->u), sizeof(e->u));
|
||||
e->pref = new->pref;
|
||||
e->pflags = new->pflags;
|
||||
}
|
||||
/* Copy protocol specific embedded attributes. */
|
||||
memcpy(&(e->u), &(new->u), sizeof(e->u));
|
||||
e->pref = new->pref;
|
||||
e->pflags = new->pflags;
|
||||
|
||||
src = a.src;
|
||||
}
|
||||
|
@ -100,7 +90,7 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
|
|||
}
|
||||
|
||||
src_table->pipe_busy = 1;
|
||||
rte_update2(ah, nn, e, src);
|
||||
rte_update2(dst, nn, e, src);
|
||||
src_table->pipe_busy = 0;
|
||||
}
|
||||
|
||||
|
@ -111,171 +101,117 @@ pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpo
|
|||
|
||||
if (pp == P)
|
||||
return -1; /* Avoid local loops automatically */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pipe_reload_routes(struct proto *P)
|
||||
static void
|
||||
pipe_reload_routes(struct channel *C)
|
||||
{
|
||||
struct pipe_proto *p = (struct pipe_proto *) P;
|
||||
struct pipe_proto *p = (void *) C->proto;
|
||||
|
||||
/*
|
||||
* Because the pipe protocol feeds routes from both routing tables
|
||||
* together, both directions are reloaded during refeed and 'reload
|
||||
* out' command works like 'reload' command. For symmetry, we also
|
||||
* request refeed when 'reload in' command is used.
|
||||
*/
|
||||
proto_request_feeding(P);
|
||||
/* Route reload on one channel is just refeed on the other */
|
||||
channel_request_feeding((C == p->pri) ? p->sec : p->pri);
|
||||
}
|
||||
|
||||
proto_reset_limit(P->main_ahook->in_limit);
|
||||
proto_reset_limit(p->peer_ahook->in_limit);
|
||||
|
||||
return 1;
|
||||
static void
|
||||
pipe_postconfig(struct proto_config *CF)
|
||||
{
|
||||
struct pipe_config *cf = (void *) CF;
|
||||
struct channel_config *cc = proto_cf_main_channel(CF);
|
||||
|
||||
if (!cc->table)
|
||||
cf_error("Primary routing table not specified");
|
||||
|
||||
if (!cf->peer)
|
||||
cf_error("Secondary routing table not specified");
|
||||
|
||||
if (cc->table == cf->peer)
|
||||
cf_error("Primary table and peer table must be different");
|
||||
|
||||
if (cc->table->addr_type != cf->peer->addr_type)
|
||||
cf_error("Primary table and peer table must have the same type");
|
||||
|
||||
if (cc->rx_limit.action)
|
||||
cf_error("Pipe protocol does not support receive limits");
|
||||
|
||||
if (cc->in_keep_filtered)
|
||||
cf_error("Pipe protocol prohibits keeping filtered routes");
|
||||
}
|
||||
|
||||
static int
|
||||
pipe_configure_channels(struct pipe_proto *p, struct pipe_config *cf)
|
||||
{
|
||||
struct channel_config *cc = proto_cf_main_channel(&cf->c);
|
||||
|
||||
struct channel_config pri_cf = {
|
||||
.name = "pri",
|
||||
.channel = cc->channel,
|
||||
.table = cc->table,
|
||||
.out_filter = cc->out_filter,
|
||||
.in_limit = cc->in_limit,
|
||||
.ra_mode = RA_ANY
|
||||
};
|
||||
|
||||
struct channel_config sec_cf = {
|
||||
.name = "sec",
|
||||
.channel = cc->channel,
|
||||
.table = cf->peer,
|
||||
.out_filter = cc->in_filter,
|
||||
.in_limit = cc->out_limit,
|
||||
.ra_mode = RA_ANY
|
||||
};
|
||||
|
||||
return
|
||||
proto_configure_channel(&p->p, &p->pri, &pri_cf) &&
|
||||
proto_configure_channel(&p->p, &p->sec, &sec_cf);
|
||||
}
|
||||
|
||||
static struct proto *
|
||||
pipe_init(struct proto_config *C)
|
||||
pipe_init(struct proto_config *CF)
|
||||
{
|
||||
struct pipe_config *c = (struct pipe_config *) C;
|
||||
struct proto *P = proto_new(C, sizeof(struct pipe_proto));
|
||||
struct pipe_proto *p = (struct pipe_proto *) P;
|
||||
struct proto *P = proto_new(CF);
|
||||
struct pipe_proto *p = (void *) P;
|
||||
struct pipe_config *cf = (void *) CF;
|
||||
|
||||
p->mode = c->mode;
|
||||
p->peer_table = c->peer->table;
|
||||
P->accept_ra_types = (p->mode == PIPE_OPAQUE) ? RA_OPTIMAL : RA_ANY;
|
||||
P->rt_notify = pipe_rt_notify;
|
||||
P->import_control = pipe_import_control;
|
||||
P->reload_routes = pipe_reload_routes;
|
||||
|
||||
pipe_configure_channels(p, cf);
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
static int
|
||||
pipe_start(struct proto *P)
|
||||
pipe_reconfigure(struct proto *P, struct proto_config *CF)
|
||||
{
|
||||
struct pipe_config *cf = (struct pipe_config *) P->cf;
|
||||
struct pipe_proto *p = (struct pipe_proto *) P;
|
||||
struct pipe_proto *p = (void *) P;
|
||||
struct pipe_config *cf = (void *) CF;
|
||||
|
||||
/* Lock both tables, unlock is handled in pipe_cleanup() */
|
||||
rt_lock_table(P->table);
|
||||
rt_lock_table(p->peer_table);
|
||||
|
||||
/* Going directly to PS_UP - prepare for feeding,
|
||||
connect the protocol to both routing tables */
|
||||
|
||||
P->main_ahook = proto_add_announce_hook(P, P->table, &P->stats);
|
||||
P->main_ahook->out_filter = cf->c.out_filter;
|
||||
P->main_ahook->in_limit = cf->c.in_limit;
|
||||
proto_reset_limit(P->main_ahook->in_limit);
|
||||
|
||||
p->peer_ahook = proto_add_announce_hook(P, p->peer_table, &p->peer_stats);
|
||||
p->peer_ahook->out_filter = cf->c.in_filter;
|
||||
p->peer_ahook->in_limit = cf->c.out_limit;
|
||||
proto_reset_limit(p->peer_ahook->in_limit);
|
||||
|
||||
if (p->mode == PIPE_OPAQUE)
|
||||
{
|
||||
P->main_source = rt_get_source(P, 0);
|
||||
rt_lock_source(P->main_source);
|
||||
}
|
||||
|
||||
return PS_UP;
|
||||
}
|
||||
|
||||
static void
|
||||
pipe_cleanup(struct proto *P)
|
||||
{
|
||||
struct pipe_proto *p = (struct pipe_proto *) P;
|
||||
|
||||
bzero(&P->stats, sizeof(struct proto_stats));
|
||||
bzero(&p->peer_stats, sizeof(struct proto_stats));
|
||||
|
||||
P->main_ahook = NULL;
|
||||
p->peer_ahook = NULL;
|
||||
|
||||
if (p->mode == PIPE_OPAQUE)
|
||||
rt_unlock_source(P->main_source);
|
||||
P->main_source = NULL;
|
||||
|
||||
rt_unlock_table(P->table);
|
||||
rt_unlock_table(p->peer_table);
|
||||
}
|
||||
|
||||
static void
|
||||
pipe_postconfig(struct proto_config *C)
|
||||
{
|
||||
struct pipe_config *c = (struct pipe_config *) C;
|
||||
|
||||
if (!c->peer)
|
||||
cf_error("Name of peer routing table not specified");
|
||||
if (c->peer == C->table)
|
||||
cf_error("Primary table and peer table must be different");
|
||||
|
||||
if (C->in_keep_filtered)
|
||||
cf_error("Pipe protocol prohibits keeping filtered routes");
|
||||
if (C->rx_limit)
|
||||
cf_error("Pipe protocol does not support receive limits");
|
||||
}
|
||||
|
||||
extern int proto_reconfig_type;
|
||||
|
||||
static int
|
||||
pipe_reconfigure(struct proto *P, struct proto_config *new)
|
||||
{
|
||||
struct pipe_proto *p = (struct pipe_proto *)P;
|
||||
struct proto_config *old = P->cf;
|
||||
struct pipe_config *oc = (struct pipe_config *) old;
|
||||
struct pipe_config *nc = (struct pipe_config *) new;
|
||||
|
||||
if ((oc->peer->table != nc->peer->table) || (oc->mode != nc->mode))
|
||||
return 0;
|
||||
|
||||
/* Update output filters in ahooks */
|
||||
if (P->main_ahook)
|
||||
{
|
||||
P->main_ahook->out_filter = new->out_filter;
|
||||
P->main_ahook->in_limit = new->in_limit;
|
||||
proto_verify_limits(P->main_ahook);
|
||||
}
|
||||
|
||||
if (p->peer_ahook)
|
||||
{
|
||||
p->peer_ahook->out_filter = new->in_filter;
|
||||
p->peer_ahook->in_limit = new->out_limit;
|
||||
proto_verify_limits(p->peer_ahook);
|
||||
}
|
||||
|
||||
if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT))
|
||||
return 1;
|
||||
|
||||
if ((new->preference != old->preference)
|
||||
|| ! filter_same(new->in_filter, old->in_filter)
|
||||
|| ! filter_same(new->out_filter, old->out_filter))
|
||||
proto_request_feeding(P);
|
||||
|
||||
return 1;
|
||||
return pipe_configure_channels(p, cf);
|
||||
}
|
||||
|
||||
static void
|
||||
pipe_copy_config(struct proto_config *dest, struct proto_config *src)
|
||||
{
|
||||
/* Just a shallow copy, not many items here */
|
||||
proto_copy_rest(dest, src, sizeof(struct pipe_config));
|
||||
}
|
||||
|
||||
static void
|
||||
pipe_get_status(struct proto *P, byte *buf)
|
||||
{
|
||||
struct pipe_proto *p = (struct pipe_proto *) P;
|
||||
struct pipe_proto *p = (void *) P;
|
||||
|
||||
bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer_table->name);
|
||||
bsprintf(buf, "%s <=> %s", p->pri->table->name, p->sec->table->name);
|
||||
}
|
||||
|
||||
static void
|
||||
pipe_show_stats(struct pipe_proto *p)
|
||||
{
|
||||
struct proto_stats *s1 = &p->p.stats;
|
||||
struct proto_stats *s2 = &p->peer_stats;
|
||||
struct proto_stats *s1 = &p->pri->stats;
|
||||
struct proto_stats *s2 = &p->sec->stats;
|
||||
|
||||
/*
|
||||
* Pipe stats (as anything related to pipes) are a bit tricky. There
|
||||
|
@ -318,17 +254,16 @@ pipe_show_stats(struct pipe_proto *p)
|
|||
static void
|
||||
pipe_show_proto_info(struct proto *P)
|
||||
{
|
||||
struct pipe_proto *p = (struct pipe_proto *) P;
|
||||
struct pipe_config *cf = (struct pipe_config *) P->cf;
|
||||
struct pipe_proto *p = (void *) P;
|
||||
|
||||
// cli_msg(-1006, " Table: %s", P->table->name);
|
||||
// cli_msg(-1006, " Peer table: %s", p->peer_table->name);
|
||||
cli_msg(-1006, " Preference: %d", P->preference);
|
||||
cli_msg(-1006, " Input filter: %s", filter_name(cf->c.in_filter));
|
||||
cli_msg(-1006, " Output filter: %s", filter_name(cf->c.out_filter));
|
||||
cli_msg(-1006, " Channel %s", "main");
|
||||
cli_msg(-1006, " Table: %s", p->pri->table->name);
|
||||
cli_msg(-1006, " Peer table: %s", p->sec->table->name);
|
||||
cli_msg(-1006, " Import filter: %s", filter_name(p->sec->out_filter));
|
||||
cli_msg(-1006, " Export filter: %s", filter_name(p->pri->out_filter));
|
||||
|
||||
proto_show_limit(cf->c.in_limit, "Import limit:");
|
||||
proto_show_limit(cf->c.out_limit, "Export limit:");
|
||||
channel_show_limit(&p->pri->in_limit, "Import limit:");
|
||||
channel_show_limit(&p->sec->in_limit, "Export limit:");
|
||||
|
||||
if (P->proto_state != PS_DOWN)
|
||||
pipe_show_stats(p);
|
||||
|
@ -338,13 +273,10 @@ pipe_show_proto_info(struct proto *P)
|
|||
struct protocol proto_pipe = {
|
||||
.name = "Pipe",
|
||||
.template = "pipe%d",
|
||||
.multitable = 1,
|
||||
.preference = DEF_PREF_PIPE,
|
||||
.proto_size = sizeof(struct pipe_proto),
|
||||
.config_size = sizeof(struct pipe_config),
|
||||
.postconfig = pipe_postconfig,
|
||||
.init = pipe_init,
|
||||
.start = pipe_start,
|
||||
.cleanup = pipe_cleanup,
|
||||
.reconfigure = pipe_reconfigure,
|
||||
.copy_config = pipe_copy_config,
|
||||
.get_status = pipe_get_status,
|
||||
|
|
|
@ -9,27 +9,15 @@
|
|||
#ifndef _BIRD_PIPE_H_
|
||||
#define _BIRD_PIPE_H_
|
||||
|
||||
#define PIPE_OPAQUE 0
|
||||
#define PIPE_TRANSPARENT 1
|
||||
|
||||
struct pipe_config {
|
||||
struct proto_config c;
|
||||
struct rtable_config *peer; /* Table we're connected to */
|
||||
int mode; /* PIPE_OPAQUE or PIPE_TRANSPARENT */
|
||||
};
|
||||
|
||||
struct pipe_proto {
|
||||
struct proto p;
|
||||
struct rtable *peer_table;
|
||||
struct announce_hook *peer_ahook; /* Announce hook for direction peer->primary */
|
||||
struct proto_stats peer_stats; /* Statistics for the direction peer->primary */
|
||||
int mode; /* PIPE_OPAQUE or PIPE_TRANSPARENT */
|
||||
struct channel *pri;
|
||||
struct channel *sec;
|
||||
};
|
||||
|
||||
|
||||
extern struct protocol proto_pipe;
|
||||
|
||||
static inline int proto_is_pipe(struct proto *p)
|
||||
{ return p->proto == &proto_pipe; }
|
||||
|
||||
#endif
|
||||
|
|
|
@ -41,6 +41,7 @@ CF_ADDTO(proto, radv_proto)
|
|||
radv_proto_start: proto_start RADV
|
||||
{
|
||||
this_proto = proto_config_new(&proto_radv, $1);
|
||||
|
||||
init_list(&RADV_CFG->patt_list);
|
||||
init_list(&RADV_CFG->pref_list);
|
||||
init_list(&RADV_CFG->rdnss_list);
|
||||
|
@ -49,14 +50,12 @@ radv_proto_start: proto_start RADV
|
|||
|
||||
radv_proto_item:
|
||||
proto_item
|
||||
| proto_channel
|
||||
| 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); }
|
||||
| TRIGGER net_any {
|
||||
RADV_CFG->trigger = $2;
|
||||
RADV_CFG->trigger_valid = 1;
|
||||
}
|
||||
| TRIGGER net_ip6 { RADV_CFG->trigger = $2; }
|
||||
;
|
||||
|
||||
radv_proto_opts:
|
||||
|
|
|
@ -256,9 +256,16 @@ radv_ifa_notify(struct proto *p, unsigned flags, struct ifa *a)
|
|||
radv_iface_notify(ifa, RA_EV_CHANGE);
|
||||
}
|
||||
|
||||
static inline int radv_net_match_trigger(struct radv_config *cf, net *n)
|
||||
static inline int
|
||||
radv_trigger_valid(struct radv_config *cf)
|
||||
{
|
||||
return cf->trigger_valid && net_equal(n->n.addr, cf->trigger);
|
||||
return cf->trigger.type != 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
radv_net_match_trigger(struct radv_config *cf, net *n)
|
||||
{
|
||||
return radv_trigger_valid(cf) && net_equal(n->n.addr, &cf->trigger);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -301,22 +308,35 @@ radv_check_active(struct proto_radv *ra)
|
|||
{
|
||||
struct radv_config *cf = (struct radv_config *) (ra->p.cf);
|
||||
|
||||
if (! cf->trigger_valid)
|
||||
if (!radv_trigger_valid(cf))
|
||||
return 1;
|
||||
|
||||
return rt_examine(ra->p.table, cf->trigger, &ra->p, ra->p.cf->out_filter);
|
||||
struct channel *c =ra->p.main_channel;
|
||||
return rt_examine(c->table, &cf->trigger, &ra->p, c->out_filter);
|
||||
}
|
||||
|
||||
static void
|
||||
radv_postconfig(struct proto_config *CF)
|
||||
{
|
||||
// struct radv_config *cf = (void *) CF;
|
||||
|
||||
/* Define default channel */
|
||||
if (EMPTY_LIST(CF->channels))
|
||||
channel_config_new(NULL, NET_IP6, CF);
|
||||
}
|
||||
|
||||
static struct proto *
|
||||
radv_init(struct proto_config *c)
|
||||
radv_init(struct proto_config *CF)
|
||||
{
|
||||
struct proto *p = proto_new(c, sizeof(struct proto_radv));
|
||||
struct proto *p = proto_new(CF);
|
||||
|
||||
p->main_channel = proto_add_channel(p, proto_cf_main_channel(CF));
|
||||
|
||||
p->accept_ra_types = RA_OPTIMAL;
|
||||
p->import_control = radv_import_control;
|
||||
p->rt_notify = radv_rt_notify;
|
||||
p->if_notify = radv_if_notify;
|
||||
p->ifa_notify = radv_ifa_notify;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -327,7 +347,7 @@ radv_start(struct proto *p)
|
|||
struct radv_config *cf = (struct radv_config *) (p->cf);
|
||||
|
||||
init_list(&(ra->iface_list));
|
||||
ra->active = !cf->trigger_valid;
|
||||
ra->active = !radv_trigger_valid(cf);
|
||||
|
||||
return PS_UP;
|
||||
}
|
||||
|
@ -352,11 +372,11 @@ radv_shutdown(struct proto *p)
|
|||
}
|
||||
|
||||
static int
|
||||
radv_reconfigure(struct proto *p, struct proto_config *c)
|
||||
radv_reconfigure(struct proto *p, struct proto_config *CF)
|
||||
{
|
||||
struct proto_radv *ra = (struct proto_radv *) p;
|
||||
// struct radv_config *old = (struct radv_config *) (p->cf);
|
||||
struct radv_config *new = (struct radv_config *) c;
|
||||
struct radv_config *new = (struct radv_config *) CF;
|
||||
|
||||
/*
|
||||
* The question is why there is a reconfigure function for RAdv if
|
||||
|
@ -366,7 +386,10 @@ radv_reconfigure(struct proto *p, struct proto_config *c)
|
|||
* causing nodes to temporary remove their default routes.
|
||||
*/
|
||||
|
||||
p->cf = c; /* radv_check_active() requires proper p->cf */
|
||||
if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF)))
|
||||
return 0;
|
||||
|
||||
p->cf = CF; /* radv_check_active() requires proper p->cf */
|
||||
ra->active = radv_check_active(ra);
|
||||
|
||||
struct iface *iface;
|
||||
|
@ -423,7 +446,10 @@ radv_get_status(struct proto *p, byte *buf)
|
|||
struct protocol proto_radv = {
|
||||
.name = "RAdv",
|
||||
.template = "radv%d",
|
||||
.channel_mask = NB_IP6,
|
||||
.proto_size = sizeof(struct proto_radv),
|
||||
.config_size = sizeof(struct radv_config),
|
||||
.postconfig = radv_postconfig,
|
||||
.init = radv_init,
|
||||
.start = radv_start,
|
||||
.shutdown = radv_shutdown,
|
||||
|
|
|
@ -50,8 +50,7 @@ struct radv_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) */
|
||||
|
||||
net_addr *trigger; /* Prefix of a trigger route, if defined */
|
||||
u8 trigger_valid; /* Whether a trigger route is defined */
|
||||
net_addr trigger; /* Prefix of a trigger route, if defined */
|
||||
};
|
||||
|
||||
struct radv_iface_config
|
||||
|
|
|
@ -52,17 +52,18 @@ rip_variant:
|
|||
rip_proto_start: proto_start rip_variant
|
||||
{
|
||||
this_proto = proto_config_new(&proto_rip, $1);
|
||||
init_list(&RIP_CFG->patt_list);
|
||||
this_proto->net_type = $2 ? NET_IP4 : NET_IP6;
|
||||
|
||||
init_list(&RIP_CFG->patt_list);
|
||||
RIP_CFG->rip2 = $2;
|
||||
RIP_CFG->infinity = RIP_DEFAULT_INFINITY;
|
||||
|
||||
RIP_CFG->min_timeout_time = 60;
|
||||
RIP_CFG->max_garbage_time = 60;
|
||||
};
|
||||
|
||||
rip_proto_item:
|
||||
proto_item
|
||||
| proto_channel
|
||||
| ECMP bool { RIP_CFG->ecmp = $2 ? RIP_DEFAULT_ECMP_LIMIT : 0; }
|
||||
| ECMP bool LIMIT expr { RIP_CFG->ecmp = $2 ? $4 : 0; if ($4 < 0) cf_error("ECMP limit cannot be negative"); }
|
||||
| INFINITY expr { RIP_CFG->infinity = $2; }
|
||||
|
|
|
@ -143,7 +143,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
|
|||
if (rt)
|
||||
{
|
||||
/* Update */
|
||||
net *n = net_get(p->p.table, en->n.addr);
|
||||
net *n = net_get(p->p.main_channel->table, en->n.addr);
|
||||
|
||||
rta a0 = {
|
||||
.src = p->p.main_source,
|
||||
|
@ -212,7 +212,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
|
|||
else
|
||||
{
|
||||
/* Withdraw */
|
||||
net *n = net_find(p->p.table, en->n.addr);
|
||||
net *n = net_find(p->p.main_channel->table, en->n.addr);
|
||||
rte_update(&p->p, n, NULL);
|
||||
}
|
||||
}
|
||||
|
@ -1027,19 +1027,17 @@ rip_import_control(struct proto *P, struct rte **rt, struct ea_list **attrs, str
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rip_reload_routes(struct proto *P)
|
||||
static void
|
||||
rip_reload_routes(struct channel *C)
|
||||
{
|
||||
struct rip_proto *p = (struct rip_proto *) P;
|
||||
struct rip_proto *p = (struct rip_proto *) C->proto;
|
||||
|
||||
if (p->rt_reload)
|
||||
return 1;
|
||||
return;
|
||||
|
||||
TRACE(D_EVENTS, "Scheduling route reload");
|
||||
p->rt_reload = 1;
|
||||
rip_kick_timer(p);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct ea_list *
|
||||
|
@ -1070,12 +1068,23 @@ rip_rte_same(struct rte *new, struct rte *old)
|
|||
}
|
||||
|
||||
|
||||
static struct proto *
|
||||
rip_init(struct proto_config *cfg)
|
||||
static void
|
||||
rip_postconfig(struct proto_config *CF)
|
||||
{
|
||||
struct proto *P = proto_new(cfg, sizeof(struct rip_proto));
|
||||
// struct rip_config *cf = (void *) CF;
|
||||
|
||||
/* Define default channel */
|
||||
if (EMPTY_LIST(CF->channels))
|
||||
channel_config_new(NULL, CF->net_type, CF);
|
||||
}
|
||||
|
||||
static struct proto *
|
||||
rip_init(struct proto_config *CF)
|
||||
{
|
||||
struct proto *P = proto_new(CF);
|
||||
|
||||
P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
|
||||
|
||||
P->accept_ra_types = RA_OPTIMAL;
|
||||
P->if_notify = rip_if_notify;
|
||||
P->rt_notify = rip_rt_notify;
|
||||
P->neigh_notify = rip_neigh_notify;
|
||||
|
@ -1115,10 +1124,10 @@ rip_start(struct proto *P)
|
|||
}
|
||||
|
||||
static int
|
||||
rip_reconfigure(struct proto *P, struct proto_config *c)
|
||||
rip_reconfigure(struct proto *P, struct proto_config *CF)
|
||||
{
|
||||
struct rip_proto *p = (void *) P;
|
||||
struct rip_config *new = (void *) c;
|
||||
struct rip_config *new = (void *) CF;
|
||||
// struct rip_config *old = (void *) (P->cf);
|
||||
|
||||
if (new->rip2 != p->rip2)
|
||||
|
@ -1127,9 +1136,12 @@ rip_reconfigure(struct proto *P, struct proto_config *c)
|
|||
if (new->infinity != p->infinity)
|
||||
return 0;
|
||||
|
||||
if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
|
||||
return 0;
|
||||
|
||||
TRACE(D_EVENTS, "Reconfiguring");
|
||||
|
||||
p->p.cf = c;
|
||||
p->p.cf = CF;
|
||||
p->ecmp = new->ecmp;
|
||||
rip_reconfigure_ifaces(p, new);
|
||||
|
||||
|
@ -1270,7 +1282,10 @@ struct protocol proto_rip = {
|
|||
.template = "rip%d",
|
||||
.attr_class = EAP_RIP,
|
||||
.preference = DEF_PREF_RIP,
|
||||
.channel_mask = NB_IP,
|
||||
.proto_size = sizeof(struct rip_proto),
|
||||
.config_size = sizeof(struct rip_config),
|
||||
.postconfig = rip_postconfig,
|
||||
.init = rip_init,
|
||||
.dump = rip_dump,
|
||||
.start = rip_start,
|
||||
|
|
|
@ -38,15 +38,16 @@ CF_GRAMMAR
|
|||
|
||||
CF_ADDTO(proto, static_proto '}')
|
||||
|
||||
static_proto_start: proto_start STATIC {
|
||||
this_proto = proto_config_new(&proto_static, $1);
|
||||
static_init_config((struct static_config *) this_proto);
|
||||
}
|
||||
;
|
||||
static_proto_start: proto_start STATIC
|
||||
{
|
||||
this_proto = proto_config_new(&proto_static, $1);
|
||||
static_init_config(STATIC_CFG);
|
||||
};
|
||||
|
||||
static_proto:
|
||||
static_proto_start proto_name '{'
|
||||
| static_proto proto_item ';'
|
||||
| static_proto proto_channel ';' { this_proto->net_type = $2->net_type; }
|
||||
| static_proto CHECK LINK bool ';' { STATIC_CFG->check_link = $4; }
|
||||
| static_proto IGP TABLE rtable ';' { STATIC_CFG->igp_table = $4; }
|
||||
| static_proto stat_route stat_route_opt_list ';' { static_route_finish(); }
|
||||
|
|
|
@ -54,7 +54,7 @@ static inline rtable *
|
|||
p_igp_table(struct proto *p)
|
||||
{
|
||||
struct static_config *cf = (void *) p->cf;
|
||||
return cf->igp_table ? cf->igp_table->table : p->table;
|
||||
return cf->igp_table ? cf->igp_table->table : p->main_channel->table;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -108,11 +108,11 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa)
|
|||
}
|
||||
|
||||
if (r->dest == RTDX_RECURSIVE)
|
||||
rta_set_recursive_next_hop(p->table, &a, p_igp_table(p), &r->via, &r->via);
|
||||
rta_set_recursive_next_hop(p->main_channel->table, &a, p_igp_table(p), &r->via, &r->via);
|
||||
|
||||
/* We skip rta_lookup() here */
|
||||
|
||||
n = net_get(p->table, r->net);
|
||||
n = net_get(p->main_channel->table, r->net);
|
||||
e = rte_get_temp(&a);
|
||||
e->net = n;
|
||||
e->pflags = 0;
|
||||
|
@ -136,7 +136,7 @@ static_remove(struct proto *p, struct static_route *r)
|
|||
return;
|
||||
|
||||
DBG("Removing static route %N via %I\n", r->net, r->via);
|
||||
n = net_find(p->table, r->net);
|
||||
n = net_find(p->main_channel->table, r->net);
|
||||
rte_update(p, n, NULL);
|
||||
r->installed = 0;
|
||||
}
|
||||
|
@ -309,6 +309,17 @@ static_shutdown(struct proto *p)
|
|||
r->installed = 0;
|
||||
}
|
||||
|
||||
/* Handle failure during channel reconfigure */
|
||||
/* FIXME: This should be handled in a better way */
|
||||
cf = (void *) p->cf_new;
|
||||
if (cf)
|
||||
{
|
||||
WALK_LIST(r, cf->iface_routes)
|
||||
r->installed = 0;
|
||||
WALK_LIST(r, cf->other_routes)
|
||||
r->installed = 0;
|
||||
}
|
||||
|
||||
return PS_DOWN;
|
||||
}
|
||||
|
||||
|
@ -450,16 +461,40 @@ static_init_config(struct static_config *c)
|
|||
init_list(&c->other_routes);
|
||||
}
|
||||
|
||||
static struct proto *
|
||||
static_init(struct proto_config *c)
|
||||
static void
|
||||
static_postconfig(struct proto_config *CF)
|
||||
{
|
||||
struct proto *p = proto_new(c, sizeof(struct proto));
|
||||
struct static_config *cf = (void *) CF;
|
||||
struct static_route *r;
|
||||
|
||||
p->neigh_notify = static_neigh_notify;
|
||||
p->if_notify = static_if_notify;
|
||||
p->rte_mergable = static_rte_mergable;
|
||||
if (EMPTY_LIST(CF->channels))
|
||||
cf_error("Channel not specified");
|
||||
|
||||
return p;
|
||||
|
||||
WALK_LIST(r, cf->iface_routes)
|
||||
if (r->net->type != CF->net_type)
|
||||
cf_error("Route %N incompatible with channel type", r->net);
|
||||
|
||||
WALK_LIST(r, cf->other_routes)
|
||||
if (r->net->type != CF->net_type)
|
||||
cf_error("Route %N incompatible with channel type", r->net);
|
||||
}
|
||||
|
||||
|
||||
static struct proto *
|
||||
static_init(struct proto_config *CF)
|
||||
{
|
||||
struct proto *P = proto_new(CF);
|
||||
// struct static_proto *p = (void *) P;
|
||||
// struct static_config *cf = (void *) CF;
|
||||
|
||||
P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
|
||||
|
||||
P->neigh_notify = static_neigh_notify;
|
||||
P->if_notify = static_if_notify;
|
||||
P->rte_mergable = static_rte_mergable;
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
static inline int
|
||||
|
@ -543,15 +578,18 @@ cf_igp_table(struct static_config *cf)
|
|||
}
|
||||
|
||||
static int
|
||||
static_reconfigure(struct proto *p, struct proto_config *new)
|
||||
static_reconfigure(struct proto *p, struct proto_config *CF)
|
||||
{
|
||||
struct static_config *o = (void *) p->cf;
|
||||
struct static_config *n = (void *) new;
|
||||
struct static_config *n = (void *) CF;
|
||||
struct static_route *r;
|
||||
|
||||
if (cf_igp_table(o) != cf_igp_table(n))
|
||||
return 0;
|
||||
|
||||
if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF)))
|
||||
return 0;
|
||||
|
||||
/* Delete all obsolete routes and reset neighbor entries */
|
||||
WALK_LIST(r, o->iface_routes)
|
||||
static_match(p, r, n);
|
||||
|
@ -617,20 +655,19 @@ static_copy_config(struct proto_config *dest, struct proto_config *src)
|
|||
struct static_config *d = (struct static_config *) dest;
|
||||
struct static_config *s = (struct static_config *) src;
|
||||
|
||||
/* Shallow copy of everything */
|
||||
proto_copy_rest(dest, src, sizeof(struct static_config));
|
||||
|
||||
/* Copy route lists */
|
||||
static_copy_routes(&d->iface_routes, &s->iface_routes);
|
||||
static_copy_routes(&d->other_routes, &s->other_routes);
|
||||
}
|
||||
|
||||
|
||||
struct protocol proto_static = {
|
||||
.name = "Static",
|
||||
.template = "static%d",
|
||||
.preference = DEF_PREF_STATIC,
|
||||
.channel_mask = NB_ANY,
|
||||
.proto_size = sizeof(struct proto),
|
||||
.config_size = sizeof(struct static_config),
|
||||
.postconfig = static_postconfig,
|
||||
.init = static_init,
|
||||
.dump = static_dump,
|
||||
.start = static_start,
|
||||
|
|
|
@ -1127,7 +1127,7 @@ nl_parse_route(struct nlmsghdr *h, int scan)
|
|||
src = KRT_SRC_ALIEN;
|
||||
}
|
||||
|
||||
net *net = net_get(p->p.table, &dst);
|
||||
net *net = net_get(p->p.main_channel->table, &dst);
|
||||
|
||||
rta ra = {
|
||||
.src= p->p.main_source,
|
||||
|
|
|
@ -15,6 +15,16 @@ CF_DEFINES
|
|||
#define THIS_KRT ((struct krt_config *) this_proto)
|
||||
#define THIS_KIF ((struct kif_config *) this_proto)
|
||||
|
||||
static void
|
||||
krt_set_merge_paths(struct channel_config *cc, uint merge, uint limit)
|
||||
{
|
||||
if ((limit <= 0) || (limit > 255))
|
||||
cf_error("Merge paths limit must be in range 1-255");
|
||||
|
||||
cc->ra_mode = merge ? RA_MERGED : RA_OPTIMAL;
|
||||
cc->merge_limit = limit;
|
||||
}
|
||||
|
||||
CF_DECLS
|
||||
|
||||
CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES, GRACEFUL, RESTART, KRT_SOURCE, KRT_METRIC, MERGE, PATHS)
|
||||
|
@ -25,15 +35,18 @@ CF_GRAMMAR
|
|||
|
||||
CF_ADDTO(proto, kern_proto '}')
|
||||
|
||||
kern_proto_start: proto_start KERNEL { this_proto = krt_init_config($1); }
|
||||
kern_proto_start: proto_start KERNEL {
|
||||
this_proto = krt_init_config($1);
|
||||
}
|
||||
;
|
||||
|
||||
CF_ADDTO(kern_proto, kern_proto_start proto_name '{')
|
||||
CF_ADDTO(kern_proto, kern_proto proto_item ';')
|
||||
CF_ADDTO(kern_proto, kern_proto kern_item ';')
|
||||
|
||||
kern_item:
|
||||
PERSIST bool { THIS_KRT->persist = $2; }
|
||||
proto_item
|
||||
| proto_channel { this_proto->net_type = $1->net_type; }
|
||||
| PERSIST bool { THIS_KRT->persist = $2; }
|
||||
| SCAN TIME expr {
|
||||
/* Scan time of 0 means scan on startup only */
|
||||
THIS_KRT->scan_time = $3;
|
||||
|
@ -47,8 +60,8 @@ kern_item:
|
|||
}
|
||||
| DEVICE ROUTES bool { THIS_KRT->devroutes = $3; }
|
||||
| GRACEFUL RESTART bool { THIS_KRT->graceful_restart = $3; }
|
||||
| MERGE PATHS bool { THIS_KRT->merge_paths = $3 ? KRT_DEFAULT_ECMP_LIMIT : 0; }
|
||||
| MERGE PATHS bool LIMIT expr { THIS_KRT->merge_paths = $3 ? $5 : 0; if (($5 <= 0) || ($5 > 255)) cf_error("Merge paths limit must be in range 1-255"); }
|
||||
| MERGE PATHS bool { krt_set_merge_paths(this_channel, $3, KRT_DEFAULT_ECMP_LIMIT); }
|
||||
| MERGE PATHS bool LIMIT expr { krt_set_merge_paths(this_channel, $3, $5); }
|
||||
;
|
||||
|
||||
/* Kernel interface protocol */
|
||||
|
@ -59,11 +72,11 @@ kif_proto_start: proto_start DEVICE { this_proto = kif_init_config($1); }
|
|||
;
|
||||
|
||||
CF_ADDTO(kif_proto, kif_proto_start proto_name '{')
|
||||
CF_ADDTO(kif_proto, kif_proto proto_item ';')
|
||||
CF_ADDTO(kif_proto, kif_proto kif_item ';')
|
||||
|
||||
kif_item:
|
||||
SCAN TIME expr {
|
||||
proto_item
|
||||
| SCAN TIME expr {
|
||||
/* Scan time of 0 means scan on startup only */
|
||||
THIS_KIF->scan_time = $3;
|
||||
}
|
||||
|
|
|
@ -170,7 +170,7 @@ kif_choose_primary(struct iface *i)
|
|||
static struct proto *
|
||||
kif_init(struct proto_config *c)
|
||||
{
|
||||
struct kif_proto *p = proto_new(c, sizeof(struct kif_proto));
|
||||
struct kif_proto *p = proto_new(c);
|
||||
|
||||
kif_sys_init(p);
|
||||
return &p->p;
|
||||
|
@ -266,9 +266,6 @@ kif_copy_config(struct proto_config *dest, struct proto_config *src)
|
|||
struct kif_config *d = (struct kif_config *) dest;
|
||||
struct kif_config *s = (struct kif_config *) src;
|
||||
|
||||
/* Shallow copy of everything (just scan_time currently) */
|
||||
proto_copy_rest(dest, src, sizeof(struct kif_config));
|
||||
|
||||
/* Copy primary addr list */
|
||||
cfg_copy_list(&d->primary, &s->primary, sizeof(struct kif_primary_item));
|
||||
|
||||
|
@ -280,7 +277,7 @@ kif_copy_config(struct proto_config *dest, struct proto_config *src)
|
|||
struct protocol proto_unix_iface = {
|
||||
.name = "Device",
|
||||
.template = "device%d",
|
||||
.preference = DEF_PREF_DIRECT,
|
||||
.proto_size = sizeof(struct kif_proto),
|
||||
.config_size = sizeof(struct kif_config),
|
||||
.preconfig = kif_preconfig,
|
||||
.init = kif_init,
|
||||
|
@ -348,10 +345,9 @@ krt_learn_announce_update(struct krt_proto *p, rte *e)
|
|||
net *n = e->net;
|
||||
rta *aa = rta_clone(e->attrs);
|
||||
rte *ee = rte_get_temp(aa);
|
||||
net *nn = net_get(p->p.table, n->n.addr);
|
||||
net *nn = net_get(p->p.main_channel->table, n->n.addr);
|
||||
ee->net = nn;
|
||||
ee->pflags = 0;
|
||||
ee->pref = p->p.preference;
|
||||
ee->u.krt = e->u.krt;
|
||||
rte_update(&p->p, nn, ee);
|
||||
}
|
||||
|
@ -359,7 +355,7 @@ krt_learn_announce_update(struct krt_proto *p, rte *e)
|
|||
static void
|
||||
krt_learn_announce_delete(struct krt_proto *p, net *n)
|
||||
{
|
||||
n = net_find(p->p.table, n->n.addr);
|
||||
n = net_find(p->p.main_channel->table, n->n.addr);
|
||||
rte_update(&p->p, n, NULL);
|
||||
}
|
||||
|
||||
|
@ -575,7 +571,7 @@ krt_dump_attrs(rte *e)
|
|||
static void
|
||||
krt_flush_routes(struct krt_proto *p)
|
||||
{
|
||||
struct rtable *t = p->p.table;
|
||||
struct rtable *t = p->p.main_channel->table;
|
||||
|
||||
KRT_TRACE(p, D_EVENTS, "Flushing kernel routes");
|
||||
FIB_WALK(&t->fib, net, n)
|
||||
|
@ -594,12 +590,12 @@ krt_flush_routes(struct krt_proto *p)
|
|||
static struct rte *
|
||||
krt_export_net(struct krt_proto *p, net *net, rte **rt_free, ea_list **tmpa)
|
||||
{
|
||||
struct announce_hook *ah = p->p.main_ahook;
|
||||
struct filter *filter = ah->out_filter;
|
||||
struct channel *c = p->p.main_channel;
|
||||
struct filter *filter = c->out_filter;
|
||||
rte *rt;
|
||||
|
||||
if (p->p.accept_ra_types == RA_MERGED)
|
||||
return rt_export_merged(ah, net, rt_free, tmpa, 1);
|
||||
if (c->ra_mode == RA_MERGED)
|
||||
return rt_export_merged(c, net, rt_free, tmpa, 1);
|
||||
|
||||
rt = net->routes;
|
||||
*rt_free = NULL;
|
||||
|
@ -746,7 +742,7 @@ krt_got_route(struct krt_proto *p, rte *e)
|
|||
static void
|
||||
krt_prune(struct krt_proto *p)
|
||||
{
|
||||
struct rtable *t = p->p.table;
|
||||
struct rtable *t = p->p.main_channel->table;
|
||||
|
||||
KRT_TRACE(p, D_EVENTS, "Pruning table %s", t->name);
|
||||
FIB_WALK(&t->fib, net, n)
|
||||
|
@ -1052,10 +1048,10 @@ krt_if_notify(struct proto *P, uint flags, struct iface *iface UNUSED)
|
|||
krt_scan_timer_kick(p);
|
||||
}
|
||||
|
||||
static int
|
||||
krt_reload_routes(struct proto *P)
|
||||
static void
|
||||
krt_reload_routes(struct channel *C)
|
||||
{
|
||||
struct krt_proto *p = (struct krt_proto *) P;
|
||||
struct krt_proto *p = (void *) C->proto;
|
||||
|
||||
/* Although we keep learned routes in krt_table, we rather schedule a scan */
|
||||
|
||||
|
@ -1064,14 +1060,12 @@ krt_reload_routes(struct proto *P)
|
|||
p->reload = 1;
|
||||
krt_scan_timer_kick(p);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
krt_feed_end(struct proto *P)
|
||||
krt_feed_end(struct channel *C)
|
||||
{
|
||||
struct krt_proto *p = (struct krt_proto *) P;
|
||||
struct krt_proto *p = (void *) C->proto;
|
||||
|
||||
p->ready = 1;
|
||||
krt_scan_timer_kick(p);
|
||||
|
@ -1092,14 +1086,42 @@ krt_rte_same(rte *a, rte *b)
|
|||
|
||||
struct krt_config *krt_cf;
|
||||
|
||||
static struct proto *
|
||||
krt_init(struct proto_config *C)
|
||||
static void
|
||||
krt_preconfig(struct protocol *P UNUSED, struct config *c)
|
||||
{
|
||||
struct krt_proto *p = proto_new(C, sizeof(struct krt_proto));
|
||||
struct krt_config *c = (struct krt_config *) C;
|
||||
krt_cf = NULL;
|
||||
krt_sys_preconfig(c);
|
||||
}
|
||||
|
||||
static void
|
||||
krt_postconfig(struct proto_config *CF)
|
||||
{
|
||||
struct krt_config *cf = (void *) CF;
|
||||
|
||||
if (EMPTY_LIST(CF->channels))
|
||||
cf_error("Channel not specified");
|
||||
|
||||
#ifdef CONFIG_ALL_TABLES_AT_ONCE
|
||||
if (krt_cf->scan_time != cf->scan_time)
|
||||
cf_error("All kernel syncers must use the same table scan interval");
|
||||
#endif
|
||||
|
||||
struct rtable_config *tab = proto_cf_main_channel(CF)->table;
|
||||
if (tab->krt_attached)
|
||||
cf_error("Kernel syncer (%s) already attached to table %s", tab->krt_attached->name, tab->name);
|
||||
tab->krt_attached = CF;
|
||||
|
||||
krt_sys_postconfig(cf);
|
||||
}
|
||||
|
||||
static struct proto *
|
||||
krt_init(struct proto_config *CF)
|
||||
{
|
||||
struct krt_proto *p = proto_new(CF);
|
||||
// struct krt_config *cf = (void *) CF;
|
||||
|
||||
p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(CF));
|
||||
|
||||
p->p.accept_ra_types = c->merge_paths ? RA_MERGED : RA_OPTIMAL;
|
||||
p->p.merge_limit = c->merge_paths;
|
||||
p->p.import_control = krt_import_control;
|
||||
p->p.rt_notify = krt_rt_notify;
|
||||
p->p.if_notify = krt_if_notify;
|
||||
|
@ -1118,7 +1140,7 @@ krt_start(struct proto *P)
|
|||
{
|
||||
struct krt_proto *p = (struct krt_proto *) P;
|
||||
|
||||
switch (p->p.table->addr_type)
|
||||
switch (p->p.net_type)
|
||||
{
|
||||
case NET_IP4: p->af = AF_INET; break;
|
||||
case NET_IP6: p->af = AF_INET6; break;
|
||||
|
@ -1139,8 +1161,8 @@ krt_start(struct proto *P)
|
|||
|
||||
krt_scan_timer_start(p);
|
||||
|
||||
if (P->gr_recovery && KRT_CF->graceful_restart)
|
||||
P->gr_wait = 1;
|
||||
if (p->p.gr_recovery && KRT_CF->graceful_restart)
|
||||
p->p.main_channel->gr_wait = 1;
|
||||
|
||||
return PS_UP;
|
||||
}
|
||||
|
@ -1169,40 +1191,19 @@ krt_shutdown(struct proto *P)
|
|||
}
|
||||
|
||||
static int
|
||||
krt_reconfigure(struct proto *p, struct proto_config *new)
|
||||
krt_reconfigure(struct proto *p, struct proto_config *CF)
|
||||
{
|
||||
struct krt_config *o = (struct krt_config *) p->cf;
|
||||
struct krt_config *n = (struct krt_config *) new;
|
||||
struct krt_config *o = (void *) p->cf;
|
||||
struct krt_config *n = (void *) CF;
|
||||
|
||||
if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF)))
|
||||
return 0;
|
||||
|
||||
if (!krt_sys_reconfigure((struct krt_proto *) p, n, o))
|
||||
return 0;
|
||||
|
||||
/* persist, graceful restart need not be the same */
|
||||
return o->scan_time == n->scan_time && o->learn == n->learn &&
|
||||
o->devroutes == n->devroutes && o->merge_paths == n->merge_paths;
|
||||
}
|
||||
|
||||
static void
|
||||
krt_preconfig(struct protocol *P UNUSED, struct config *c)
|
||||
{
|
||||
krt_cf = NULL;
|
||||
krt_sys_preconfig(c);
|
||||
}
|
||||
|
||||
static void
|
||||
krt_postconfig(struct proto_config *C)
|
||||
{
|
||||
struct krt_config *c = (struct krt_config *) C;
|
||||
|
||||
#ifdef CONFIG_ALL_TABLES_AT_ONCE
|
||||
if (krt_cf->scan_time != c->scan_time)
|
||||
cf_error("All kernel syncers must use the same table scan interval");
|
||||
#endif
|
||||
|
||||
if (C->table->krt_attached)
|
||||
cf_error("Kernel syncer (%s) already attached to table %s", C->table->krt_attached->name, C->table->name);
|
||||
C->table->krt_attached = C;
|
||||
krt_sys_postconfig(c);
|
||||
return o->scan_time == n->scan_time && o->learn == n->learn && o->devroutes == n->devroutes;
|
||||
}
|
||||
|
||||
struct proto_config *
|
||||
|
@ -1226,9 +1227,6 @@ krt_copy_config(struct proto_config *dest, struct proto_config *src)
|
|||
struct krt_config *d = (struct krt_config *) dest;
|
||||
struct krt_config *s = (struct krt_config *) src;
|
||||
|
||||
/* Shallow copy of everything */
|
||||
proto_copy_rest(dest, src, sizeof(struct krt_config));
|
||||
|
||||
/* Fix sysdep parts */
|
||||
krt_sys_copy_config(d, s);
|
||||
}
|
||||
|
@ -1257,6 +1255,8 @@ struct protocol proto_unix_kernel = {
|
|||
.template = "kernel%d",
|
||||
.attr_class = EAP_KRT,
|
||||
.preference = DEF_PREF_INHERITED,
|
||||
.channel_mask = NB_IP,
|
||||
.proto_size = sizeof(struct krt_proto),
|
||||
.config_size = sizeof(struct krt_config),
|
||||
.preconfig = krt_preconfig,
|
||||
.postconfig = krt_postconfig,
|
||||
|
|
|
@ -49,7 +49,6 @@ struct krt_config {
|
|||
int learn; /* Learn routes from other sources */
|
||||
int devroutes; /* Allow export of device routes */
|
||||
int graceful_restart; /* Regard graceful restart recovery */
|
||||
int merge_paths; /* Exported routes are merged for ECMP */
|
||||
};
|
||||
|
||||
struct krt_proto {
|
||||
|
|
Loading…
Reference in a new issue