mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2025-01-11 03:21:53 +00:00
Merge commit 'c93c02088a026b83f452fbd260135ba4c8da7ecf' into integrated
This commit is contained in:
commit
c570200d39
6
NEWS
6
NEWS
@ -1,3 +1,9 @@
|
|||||||
|
Version 1.3.9 (2012-11-16)
|
||||||
|
o BIRD can be configured to keep and show filtered routes.
|
||||||
|
o Dragonfly BSD support.
|
||||||
|
o Fixed OSPFv3 vlinks.
|
||||||
|
o Several minor bugfixes.
|
||||||
|
|
||||||
Version 1.3.8 (2012-08-07)
|
Version 1.3.8 (2012-08-07)
|
||||||
o Generalized import and export route limits.
|
o Generalized import and export route limits.
|
||||||
o RDNSS and DNSSL support for RAdv.
|
o RDNSS and DNSSL support for RAdv.
|
||||||
|
@ -459,6 +459,14 @@ to zero to disable it. An empty <cf><m/switch/</cf> is equivalent to <cf/on/
|
|||||||
works in the direction from the routing table to the protocol.
|
works in the direction from the routing table to the protocol.
|
||||||
Default: <cf/none/.
|
Default: <cf/none/.
|
||||||
|
|
||||||
|
<tag>import keep filtered <m/bool/</tag>
|
||||||
|
Usually, if an import filter rejects a route, the route is
|
||||||
|
forgotten. When this option is active, these routes are
|
||||||
|
kept in the routing table, but they are hidden and not
|
||||||
|
propagated to other protocols. But it is possible to show them
|
||||||
|
using <cf/show route filtered/. Note that this option does not
|
||||||
|
work for the pipe protocol. Default: off.
|
||||||
|
|
||||||
<tag>import limit <m/number/ [action warn | block | restart | disable]</tag>
|
<tag>import limit <m/number/ [action warn | block | restart | disable]</tag>
|
||||||
Specify an import route limit (a maximum number of routes
|
Specify an import route limit (a maximum number of routes
|
||||||
imported from the protocol) and optionally the action to be
|
imported from the protocol) and optionally the action to be
|
||||||
@ -467,8 +475,11 @@ to zero to disable it. An empty <cf><m/switch/</cf> is equivalent to <cf/on/
|
|||||||
protocol. Restart and disable actions shut the protocol down
|
protocol. Restart and disable actions shut the protocol down
|
||||||
like appropriate commands. Disable is the default action if an
|
like appropriate commands. Disable is the default action if an
|
||||||
action is not explicitly specified. Note that limits are reset
|
action is not explicitly specified. Note that limits are reset
|
||||||
during protocol reconfigure, reload or restart.
|
during protocol reconfigure, reload or restart. Also note that
|
||||||
Default: <cf/none/.
|
if <cf/import keep filtered/ is active, filtered routes are
|
||||||
|
counted towards the limit and blocked routes are forgotten, as
|
||||||
|
the main purpose of the import limit is to protect routing
|
||||||
|
tables from overflow. Default: <cf/none/.
|
||||||
|
|
||||||
<tag>export limit <m/number/ [action warn | block | restart | disable]</tag>
|
<tag>export limit <m/number/ [action warn | block | restart | disable]</tag>
|
||||||
Specify an export route limit, works similarly to
|
Specify an export route limit, works similarly to
|
||||||
@ -661,6 +672,9 @@ This argument can be omitted if there exists only a single instance.
|
|||||||
<p>You can also select just routes added by a specific protocol.
|
<p>You can also select just routes added by a specific protocol.
|
||||||
<cf>protocol <m/p/</cf>.
|
<cf>protocol <m/p/</cf>.
|
||||||
|
|
||||||
|
<p>If BIRD is configured to keep filtered routes (see </cf/import keep filtered/
|
||||||
|
option), you can show them instead of routes by using </cf/filtered/ switch.
|
||||||
|
|
||||||
<p>The <cf/stats/ switch requests showing of route statistics (the
|
<p>The <cf/stats/ switch requests showing of route statistics (the
|
||||||
number of networks, number of routes before and after filtering). If
|
number of networks, number of routes before and after filtering). If
|
||||||
you use <cf/count/ instead, only the statistics will be printed.
|
you use <cf/count/ instead, only the statistics will be printed.
|
||||||
@ -1640,6 +1654,15 @@ use cases that use the direct protocol (like abusing eBGP as an IGP
|
|||||||
routing protocol), in most cases it is not needed to have these device
|
routing protocol), in most cases it is not needed to have these device
|
||||||
routes in BIRD routing table and to use the direct protocol.
|
routes in BIRD routing table and to use the direct protocol.
|
||||||
|
|
||||||
|
<p>There is one notable case when you definitely want to use the
|
||||||
|
direct protocol -- running BIRD on BSD systems. Having high priority
|
||||||
|
device routes for directly connected networks from the direct protocol
|
||||||
|
protects kernel device routes from being overwritten or removed by IGP
|
||||||
|
routes during some transient network conditions, because a lower
|
||||||
|
priority IGP route for the same network is not exported to the kernel
|
||||||
|
routing table. This is an issue on BSD systems only, as on Linux
|
||||||
|
systems BIRD cannot change non-BIRD route in the kernel routing table.
|
||||||
|
|
||||||
<p>The only configurable thing about direct is what interfaces it watches:
|
<p>The only configurable thing about direct is what interfaces it watches:
|
||||||
|
|
||||||
<p><descrip>
|
<p><descrip>
|
||||||
@ -2460,13 +2483,13 @@ interface definitions, prefix definitions and DNS definitions:
|
|||||||
router. 0 means do not use as a default router. Default: 3 *
|
router. 0 means do not use as a default router. Default: 3 *
|
||||||
<cf/max ra interval/.
|
<cf/max ra interval/.
|
||||||
|
|
||||||
<tag>rdnss local <m/bool/</tag>
|
<tag>rdnss local <m/switch/</tag>
|
||||||
Use only local (interface-specific) RDNSS definitions for this
|
Use only local (interface-specific) RDNSS definitions for this
|
||||||
interface. Otherwise, both global and local definitions are
|
interface. Otherwise, both global and local definitions are
|
||||||
used. Could also be used to disable RDNSS for given interface
|
used. Could also be used to disable RDNSS for given interface
|
||||||
if no local definitons are specified. Default: no.
|
if no local definitons are specified. Default: no.
|
||||||
|
|
||||||
<tag>dnssl local <m/bool/</tag>
|
<tag>dnssl local <m/switch/</tag>
|
||||||
Use only local DNSSL definitions for this interface. See
|
Use only local DNSSL definitions for this interface. See
|
||||||
<cf/rdnss local/ option above. Default: no.
|
<cf/rdnss local/ option above. Default: no.
|
||||||
</descrip>
|
</descrip>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
Summary: BIRD Internet Routing Daemon
|
Summary: BIRD Internet Routing Daemon
|
||||||
Name: bird
|
Name: bird
|
||||||
Version: 1.3.8
|
Version: 1.3.9
|
||||||
Release: 1
|
Release: 1
|
||||||
Copyright: GPL
|
Copyright: GPL
|
||||||
Group: Networking/Daemons
|
Group: Networking/Daemons
|
||||||
|
@ -45,7 +45,7 @@ CF_DECLS
|
|||||||
CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
|
CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
|
||||||
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS)
|
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS)
|
||||||
CF_KEYWORDS(IPV4, IPVX, VPN4, VPN6, MPLS)
|
CF_KEYWORDS(IPV4, IPVX, VPN4, VPN6, MPLS)
|
||||||
CF_KEYWORDS(LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE)
|
CF_KEYWORDS(LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
|
||||||
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
|
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
|
||||||
CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, AS, MAX, FLUSH)
|
CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, AS, MAX, FLUSH)
|
||||||
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
|
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
|
||||||
@ -190,6 +190,7 @@ proto_item:
|
|||||||
| EXPORT imexport { this_proto->out_filter = $2; }
|
| EXPORT imexport { this_proto->out_filter = $2; }
|
||||||
| IMPORT LIMIT limit_spec { this_proto->in_limit = $3; }
|
| IMPORT LIMIT limit_spec { this_proto->in_limit = $3; }
|
||||||
| EXPORT LIMIT limit_spec { this_proto->out_limit = $3; }
|
| EXPORT LIMIT limit_spec { this_proto->out_limit = $3; }
|
||||||
|
| IMPORT KEEP FILTERED bool { this_proto->in_keep_filtered = $4; }
|
||||||
| TABLE rtable {
|
| TABLE rtable {
|
||||||
if (!rt_match($2->addr_type, this_proto->protocol->tables))
|
if (!rt_match($2->addr_type, this_proto->protocol->tables))
|
||||||
cf_error("Incompatible table class");
|
cf_error("Incompatible table class");
|
||||||
@ -412,7 +413,7 @@ CF_CLI(SHOW INTERFACES SUMMARY,,, [[Show summary of network interfaces]])
|
|||||||
{ if_show_summary(); } ;
|
{ if_show_summary(); } ;
|
||||||
|
|
||||||
CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]])
|
CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]])
|
||||||
CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filter <f>|where <cond>] [all] [primary] [(export|preexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]])
|
CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filter <f>|where <cond>] [all] [primary] [filtered] [(export|preexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]])
|
||||||
{ rt_show($3); } ;
|
{ rt_show($3); } ;
|
||||||
|
|
||||||
r_args:
|
r_args:
|
||||||
@ -458,6 +459,10 @@ r_args:
|
|||||||
$$ = $1;
|
$$ = $1;
|
||||||
$$->primary_only = 1;
|
$$->primary_only = 1;
|
||||||
}
|
}
|
||||||
|
| r_args FILTERED {
|
||||||
|
$$ = $1;
|
||||||
|
$$->filtered = 1;
|
||||||
|
}
|
||||||
| r_args export_or_preexport SYM {
|
| r_args export_or_preexport SYM {
|
||||||
struct proto_config *c = (struct proto_config *) $3->def;
|
struct proto_config *c = (struct proto_config *) $3->def;
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
|
20
nest/proto.c
20
nest/proto.c
@ -414,6 +414,7 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
|
|||||||
p->main_ahook->out_filter = nc->out_filter;
|
p->main_ahook->out_filter = nc->out_filter;
|
||||||
p->main_ahook->in_limit = nc->in_limit;
|
p->main_ahook->in_limit = nc->in_limit;
|
||||||
p->main_ahook->out_limit = nc->out_limit;
|
p->main_ahook->out_limit = nc->out_limit;
|
||||||
|
p->main_ahook->in_keep_filtered = nc->in_keep_filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update routes when filters changed. If the protocol in not UP,
|
/* Update routes when filters changed. If the protocol in not UP,
|
||||||
@ -720,8 +721,9 @@ proto_fell_down(struct proto *p)
|
|||||||
{
|
{
|
||||||
DBG("Protocol %s down\n", p->name);
|
DBG("Protocol %s down\n", p->name);
|
||||||
|
|
||||||
if (p->stats.imp_routes != 0)
|
u32 all_routes = p->stats.imp_routes + p->stats.filt_routes;
|
||||||
log(L_ERR "Protocol %s is down but still has %d routes", p->name, p->stats.imp_routes);
|
if (all_routes != 0)
|
||||||
|
log(L_ERR "Protocol %s is down but still has %d routes", p->name, all_routes);
|
||||||
|
|
||||||
bzero(&p->stats, sizeof(struct proto_stats));
|
bzero(&p->stats, sizeof(struct proto_stats));
|
||||||
proto_free_ahooks(p);
|
proto_free_ahooks(p);
|
||||||
@ -797,6 +799,7 @@ proto_schedule_feed(struct proto *p, int initial)
|
|||||||
p->main_ahook->out_filter = p->cf->out_filter;
|
p->main_ahook->out_filter = p->cf->out_filter;
|
||||||
p->main_ahook->in_limit = p->cf->in_limit;
|
p->main_ahook->in_limit = p->cf->in_limit;
|
||||||
p->main_ahook->out_limit = p->cf->out_limit;
|
p->main_ahook->out_limit = p->cf->out_limit;
|
||||||
|
p->main_ahook->in_keep_filtered = p->cf->in_keep_filtered;
|
||||||
proto_reset_limit(p->main_ahook->in_limit);
|
proto_reset_limit(p->main_ahook->in_limit);
|
||||||
proto_reset_limit(p->main_ahook->out_limit);
|
proto_reset_limit(p->main_ahook->out_limit);
|
||||||
}
|
}
|
||||||
@ -1094,10 +1097,15 @@ proto_state_name(struct proto *p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
proto_show_stats(struct proto_stats *s)
|
proto_show_stats(struct proto_stats *s, int in_keep_filtered)
|
||||||
{
|
{
|
||||||
cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
|
if (in_keep_filtered)
|
||||||
s->imp_routes, s->exp_routes, s->pref_routes);
|
cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred",
|
||||||
|
s->imp_routes, s->filt_routes, s->exp_routes, s->pref_routes);
|
||||||
|
else
|
||||||
|
cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
|
||||||
|
s->imp_routes, s->exp_routes, s->pref_routes);
|
||||||
|
|
||||||
cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
|
cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
|
||||||
cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
|
cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
|
||||||
s->imp_updates_received, s->imp_updates_invalid,
|
s->imp_updates_received, s->imp_updates_invalid,
|
||||||
@ -1135,7 +1143,7 @@ proto_show_basic_info(struct proto *p)
|
|||||||
proto_show_limit(p->cf->out_limit, "Export limit:");
|
proto_show_limit(p->cf->out_limit, "Export limit:");
|
||||||
|
|
||||||
if (p->proto_state != PS_DOWN)
|
if (p->proto_state != PS_DOWN)
|
||||||
proto_show_stats(&p->stats);
|
proto_show_stats(&p->stats, p->cf->in_keep_filtered);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -92,6 +92,7 @@ struct proto_config {
|
|||||||
int class; /* SYM_PROTO or SYM_TEMPLATE */
|
int class; /* SYM_PROTO or SYM_TEMPLATE */
|
||||||
u32 debug, mrtdump; /* Debugging bitfields, both use D_* constants */
|
u32 debug, mrtdump; /* Debugging bitfields, both use D_* constants */
|
||||||
unsigned preference, disabled; /* Generic parameters */
|
unsigned preference, disabled; /* Generic parameters */
|
||||||
|
int in_keep_filtered; /* Routes rejected in import filter are kept */
|
||||||
u32 router_id; /* Protocol specific router ID */
|
u32 router_id; /* Protocol specific router ID */
|
||||||
struct rtable_config *table; /* Table we're attached to */
|
struct rtable_config *table; /* Table we're attached to */
|
||||||
struct filter *in_filter, *out_filter; /* Attached filters */
|
struct filter *in_filter, *out_filter; /* Attached filters */
|
||||||
@ -107,7 +108,8 @@ struct proto_config {
|
|||||||
struct proto_stats {
|
struct proto_stats {
|
||||||
/* Import - from protocol to core */
|
/* Import - from protocol to core */
|
||||||
u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */
|
u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */
|
||||||
u32 pref_routes; /* Number of routes that are preferred, sum over all 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_received; /* Number of route updates received */
|
||||||
u32 imp_updates_invalid; /* Number of route updates rejected as invalid */
|
u32 imp_updates_invalid; /* Number of route updates rejected as invalid */
|
||||||
u32 imp_updates_filtered; /* Number of route updates rejected by filters */
|
u32 imp_updates_filtered; /* Number of route updates rejected by filters */
|
||||||
@ -411,6 +413,7 @@ struct announce_hook {
|
|||||||
struct proto_limit *out_limit; /* Output limit */
|
struct proto_limit *out_limit; /* Output limit */
|
||||||
struct proto_stats *stats; /* Per-table protocol statistics */
|
struct proto_stats *stats; /* Per-table protocol statistics */
|
||||||
struct announce_hook *next; /* Next hook for the same protocol */
|
struct announce_hook *next; /* Next hook for the same protocol */
|
||||||
|
int in_keep_filtered; /* Routes rejected in import filter are kept */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct announce_hook *proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats);
|
struct announce_hook *proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats);
|
||||||
|
10
nest/route.h
10
nest/route.h
@ -241,6 +241,14 @@ typedef struct rte {
|
|||||||
} rte;
|
} rte;
|
||||||
|
|
||||||
#define REF_COW 1 /* Copy this rte on write */
|
#define REF_COW 1 /* Copy this rte on write */
|
||||||
|
#define REF_FILTERED 2 /* Route is rejected by import filter */
|
||||||
|
|
||||||
|
/* Route is valid for propagation (may depend on other flags in the future), accepts NULL */
|
||||||
|
static inline int rte_is_valid(rte *r) { return r && !(r->flags & REF_FILTERED); }
|
||||||
|
|
||||||
|
/* Route just has REF_FILTERED flag */
|
||||||
|
static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED); }
|
||||||
|
|
||||||
|
|
||||||
/* Types of routing tables/entries */
|
/* Types of routing tables/entries */
|
||||||
#define RT_IPV4 1
|
#define RT_IPV4 1
|
||||||
@ -308,7 +316,7 @@ struct rt_show_data {
|
|||||||
struct fib_iterator fit;
|
struct fib_iterator fit;
|
||||||
struct proto *show_protocol;
|
struct proto *show_protocol;
|
||||||
struct proto *export_protocol;
|
struct proto *export_protocol;
|
||||||
int export_mode, primary_only;
|
int export_mode, primary_only, filtered;
|
||||||
struct config *running_on_config;
|
struct config *running_on_config;
|
||||||
int net_counter, rt_counter, show_counter;
|
int net_counter, rt_counter, show_counter;
|
||||||
int stats, show_for;
|
int stats, show_for;
|
||||||
|
160
nest/rt-table.c
160
nest/rt-table.c
@ -72,7 +72,7 @@ net_route(rtable *tab, ip_addr a, int len)
|
|||||||
{
|
{
|
||||||
a0 = ipa_and(a, ipa_mkmask(len));
|
a0 = ipa_and(a, ipa_mkmask(len));
|
||||||
n = fib_find(&tab->fib, &a0, len);
|
n = fib_find(&tab->fib, &a0, len);
|
||||||
if (n && n->routes)
|
if (n && rte_is_valid(n->routes))
|
||||||
return n;
|
return n;
|
||||||
len--;
|
len--;
|
||||||
}
|
}
|
||||||
@ -142,8 +142,11 @@ rte_better(rte *new, rte *old)
|
|||||||
{
|
{
|
||||||
int (*better)(rte *, rte *);
|
int (*better)(rte *, rte *);
|
||||||
|
|
||||||
if (!old)
|
if (!rte_is_valid(old))
|
||||||
return 1;
|
return 1;
|
||||||
|
if (!rte_is_valid(new))
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (new->pref > old->pref)
|
if (new->pref > old->pref)
|
||||||
return 1;
|
return 1;
|
||||||
if (new->pref < old->pref)
|
if (new->pref < old->pref)
|
||||||
@ -402,9 +405,13 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
|
|||||||
rte *old_free = NULL;
|
rte *old_free = NULL;
|
||||||
rte *r;
|
rte *r;
|
||||||
|
|
||||||
/* Used to track whether we met old_changed position. If it is NULL
|
/* Used to track whether we met old_changed position. If before_old is NULL
|
||||||
it was the first and met it implicitly before current best route. */
|
old_changed was the first and we met it implicitly before current best route. */
|
||||||
int old_meet = (old_changed && !before_old) ? 1 : 0;
|
int old_meet = old_changed && !before_old;
|
||||||
|
|
||||||
|
/* Note that before_old is either NULL or valid (not rejected) route.
|
||||||
|
If old_changed is valid, before_old have to be too. If old changed route
|
||||||
|
was not valid, caller must use NULL for both old_changed and before_old. */
|
||||||
|
|
||||||
if (new_changed)
|
if (new_changed)
|
||||||
stats->exp_updates_received++;
|
stats->exp_updates_received++;
|
||||||
@ -412,7 +419,7 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
|
|||||||
stats->exp_withdraws_received++;
|
stats->exp_withdraws_received++;
|
||||||
|
|
||||||
/* First, find the new_best route - first accepted by filters */
|
/* First, find the new_best route - first accepted by filters */
|
||||||
for (r=net->routes; r; r=r->next)
|
for (r=net->routes; rte_is_valid(r); r=r->next)
|
||||||
{
|
{
|
||||||
if (new_best = export_filter(ah, r, &new_free, &tmpa, 0))
|
if (new_best = export_filter(ah, r, &new_free, &tmpa, 0))
|
||||||
break;
|
break;
|
||||||
@ -431,7 +438,8 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
|
|||||||
if (feed)
|
if (feed)
|
||||||
{
|
{
|
||||||
if (feed == 2) /* refeed */
|
if (feed == 2) /* refeed */
|
||||||
old_best = new_best ? new_best : net->routes;
|
old_best = new_best ? new_best :
|
||||||
|
(rte_is_valid(net->routes) ? net->routes : NULL);
|
||||||
else
|
else
|
||||||
old_best = NULL;
|
old_best = NULL;
|
||||||
|
|
||||||
@ -480,7 +488,7 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Fourth case */
|
/* Fourth case */
|
||||||
for (r=r->next; r; r=r->next)
|
for (r=r->next; rte_is_valid(r); r=r->next)
|
||||||
{
|
{
|
||||||
if (old_best = export_filter(ah, r, &old_free, NULL, 1))
|
if (old_best = export_filter(ah, r, &old_free, NULL, 1))
|
||||||
goto found;
|
goto found;
|
||||||
@ -534,7 +542,14 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
|
|||||||
static void
|
static void
|
||||||
rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *before_old, ea_list *tmpa)
|
rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *before_old, ea_list *tmpa)
|
||||||
{
|
{
|
||||||
struct announce_hook *a;
|
if (!rte_is_valid(old))
|
||||||
|
old = before_old = NULL;
|
||||||
|
|
||||||
|
if (!rte_is_valid(new))
|
||||||
|
new = NULL;
|
||||||
|
|
||||||
|
if (!old && !new)
|
||||||
|
return;
|
||||||
|
|
||||||
if (type == RA_OPTIMAL)
|
if (type == RA_OPTIMAL)
|
||||||
{
|
{
|
||||||
@ -547,6 +562,7 @@ rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *befo
|
|||||||
rt_notify_hostcache(tab, net);
|
rt_notify_hostcache(tab, net);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct announce_hook *a;
|
||||||
WALK_LIST(a, tab->hooks)
|
WALK_LIST(a, tab->hooks)
|
||||||
{
|
{
|
||||||
ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING);
|
ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING);
|
||||||
@ -618,6 +634,8 @@ rte_same(rte *x, rte *y)
|
|||||||
(!x->attrs->proto->rte_same || x->attrs->proto->rte_same(x, y));
|
(!x->attrs->proto->rte_same || x->attrs->proto->rte_same(x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); }
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, struct proto *src)
|
rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, struct proto *src)
|
||||||
{
|
{
|
||||||
@ -657,8 +675,13 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
|||||||
if (new && rte_same(old, new))
|
if (new && rte_same(old, new))
|
||||||
{
|
{
|
||||||
/* No changes, ignore the new route */
|
/* No changes, ignore the new route */
|
||||||
stats->imp_updates_ignored++;
|
|
||||||
rte_trace_in(D_ROUTES, p, new, "ignored");
|
if (!rte_is_filtered(new))
|
||||||
|
{
|
||||||
|
stats->imp_updates_ignored++;
|
||||||
|
rte_trace_in(D_ROUTES, p, new, "ignored");
|
||||||
|
}
|
||||||
|
|
||||||
rte_free_quick(new);
|
rte_free_quick(new);
|
||||||
#ifdef CONFIG_RIP
|
#ifdef CONFIG_RIP
|
||||||
/* lastmod is used internally by RIP as the last time
|
/* lastmod is used internally by RIP as the last time
|
||||||
@ -688,8 +711,10 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
|||||||
struct proto_limit *l = ah->in_limit;
|
struct proto_limit *l = ah->in_limit;
|
||||||
if (l && !old && new)
|
if (l && !old && new)
|
||||||
{
|
{
|
||||||
if (stats->imp_routes >= l->limit)
|
u32 all_routes = stats->imp_routes + stats->filt_routes;
|
||||||
proto_notify_limit(ah, l, stats->imp_routes);
|
|
||||||
|
if (all_routes >= l->limit)
|
||||||
|
proto_notify_limit(ah, l, all_routes);
|
||||||
|
|
||||||
if (l->state == PLS_BLOCKED)
|
if (l->state == PLS_BLOCKED)
|
||||||
{
|
{
|
||||||
@ -700,15 +725,20 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new)
|
int new_ok = rte_is_ok(new);
|
||||||
|
int old_ok = rte_is_ok(old);
|
||||||
|
|
||||||
|
if (new_ok)
|
||||||
stats->imp_updates_accepted++;
|
stats->imp_updates_accepted++;
|
||||||
else
|
else if (old_ok)
|
||||||
stats->imp_withdraws_accepted++;
|
stats->imp_withdraws_accepted++;
|
||||||
|
else
|
||||||
|
stats->imp_withdraws_ignored++;
|
||||||
|
|
||||||
if (new)
|
if (new)
|
||||||
stats->imp_routes++;
|
rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++;
|
||||||
if (old)
|
if (old)
|
||||||
stats->imp_routes--;
|
rte_is_filtered(old) ? stats->filt_routes-- : stats->imp_routes--;
|
||||||
|
|
||||||
if (table->config->sorted)
|
if (table->config->sorted)
|
||||||
{
|
{
|
||||||
@ -793,17 +823,19 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
|||||||
new->lastmod = now;
|
new->lastmod = now;
|
||||||
|
|
||||||
/* Log the route change */
|
/* Log the route change */
|
||||||
if (new)
|
if (p->debug & D_ROUTES)
|
||||||
rte_trace_in(D_ROUTES, p, new, net->routes == new ? "added [best]" : "added");
|
|
||||||
|
|
||||||
if (!new && (p->debug & D_ROUTES))
|
|
||||||
{
|
{
|
||||||
if (old != old_best)
|
if (new_ok)
|
||||||
rte_trace_in(D_ROUTES, p, old, "removed");
|
rte_trace(p, new, '>', new == net->routes ? "added [best]" : "added");
|
||||||
else if (net->routes)
|
else if (old_ok)
|
||||||
rte_trace_in(D_ROUTES, p, old, "removed [replaced]");
|
{
|
||||||
else
|
if (old != old_best)
|
||||||
rte_trace_in(D_ROUTES, p, old, "removed [sole]");
|
rte_trace(p, old, '>', "removed");
|
||||||
|
else if (rte_is_ok(net->routes))
|
||||||
|
rte_trace(p, old, '>', "removed [replaced]");
|
||||||
|
else
|
||||||
|
rte_trace(p, old, '>', "removed [sole]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Propagate the route change */
|
/* Propagate the route change */
|
||||||
@ -818,17 +850,13 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
|||||||
(table->gc_time + table->config->gc_min_time <= now))
|
(table->gc_time + table->config->gc_min_time <= now))
|
||||||
rt_schedule_gc(table);
|
rt_schedule_gc(table);
|
||||||
|
|
||||||
|
if (old_ok && p->rte_remove)
|
||||||
|
p->rte_remove(net, old);
|
||||||
|
if (new_ok && p->rte_insert)
|
||||||
|
p->rte_insert(net, new);
|
||||||
|
|
||||||
if (old)
|
if (old)
|
||||||
{
|
rte_free_quick(old);
|
||||||
if (p->rte_remove)
|
|
||||||
p->rte_remove(net, old);
|
|
||||||
rte_free_quick(old);
|
|
||||||
}
|
|
||||||
if (new)
|
|
||||||
{
|
|
||||||
if (p->rte_insert)
|
|
||||||
p->rte_insert(net, new);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rte_update_nest_cnt; /* Nesting counter to allow recursive updates */
|
static int rte_update_nest_cnt; /* Nesting counter to allow recursive updates */
|
||||||
@ -908,27 +936,41 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src)
|
|||||||
stats->imp_updates_invalid++;
|
stats->imp_updates_invalid++;
|
||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter == FILTER_REJECT)
|
if (filter == FILTER_REJECT)
|
||||||
{
|
{
|
||||||
stats->imp_updates_filtered++;
|
stats->imp_updates_filtered++;
|
||||||
rte_trace_in(D_FILTERS, p, new, "filtered out");
|
rte_trace_in(D_FILTERS, p, new, "filtered out");
|
||||||
goto drop;
|
|
||||||
|
if (! ah->in_keep_filtered)
|
||||||
|
goto drop;
|
||||||
|
|
||||||
|
/* new is a private copy, i could modify it */
|
||||||
|
new->flags |= REF_FILTERED;
|
||||||
}
|
}
|
||||||
if (src->make_tmp_attrs)
|
else
|
||||||
tmpa = src->make_tmp_attrs(new, rte_update_pool);
|
|
||||||
if (filter)
|
|
||||||
{
|
{
|
||||||
ea_list *old_tmpa = tmpa;
|
if (src->make_tmp_attrs)
|
||||||
int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0);
|
tmpa = src->make_tmp_attrs(new, rte_update_pool);
|
||||||
if (fr > F_ACCEPT)
|
if (filter && (filter != FILTER_REJECT))
|
||||||
{
|
{
|
||||||
stats->imp_updates_filtered++;
|
ea_list *old_tmpa = tmpa;
|
||||||
rte_trace_in(D_FILTERS, p, new, "filtered out");
|
int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0);
|
||||||
goto drop;
|
if (fr > F_ACCEPT)
|
||||||
|
{
|
||||||
|
stats->imp_updates_filtered++;
|
||||||
|
rte_trace_in(D_FILTERS, p, new, "filtered out");
|
||||||
|
|
||||||
|
if (! ah->in_keep_filtered)
|
||||||
|
goto drop;
|
||||||
|
|
||||||
|
new->flags |= REF_FILTERED;
|
||||||
|
}
|
||||||
|
if (tmpa != old_tmpa && src->store_tmp_attrs)
|
||||||
|
src->store_tmp_attrs(new, tmpa);
|
||||||
}
|
}
|
||||||
if (tmpa != old_tmpa && src->store_tmp_attrs)
|
|
||||||
src->store_tmp_attrs(new, tmpa);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(new->attrs->aflags & RTAF_CACHED)) /* Need to copy attributes */
|
if (!(new->attrs->aflags & RTAF_CACHED)) /* Need to copy attributes */
|
||||||
new->attrs = rta_lookup(new->attrs);
|
new->attrs = rta_lookup(new->attrs);
|
||||||
new->flags |= REF_COW;
|
new->flags |= REF_COW;
|
||||||
@ -1618,9 +1660,11 @@ again:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* XXXX perhaps we should change feed for RA_ACCEPTED to not use 'new' */
|
||||||
|
|
||||||
if ((p->accept_ra_types == RA_OPTIMAL) ||
|
if ((p->accept_ra_types == RA_OPTIMAL) ||
|
||||||
(p->accept_ra_types == RA_ACCEPTED))
|
(p->accept_ra_types == RA_ACCEPTED))
|
||||||
if (e)
|
if (rte_is_valid(e))
|
||||||
{
|
{
|
||||||
if (p->core_state != FS_FEEDING)
|
if (p->core_state != FS_FEEDING)
|
||||||
return 1; /* In the meantime, the protocol fell down. */
|
return 1; /* In the meantime, the protocol fell down. */
|
||||||
@ -1629,7 +1673,7 @@ again:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (p->accept_ra_types == RA_ANY)
|
if (p->accept_ra_types == RA_ANY)
|
||||||
for(e = n->routes; e != NULL; e = e->next)
|
for(e = n->routes; rte_is_valid(e); e = e->next)
|
||||||
{
|
{
|
||||||
if (p->core_state != FS_FEEDING)
|
if (p->core_state != FS_FEEDING)
|
||||||
return 1; /* In the meantime, the protocol fell down. */
|
return 1; /* In the meantime, the protocol fell down. */
|
||||||
@ -1884,7 +1928,8 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
|
|||||||
net *n = net_route(tab, he->addr, MAX_PREFIX_LENGTH);
|
net *n = net_route(tab, he->addr, MAX_PREFIX_LENGTH);
|
||||||
if (n)
|
if (n)
|
||||||
{
|
{
|
||||||
rta *a = n->routes->attrs;
|
rte *e = n->routes;
|
||||||
|
rta *a = e->attrs;
|
||||||
pxlen = n->n.pxlen;
|
pxlen = n->n.pxlen;
|
||||||
|
|
||||||
if (a->hostentry)
|
if (a->hostentry)
|
||||||
@ -1917,7 +1962,7 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
|
|||||||
}
|
}
|
||||||
|
|
||||||
he->src = rta_clone(a);
|
he->src = rta_clone(a);
|
||||||
he->igp_metric = rt_get_igp_metric(n->routes);
|
he->igp_metric = rt_get_igp_metric(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
@ -2061,14 +2106,19 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
|
|||||||
int ok;
|
int ok;
|
||||||
|
|
||||||
fn_print(prefix, sizeof(prefix), &n->n);
|
fn_print(prefix, sizeof(prefix), &n->n);
|
||||||
if (n->routes)
|
|
||||||
d->net_counter++;
|
|
||||||
for(e=n->routes; e; e=e->next)
|
for(e=n->routes; e; e=e->next)
|
||||||
{
|
{
|
||||||
|
if (rte_is_filtered(e) != d->filtered)
|
||||||
|
continue;
|
||||||
|
|
||||||
struct ea_list *tmpa;
|
struct ea_list *tmpa;
|
||||||
struct proto *p0 = e->attrs->proto;
|
struct proto *p0 = e->attrs->proto;
|
||||||
struct proto *p1 = d->export_protocol;
|
struct proto *p1 = d->export_protocol;
|
||||||
struct proto *p2 = d->show_protocol;
|
struct proto *p2 = d->show_protocol;
|
||||||
|
|
||||||
|
if (prefix[0])
|
||||||
|
d->net_counter++;
|
||||||
d->rt_counter++;
|
d->rt_counter++;
|
||||||
ee = e;
|
ee = e;
|
||||||
rte_update_lock(); /* We use the update buffer for filtering */
|
rte_update_lock(); /* We use the update buffer for filtering */
|
||||||
|
@ -238,7 +238,7 @@ bgp_format_aggregator(eattr *a, byte *buf, int buflen UNUSED)
|
|||||||
as = get_u32(data);
|
as = get_u32(data);
|
||||||
data += 4;
|
data += 4;
|
||||||
|
|
||||||
bsprintf(buf, "%d.%d.%d.%d AS%d", data[0], data[1], data[2], data[3], as);
|
bsprintf(buf, "%d.%d.%d.%d AS%u", data[0], data[1], data[2], data[3], as);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -1346,7 +1346,7 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
|
|||||||
|
|
||||||
/* The default case - find a new best-in-group route */
|
/* The default case - find a new best-in-group route */
|
||||||
r = new; /* new may not be in the list */
|
r = new; /* new may not be in the list */
|
||||||
for (s=net->routes; s; s=s->next)
|
for (s=net->routes; rte_is_valid(s); s=s->next)
|
||||||
if (use_deterministic_med(s) && same_group(s, lpref, lasn))
|
if (use_deterministic_med(s) && same_group(s, lpref, lasn))
|
||||||
{
|
{
|
||||||
s->u.bgp.suppressed = 1;
|
s->u.bgp.suppressed = 1;
|
||||||
|
@ -1188,7 +1188,7 @@ bgp_show_proto_info(struct proto *P)
|
|||||||
cli_msg(-1006, " Source address: %I", p->source_addr);
|
cli_msg(-1006, " Source address: %I", p->source_addr);
|
||||||
if (P->cf->in_limit)
|
if (P->cf->in_limit)
|
||||||
cli_msg(-1006, " Route limit: %d/%d",
|
cli_msg(-1006, " Route limit: %d/%d",
|
||||||
p->p.stats.imp_routes, P->cf->in_limit->limit);
|
p->p.stats.imp_routes + p->p.stats.filt_routes, P->cf->in_limit->limit);
|
||||||
cli_msg(-1006, " Hold timer: %d/%d",
|
cli_msg(-1006, " Hold timer: %d/%d",
|
||||||
tm_remains(c->hold_timer), c->hold_time);
|
tm_remains(c->hold_timer), c->hold_time);
|
||||||
cli_msg(-1006, " Keepalive timer: %d/%d",
|
cli_msg(-1006, " Keepalive timer: %d/%d",
|
||||||
|
@ -973,6 +973,10 @@ ospf_ifaces_reconfigure2(struct ospf_area *oa, struct ospf_area_config *nac)
|
|||||||
struct ifa *a;
|
struct ifa *a;
|
||||||
|
|
||||||
WALK_LIST(iface, iface_list)
|
WALK_LIST(iface, iface_list)
|
||||||
|
{
|
||||||
|
if (! (iface->flags & IF_UP))
|
||||||
|
continue;
|
||||||
|
|
||||||
WALK_LIST(a, iface->addrs)
|
WALK_LIST(a, iface->addrs)
|
||||||
{
|
{
|
||||||
if (a->flags & IA_SECONDARY)
|
if (a->flags & IA_SECONDARY)
|
||||||
@ -998,6 +1002,7 @@ ospf_ifaces_reconfigure2(struct ospf_area *oa, struct ospf_area_config *nac)
|
|||||||
ospf_iface_new(oa, a, ip);
|
ospf_iface_new(oa, a, ip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1008,6 +1013,10 @@ ospf_ifaces_reconfigure3(struct ospf_area *oa, struct ospf_area_config *nac)
|
|||||||
struct ifa *a;
|
struct ifa *a;
|
||||||
|
|
||||||
WALK_LIST(iface, iface_list)
|
WALK_LIST(iface, iface_list)
|
||||||
|
{
|
||||||
|
if (! (iface->flags & IF_UP))
|
||||||
|
continue;
|
||||||
|
|
||||||
WALK_LIST(a, iface->addrs)
|
WALK_LIST(a, iface->addrs)
|
||||||
{
|
{
|
||||||
if (a->flags & IA_SECONDARY)
|
if (a->flags & IA_SECONDARY)
|
||||||
@ -1036,6 +1045,7 @@ ospf_ifaces_reconfigure3(struct ospf_area *oa, struct ospf_area_config *nac)
|
|||||||
ospf_iface_new(oa, a, ip);
|
ospf_iface_new(oa, a, ip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -262,8 +262,15 @@ originate_rt2_lsa_body(struct ospf_area *oa, u16 *length)
|
|||||||
WALK_LIST(neigh, ifa->neigh_list)
|
WALK_LIST(neigh, ifa->neigh_list)
|
||||||
if (neigh->state == NEIGHBOR_FULL)
|
if (neigh->state == NEIGHBOR_FULL)
|
||||||
{
|
{
|
||||||
u32 data = (ifa->addr->flags & IA_PEER) ? ifa->iface_id : ipa_to_u32(ifa->addr->ip);
|
/*
|
||||||
add_rt2_lsa_link(po, LSART_PTP, neigh->rid, data, ifa->cost);
|
* ln->data should be ifa->iface_id in case of no/ptp
|
||||||
|
* address (ifa->addr->flags & IA_PEER) on PTP link (see
|
||||||
|
* RFC 2328 12.4.1.1.), but the iface ID value has no use,
|
||||||
|
* while using IP address even in this case is here for
|
||||||
|
* compatibility with some broken implementations that use
|
||||||
|
* this address as a next-hop.
|
||||||
|
*/
|
||||||
|
add_rt2_lsa_link(po, LSART_PTP, neigh->rid, ipa_to_u32(ifa->addr->ip), ifa->cost);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -294,7 +301,7 @@ originate_rt2_lsa_body(struct ospf_area *oa, u16 *length)
|
|||||||
/* Now we will originate stub area if there is no primary */
|
/* Now we will originate stub area if there is no primary */
|
||||||
if (net_lsa ||
|
if (net_lsa ||
|
||||||
(ifa->type == OSPF_IT_VLINK) ||
|
(ifa->type == OSPF_IT_VLINK) ||
|
||||||
(ifa->addr->flags & IA_PEER) ||
|
((ifa->addr->flags & IA_PEER) && ! ifa->cf->stub) ||
|
||||||
configured_stubnet(oa, ifa->addr))
|
configured_stubnet(oa, ifa->addr))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#define _BIRD_CONFIG_H_
|
#define _BIRD_CONFIG_H_
|
||||||
|
|
||||||
/* BIRD version */
|
/* BIRD version */
|
||||||
#define BIRD_VERSION "1.3.8"
|
#define BIRD_VERSION "1.3.9"
|
||||||
|
|
||||||
// XXXX temporary define
|
// XXXX temporary define
|
||||||
#define IPV1 1
|
#define IPV1 1
|
||||||
|
@ -581,7 +581,7 @@ krt_flush_routes(struct krt_proto *p)
|
|||||||
{
|
{
|
||||||
net *n = (net *) f;
|
net *n = (net *) f;
|
||||||
rte *e = n->routes;
|
rte *e = n->routes;
|
||||||
if (e && (n->n.flags & KRF_INSTALLED))
|
if (rte_is_valid(e) && (n->n.flags & KRF_INSTALLED))
|
||||||
{
|
{
|
||||||
/* FIXME: this does not work if gw is changed in export filter */
|
/* FIXME: this does not work if gw is changed in export filter */
|
||||||
krt_replace_rte(p, e->net, NULL, e, NULL);
|
krt_replace_rte(p, e->net, NULL, e, NULL);
|
||||||
@ -657,7 +657,7 @@ krt_got_route(struct krt_proto *p, rte *e)
|
|||||||
}
|
}
|
||||||
|
|
||||||
old = net->routes;
|
old = net->routes;
|
||||||
if ((net->n.flags & KRF_INSTALLED) && old)
|
if ((net->n.flags & KRF_INSTALLED) && rte_is_valid(old))
|
||||||
{
|
{
|
||||||
/* There may be changes in route attributes, we ignore that.
|
/* There may be changes in route attributes, we ignore that.
|
||||||
Also, this does not work well if gw is changed in export filter */
|
Also, this does not work well if gw is changed in export filter */
|
||||||
|
Loading…
Reference in New Issue
Block a user