Implement RA_ACCEPTED mode of route propagation.
This commit is contained in:
parent
bf2abe2f51
commit
00a09f3c36
5 changed files with 281 additions and 160 deletions
|
@ -219,11 +219,12 @@ typedef struct rte {
|
||||||
} u;
|
} u;
|
||||||
} rte;
|
} rte;
|
||||||
|
|
||||||
#define REF_COW 1 /* Copy this rte on write */
|
#define REF_COW 1 /* Copy this rte on write */
|
||||||
|
|
||||||
/* Types of route announcement, also used as flags */
|
/* Types of route announcement, also used as flags */
|
||||||
#define RA_OPTIMAL 1 /* Announcement of optimal route change */
|
#define RA_OPTIMAL 1 /* Announcement of optimal route change */
|
||||||
#define RA_ANY 2 /* Announcement of any route change */
|
#define RA_ACCEPTED 2 /* Announcement of first accepted route */
|
||||||
|
#define RA_ANY 3 /* Announcement of any route change */
|
||||||
|
|
||||||
struct config;
|
struct config;
|
||||||
|
|
||||||
|
|
416
nest/rt-table.c
416
nest/rt-table.c
|
@ -182,86 +182,73 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg)
|
||||||
rte_trace(p, e, '<', msg);
|
rte_trace(p, e, '<', msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static rte *
|
||||||
do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
|
export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa, int silent)
|
||||||
{
|
{
|
||||||
struct proto *p = ah->proto;
|
struct proto *p = ah->proto;
|
||||||
struct filter *filter = ah->out_filter;
|
struct filter *filter = ah->out_filter;
|
||||||
struct proto_stats *stats = ah->stats;
|
struct proto_stats *stats = ah->stats;
|
||||||
|
ea_list *tmpb = NULL;
|
||||||
|
rte *rt;
|
||||||
|
int v;
|
||||||
|
|
||||||
rte *new0 = new;
|
rt = rt0;
|
||||||
rte *old0 = old;
|
*rt_free = NULL;
|
||||||
int ok;
|
|
||||||
|
|
||||||
if (new)
|
/* If called does not care for eattrs, we prepare one internally */
|
||||||
|
if (!tmpa)
|
||||||
{
|
{
|
||||||
stats->exp_updates_received++;
|
struct proto *src = rt->attrs->proto;
|
||||||
|
tmpb = src->make_tmp_attrs ? src->make_tmp_attrs(rt, rte_update_pool) : NULL;
|
||||||
char *drop_reason = NULL;
|
tmpa = &tmpb;
|
||||||
if ((ok = p->import_control ? p->import_control(p, &new, &tmpa, rte_update_pool) : 0) < 0)
|
|
||||||
{
|
|
||||||
stats->exp_updates_rejected++;
|
|
||||||
drop_reason = "rejected by protocol";
|
|
||||||
}
|
|
||||||
else if (ok)
|
|
||||||
rte_trace_out(D_FILTERS, p, new, "forced accept by protocol");
|
|
||||||
else if ((filter == FILTER_REJECT) ||
|
|
||||||
(filter && f_run(filter, &new, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))
|
|
||||||
{
|
|
||||||
stats->exp_updates_filtered++;
|
|
||||||
drop_reason = "filtered out";
|
|
||||||
}
|
|
||||||
if (drop_reason)
|
|
||||||
{
|
|
||||||
rte_trace_out(D_FILTERS, p, new, drop_reason);
|
|
||||||
if (new != new0)
|
|
||||||
rte_free(new);
|
|
||||||
new = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
stats->exp_withdraws_received++;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is a tricky part - we don't know whether route 'old' was
|
|
||||||
* exported to protocol 'p' or was filtered by the export filter.
|
|
||||||
* We try tu run the export filter to know this to have a correct
|
|
||||||
* value in 'old' argument of rte_update (and proper filter value)
|
|
||||||
*
|
|
||||||
* FIXME - this is broken because 'configure soft' may change
|
|
||||||
* filters but keep routes. Refeed is expected to be called after
|
|
||||||
* change of the filters and with old == new, therefore we do not
|
|
||||||
* even try to run the filter on an old route, This may lead to
|
|
||||||
* 'spurious withdraws' but ensure that there are no 'missing
|
|
||||||
* withdraws'.
|
|
||||||
*
|
|
||||||
* This is not completely safe as there is a window between
|
|
||||||
* reconfiguration and the end of refeed - if a newly filtered
|
|
||||||
* route disappears during this period, proper withdraw is not
|
|
||||||
* sent (because old would be also filtered) and the route is
|
|
||||||
* not refeeded (because it disappeared before that).
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (old && !refeed)
|
|
||||||
{
|
|
||||||
if (filter == FILTER_REJECT)
|
|
||||||
old = NULL;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ea_list *tmpb = p->make_tmp_attrs ? p->make_tmp_attrs(old, rte_update_pool) : NULL;
|
|
||||||
ok = p->import_control ? p->import_control(p, &old, &tmpb, rte_update_pool) : 0;
|
|
||||||
if (ok < 0 || (!ok && filter && f_run(filter, &old, &tmpb, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))
|
|
||||||
{
|
|
||||||
if (old != old0)
|
|
||||||
rte_free(old);
|
|
||||||
old = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME - This is broken because of incorrect 'old' value (see above) */
|
v = p->import_control ? p->import_control(p, &rt, tmpa, rte_update_pool) : 0;
|
||||||
if (!new && !old)
|
if (v < 0)
|
||||||
return;
|
{
|
||||||
|
if (silent)
|
||||||
|
goto reject;
|
||||||
|
|
||||||
|
stats->exp_updates_rejected++;
|
||||||
|
rte_trace_out(D_FILTERS, p, rt, "rejected by protocol");
|
||||||
|
goto reject;
|
||||||
|
}
|
||||||
|
if (v > 0)
|
||||||
|
{
|
||||||
|
if (!silent)
|
||||||
|
rte_trace_out(D_FILTERS, p, rt, "forced accept by protocol");
|
||||||
|
goto accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = filter && ((filter == FILTER_REJECT) ||
|
||||||
|
(f_run(filter, &rt, tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT));
|
||||||
|
if (v)
|
||||||
|
{
|
||||||
|
if (silent)
|
||||||
|
goto reject;
|
||||||
|
|
||||||
|
stats->exp_updates_filtered++;
|
||||||
|
rte_trace_out(D_FILTERS, p, rt, "filtered out");
|
||||||
|
goto reject;
|
||||||
|
}
|
||||||
|
|
||||||
|
accept:
|
||||||
|
if (rt != rt0)
|
||||||
|
*rt_free = rt;
|
||||||
|
return rt;
|
||||||
|
|
||||||
|
reject:
|
||||||
|
/* Discard temporary rte */
|
||||||
|
if (rt != rt0)
|
||||||
|
rte_free(rt);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
|
||||||
|
{
|
||||||
|
struct proto *p = ah->proto;
|
||||||
|
struct proto_stats *stats = ah->stats;
|
||||||
|
|
||||||
if (new)
|
if (new)
|
||||||
stats->exp_updates_accepted++;
|
stats->exp_updates_accepted++;
|
||||||
|
@ -297,10 +284,174 @@ do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, r
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
p->rt_notify(p, ah->table, net, new, old, new->attrs->eattrs);
|
p->rt_notify(p, ah->table, net, new, old, new->attrs->eattrs);
|
||||||
if (new && new != new0) /* Discard temporary rte's */
|
|
||||||
rte_free(new);
|
}
|
||||||
if (old && old != old0)
|
|
||||||
rte_free(old);
|
|
||||||
|
static void
|
||||||
|
rt_notify_basic(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
|
||||||
|
{
|
||||||
|
// struct proto *p = ah->proto;
|
||||||
|
struct proto_stats *stats = ah->stats;
|
||||||
|
|
||||||
|
rte *new_free = NULL;
|
||||||
|
rte *old_free = NULL;
|
||||||
|
|
||||||
|
if (new)
|
||||||
|
stats->exp_updates_received++;
|
||||||
|
else
|
||||||
|
stats->exp_withdraws_received++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a tricky part - we don't know whether route 'old' was
|
||||||
|
* exported to protocol 'p' or was filtered by the export filter.
|
||||||
|
* We try to run the export filter to know this to have a correct
|
||||||
|
* value in 'old' argument of rte_update (and proper filter value)
|
||||||
|
*
|
||||||
|
* FIXME - this is broken because 'configure soft' may change
|
||||||
|
* filters but keep routes. Refeed is expected to be called after
|
||||||
|
* change of the filters and with old == new, therefore we do not
|
||||||
|
* even try to run the filter on an old route, This may lead to
|
||||||
|
* 'spurious withdraws' but ensure that there are no 'missing
|
||||||
|
* withdraws'.
|
||||||
|
*
|
||||||
|
* This is not completely safe as there is a window between
|
||||||
|
* reconfiguration and the end of refeed - if a newly filtered
|
||||||
|
* route disappears during this period, proper withdraw is not
|
||||||
|
* sent (because old would be also filtered) and the route is
|
||||||
|
* not refeeded (because it disappeared before that).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (new)
|
||||||
|
new = export_filter(ah, new, &new_free, &tmpa, 0);
|
||||||
|
|
||||||
|
if (old && !refeed)
|
||||||
|
old = export_filter(ah, old, &old_free, NULL, 1);
|
||||||
|
|
||||||
|
/* FIXME - This is broken because of incorrect 'old' value (see above) */
|
||||||
|
if (!new && !old)
|
||||||
|
return;
|
||||||
|
|
||||||
|
do_rt_notify(ah, net, new, old, tmpa, refeed);
|
||||||
|
|
||||||
|
/* Discard temporary rte's */
|
||||||
|
if (new_free)
|
||||||
|
rte_free(new_free);
|
||||||
|
if (old_free)
|
||||||
|
rte_free(old_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed, rte *before_old,
|
||||||
|
ea_list *tmpa, int feed)
|
||||||
|
{
|
||||||
|
// struct proto *p = ah->proto;
|
||||||
|
struct proto_stats *stats = ah->stats;
|
||||||
|
|
||||||
|
rte *new_best = NULL;
|
||||||
|
rte *old_best = NULL;
|
||||||
|
rte *new_free = NULL;
|
||||||
|
rte *old_free = NULL;
|
||||||
|
rte *r;
|
||||||
|
|
||||||
|
/* Used to track whethe we met old_changed position. If it is NULL
|
||||||
|
it was the first and met it implicitly before current best route. */
|
||||||
|
int old_meet = (old_changed && !before_old) ? 1 : 0;
|
||||||
|
|
||||||
|
if (new_changed)
|
||||||
|
stats->exp_updates_received++;
|
||||||
|
else
|
||||||
|
stats->exp_withdraws_received++;
|
||||||
|
|
||||||
|
/* First, find the new_best route - first accepted by filters */
|
||||||
|
for (r=net->routes; r; r=r->next)
|
||||||
|
{
|
||||||
|
if (new_best = export_filter(ah, r, &new_free, &tmpa, 0))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Note if we walked around the position of old_changed route */
|
||||||
|
if (r == before_old)
|
||||||
|
old_meet = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Second, handle the feed case. That means we do not care for
|
||||||
|
* old_best. It is NULL for feed, and the new_best for refeed.
|
||||||
|
* For refeed, there is a hack similar to one in rt_notify_basic()
|
||||||
|
* to ensure withdraws in case of changed filters
|
||||||
|
*/
|
||||||
|
if (feed)
|
||||||
|
{
|
||||||
|
if (feed == 2) /* refeed */
|
||||||
|
old_best = new_best ? new_best : net->routes;
|
||||||
|
else
|
||||||
|
old_best = NULL;
|
||||||
|
|
||||||
|
if (!new_best && !old_best)
|
||||||
|
return;
|
||||||
|
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now, we find the old_best route. Generally, it is the same as the
|
||||||
|
* new_best, unless new_best is the same as new_changed or
|
||||||
|
* old_changed is accepted before new_best.
|
||||||
|
*
|
||||||
|
* There are four cases:
|
||||||
|
*
|
||||||
|
* - We would find and accept old_changed before new_best, therefore
|
||||||
|
* old_changed is old_best. In remaining cases we suppose this
|
||||||
|
* is not true.
|
||||||
|
*
|
||||||
|
* - We found no new_best, therefore there is also no old_best and
|
||||||
|
* we ignore this withdraw.
|
||||||
|
*
|
||||||
|
* - We found new_best different than new_changed, therefore
|
||||||
|
* old_best is the same as new_best and we ignore this update.
|
||||||
|
*
|
||||||
|
* - We found new_best the same as new_changed, therefore it cannot
|
||||||
|
* be old_best and we have to continue search for old_best.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* First case */
|
||||||
|
if (old_meet)
|
||||||
|
if (old_best = export_filter(ah, old_changed, &old_free, NULL, 1))
|
||||||
|
goto found;
|
||||||
|
|
||||||
|
/* Second case */
|
||||||
|
if (!new_best)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Third case, we use r insead of new_best, because export_filter() could change it */
|
||||||
|
if (r != new_changed)
|
||||||
|
{
|
||||||
|
if (new_free)
|
||||||
|
rte_free(new_free);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fourth case */
|
||||||
|
for (; r; r=r->next)
|
||||||
|
{
|
||||||
|
if (old_best = export_filter(ah, r, &old_free, NULL, 1))
|
||||||
|
goto found;
|
||||||
|
|
||||||
|
if (r == before_old)
|
||||||
|
if (old_best = export_filter(ah, old_changed, &old_free, NULL, 1))
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implicitly, old_best is NULL and new_best is non-NULL */
|
||||||
|
|
||||||
|
found:
|
||||||
|
do_rt_notify(ah, net, new_best, old_best, tmpa, (feed == 2));
|
||||||
|
|
||||||
|
/* Discard temporary rte's */
|
||||||
|
if (new_free)
|
||||||
|
rte_free(new_free);
|
||||||
|
if (old_free)
|
||||||
|
rte_free(old_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -333,7 +484,7 @@ do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, r
|
||||||
* the protocol gets called.
|
* the protocol gets called.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, ea_list *tmpa)
|
rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *before_old, ea_list *tmpa)
|
||||||
{
|
{
|
||||||
struct announce_hook *a;
|
struct announce_hook *a;
|
||||||
|
|
||||||
|
@ -352,11 +503,13 @@ rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, ea_list *
|
||||||
{
|
{
|
||||||
ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING);
|
ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING);
|
||||||
if (a->proto->accept_ra_types == type)
|
if (a->proto->accept_ra_types == type)
|
||||||
do_rte_announce(a, type, net, new, old, tmpa, 0);
|
if (type == RA_ACCEPTED)
|
||||||
|
rt_notify_accepted(a, net, new, old, before_old, tmpa, 0);
|
||||||
|
else
|
||||||
|
rt_notify_basic(a, net, new, old, tmpa, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
rte_validate(rte *e)
|
rte_validate(rte *e)
|
||||||
{
|
{
|
||||||
|
@ -419,9 +572,10 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
||||||
struct proto *p = ah->proto;
|
struct proto *p = ah->proto;
|
||||||
struct rtable *table = ah->table;
|
struct rtable *table = ah->table;
|
||||||
struct proto_stats *stats = ah->stats;
|
struct proto_stats *stats = ah->stats;
|
||||||
|
rte *before_old = NULL;
|
||||||
rte *old_best = net->routes;
|
rte *old_best = net->routes;
|
||||||
rte *old = NULL;
|
rte *old = NULL;
|
||||||
rte **k, *r, *s;
|
rte **k;
|
||||||
|
|
||||||
k = &net->routes; /* Find and remove original route from the same protocol */
|
k = &net->routes; /* Find and remove original route from the same protocol */
|
||||||
while (old = *k)
|
while (old = *k)
|
||||||
|
@ -466,8 +620,12 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
k = &old->next;
|
k = &old->next;
|
||||||
|
before_old = old;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!old)
|
||||||
|
before_old = NULL;
|
||||||
|
|
||||||
if (!old && !new)
|
if (!old && !new)
|
||||||
{
|
{
|
||||||
stats->imp_withdraws_ignored++;
|
stats->imp_withdraws_ignored++;
|
||||||
|
@ -484,82 +642,21 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
||||||
if (old)
|
if (old)
|
||||||
stats->imp_routes--;
|
stats->imp_routes--;
|
||||||
|
|
||||||
rte_announce(table, RA_ANY, net, new, old, tmpa);
|
if (new)
|
||||||
|
|
||||||
if (src->rte_recalculate && src->rte_recalculate(table, net, new, old, old_best))
|
|
||||||
goto do_recalculate;
|
|
||||||
|
|
||||||
if (new && rte_better(new, old_best))
|
|
||||||
{
|
{
|
||||||
/* The first case - the new route is cleary optimal, we link it
|
for (k=&net->routes; *k; k=&(*k)->next)
|
||||||
at the first position and announce it */
|
if (rte_better(new, *k))
|
||||||
|
break;
|
||||||
|
|
||||||
rte_trace_in(D_ROUTES, p, new, "added [best]");
|
new->lastmod = now;
|
||||||
rte_announce(table, RA_OPTIMAL, net, new, old_best, tmpa);
|
new->next = *k;
|
||||||
new->next = net->routes;
|
*k = new;
|
||||||
net->routes = new;
|
|
||||||
}
|
|
||||||
else if (old == old_best)
|
|
||||||
{
|
|
||||||
/* The second case - the old best route disappeared, we add the
|
|
||||||
new route (if we have any) to the list (we don't care about
|
|
||||||
position) and then we elect the new optimal route and relink
|
|
||||||
that route at the first position and announce it. New optimal
|
|
||||||
route might be NULL if there is no more routes */
|
|
||||||
|
|
||||||
do_recalculate:
|
rte_trace_in(D_ROUTES, p, new, net->routes == new ? "added [best]" : "added");
|
||||||
/* Add the new route to the list */
|
|
||||||
if (new)
|
|
||||||
{
|
|
||||||
rte_trace_in(D_ROUTES, p, new, "added");
|
|
||||||
new->next = net->routes;
|
|
||||||
net->routes = new;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find new optimal route */
|
|
||||||
r = NULL;
|
|
||||||
for (s=net->routes; s; s=s->next)
|
|
||||||
if (rte_better(s, r))
|
|
||||||
r = s;
|
|
||||||
|
|
||||||
/* Announce optimal route */
|
|
||||||
rte_announce(table, RA_OPTIMAL, net, r, old_best, tmpa);
|
|
||||||
|
|
||||||
/* And relink it (if there is any) */
|
|
||||||
if (r)
|
|
||||||
{
|
|
||||||
k = &net->routes;
|
|
||||||
while (s = *k)
|
|
||||||
{
|
|
||||||
if (s == r)
|
|
||||||
{
|
|
||||||
*k = r->next;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
k = &s->next;
|
|
||||||
}
|
|
||||||
r->next = net->routes;
|
|
||||||
net->routes = r;
|
|
||||||
}
|
|
||||||
else if (table->gc_counter++ >= table->config->gc_max_ops &&
|
|
||||||
table->gc_time + table->config->gc_min_time <= now)
|
|
||||||
rt_schedule_gc(table);
|
|
||||||
}
|
|
||||||
else if (new)
|
|
||||||
{
|
|
||||||
/* The third case - the new route is not better than the old
|
|
||||||
best route (therefore old_best != NULL) and the old best
|
|
||||||
route was not removed (therefore old_best == net->routes).
|
|
||||||
We just link the new route after the old best route. */
|
|
||||||
|
|
||||||
ASSERT(net->routes != NULL);
|
|
||||||
new->next = net->routes->next;
|
|
||||||
net->routes->next = new;
|
|
||||||
rte_trace_in(D_ROUTES, p, new, "added");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Log the route removal */
|
/* Log the route removal */
|
||||||
if (!new && old && (p->debug & D_ROUTES))
|
if (!new && (p->debug & D_ROUTES))
|
||||||
{
|
{
|
||||||
if (old != old_best)
|
if (old != old_best)
|
||||||
rte_trace_in(D_ROUTES, p, old, "removed");
|
rte_trace_in(D_ROUTES, p, old, "removed");
|
||||||
|
@ -569,6 +666,15 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
||||||
rte_trace_in(D_ROUTES, p, old, "removed [sole]");
|
rte_trace_in(D_ROUTES, p, old, "removed [sole]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rte_announce(table, RA_ANY, net, new, old, NULL, tmpa);
|
||||||
|
rte_announce(table, RA_OPTIMAL, net, net->routes, old_best, NULL, tmpa);
|
||||||
|
rte_announce(table, RA_ACCEPTED, net, new, old, before_old, tmpa);
|
||||||
|
|
||||||
|
if (!net->routes &&
|
||||||
|
(table->gc_counter++ >= table->config->gc_max_ops) &&
|
||||||
|
(table->gc_time + table->config->gc_min_time <= now))
|
||||||
|
rt_schedule_gc(table);
|
||||||
|
|
||||||
if (old)
|
if (old)
|
||||||
{
|
{
|
||||||
if (p->rte_remove)
|
if (p->rte_remove)
|
||||||
|
@ -577,7 +683,6 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
|
||||||
}
|
}
|
||||||
if (new)
|
if (new)
|
||||||
{
|
{
|
||||||
new->lastmod = now;
|
|
||||||
if (p->rte_insert)
|
if (p->rte_insert)
|
||||||
p->rte_insert(net, new);
|
p->rte_insert(net, new);
|
||||||
}
|
}
|
||||||
|
@ -709,7 +814,7 @@ rte_announce_i(rtable *tab, unsigned type, net *n, rte *new, rte *old)
|
||||||
rte_update_lock();
|
rte_update_lock();
|
||||||
src = new->attrs->proto;
|
src = new->attrs->proto;
|
||||||
tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(new, rte_update_pool) : NULL;
|
tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(new, rte_update_pool) : NULL;
|
||||||
rte_announce(tab, type, n, new, old, tmpa);
|
rte_announce(tab, type, n, new, old, NULL, tmpa);
|
||||||
rte_update_unlock();
|
rte_update_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1259,12 +1364,15 @@ rt_commit(struct config *new, struct config *old)
|
||||||
static inline void
|
static inline void
|
||||||
do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e)
|
do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e)
|
||||||
{
|
{
|
||||||
struct proto *q = e->attrs->proto;
|
struct proto *src = e->attrs->proto;
|
||||||
ea_list *tmpa;
|
ea_list *tmpa;
|
||||||
|
|
||||||
rte_update_lock();
|
rte_update_lock();
|
||||||
tmpa = q->make_tmp_attrs ? q->make_tmp_attrs(e, rte_update_pool) : NULL;
|
tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(e, rte_update_pool) : NULL;
|
||||||
do_rte_announce(h, type, n, e, p->refeeding ? e : NULL, tmpa, p->refeeding);
|
if (type == RA_ACCEPTED)
|
||||||
|
rt_notify_accepted(h, n, e, NULL, NULL, tmpa, p->refeeding ? 2 : 1);
|
||||||
|
else
|
||||||
|
rt_notify_basic(h, n, e, p->refeeding ? e : NULL, tmpa, p->refeeding);
|
||||||
rte_update_unlock();
|
rte_update_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -909,7 +909,7 @@ bgp_init(struct proto_config *C)
|
||||||
struct proto *P = proto_new(C, sizeof(struct bgp_proto));
|
struct proto *P = proto_new(C, sizeof(struct bgp_proto));
|
||||||
struct bgp_proto *p = (struct bgp_proto *) P;
|
struct bgp_proto *p = (struct bgp_proto *) P;
|
||||||
|
|
||||||
P->accept_ra_types = RA_OPTIMAL;
|
P->accept_ra_types = c->secondary ? RA_ACCEPTED : RA_OPTIMAL;
|
||||||
P->rt_notify = bgp_rt_notify;
|
P->rt_notify = bgp_rt_notify;
|
||||||
P->rte_better = bgp_rte_better;
|
P->rte_better = bgp_rte_better;
|
||||||
P->import_control = bgp_import_control;
|
P->import_control = bgp_import_control;
|
||||||
|
@ -955,12 +955,14 @@ bgp_check_config(struct bgp_config *c)
|
||||||
if (internal && c->rs_client)
|
if (internal && c->rs_client)
|
||||||
cf_error("Only external neighbor can be RS client");
|
cf_error("Only external neighbor can be RS client");
|
||||||
|
|
||||||
|
/*
|
||||||
if (c->multihop && (c->gw_mode == GW_DIRECT))
|
if (c->multihop && (c->gw_mode == GW_DIRECT))
|
||||||
cf_error("Multihop BGP cannot use direct gateway mode");
|
cf_error("Multihop BGP cannot use direct gateway mode");
|
||||||
|
|
||||||
if (c->multihop && (ipa_has_link_scope(c->remote_ip) ||
|
if (c->multihop && (ipa_has_link_scope(c->remote_ip) ||
|
||||||
ipa_has_link_scope(c->source_addr)))
|
ipa_has_link_scope(c->source_addr)))
|
||||||
cf_error("Multihop BGP cannot be used with link-local addresses");
|
cf_error("Multihop BGP cannot be used with link-local addresses");
|
||||||
|
*/
|
||||||
|
|
||||||
/* Different default based on rs_client */
|
/* Different default based on rs_client */
|
||||||
if (!c->missing_lladdr)
|
if (!c->missing_lladdr)
|
||||||
|
@ -968,7 +970,8 @@ bgp_check_config(struct bgp_config *c)
|
||||||
|
|
||||||
/* Different default for gw_mode */
|
/* Different default for gw_mode */
|
||||||
if (!c->gw_mode)
|
if (!c->gw_mode)
|
||||||
c->gw_mode = (c->multihop || internal) ? GW_RECURSIVE : GW_DIRECT;
|
// c->gw_mode = (c->multihop || internal) ? GW_RECURSIVE : GW_DIRECT;
|
||||||
|
c->gw_mode = GW_DIRECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
|
@ -43,6 +43,7 @@ struct bgp_config {
|
||||||
u32 route_limit; /* Number of routes that may be imported, 0 means disable limit */
|
u32 route_limit; /* Number of routes that may be imported, 0 means disable limit */
|
||||||
int passive; /* Do not initiate outgoing connection */
|
int passive; /* Do not initiate outgoing connection */
|
||||||
int interpret_communities; /* Hardwired handling of well-known communities */
|
int interpret_communities; /* Hardwired handling of well-known communities */
|
||||||
|
int secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */
|
||||||
unsigned connect_retry_time;
|
unsigned connect_retry_time;
|
||||||
unsigned hold_time, initial_hold_time;
|
unsigned hold_time, initial_hold_time;
|
||||||
unsigned keepalive_time;
|
unsigned keepalive_time;
|
||||||
|
|
|
@ -25,7 +25,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
|
||||||
CLUSTER, ID, AS4, ADVERTISE, IPV4, CAPABILITIES, LIMIT, PASSIVE,
|
CLUSTER, ID, AS4, ADVERTISE, IPV4, CAPABILITIES, LIMIT, PASSIVE,
|
||||||
PREFER, OLDER, MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH,
|
PREFER, OLDER, MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH,
|
||||||
INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
|
INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
|
||||||
TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC)
|
TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC,
|
||||||
|
SECONDARY)
|
||||||
|
|
||||||
CF_GRAMMAR
|
CF_GRAMMAR
|
||||||
|
|
||||||
|
@ -73,19 +74,25 @@ bgp_proto:
|
||||||
| bgp_proto STARTUP HOLD TIME expr ';' { BGP_CFG->initial_hold_time = $5; }
|
| bgp_proto STARTUP HOLD TIME expr ';' { BGP_CFG->initial_hold_time = $5; }
|
||||||
| bgp_proto CONNECT RETRY TIME expr ';' { BGP_CFG->connect_retry_time = $5; }
|
| bgp_proto CONNECT RETRY TIME expr ';' { BGP_CFG->connect_retry_time = $5; }
|
||||||
| bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; }
|
| bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; }
|
||||||
|
/*
|
||||||
| bgp_proto MULTIHOP ';' { BGP_CFG->multihop = 64; }
|
| bgp_proto MULTIHOP ';' { BGP_CFG->multihop = 64; }
|
||||||
| bgp_proto MULTIHOP expr ';' { BGP_CFG->multihop = $3; if (($3<1) || ($3>255)) cf_error("Multihop must be in range 1-255"); }
|
| bgp_proto MULTIHOP expr ';' { BGP_CFG->multihop = $3; if (($3<1) || ($3>255)) cf_error("Multihop must be in range 1-255"); }
|
||||||
|
*/
|
||||||
| bgp_proto NEXT HOP SELF ';' { BGP_CFG->next_hop_self = 1; }
|
| bgp_proto NEXT HOP SELF ';' { BGP_CFG->next_hop_self = 1; }
|
||||||
| bgp_proto MISSING LLADDR SELF ';' { BGP_CFG->missing_lladdr = MLL_SELF; }
|
| bgp_proto MISSING LLADDR SELF ';' { BGP_CFG->missing_lladdr = MLL_SELF; }
|
||||||
| bgp_proto MISSING LLADDR DROP ';' { BGP_CFG->missing_lladdr = MLL_DROP; }
|
| bgp_proto MISSING LLADDR DROP ';' { BGP_CFG->missing_lladdr = MLL_DROP; }
|
||||||
| bgp_proto MISSING LLADDR IGNORE ';' { BGP_CFG->missing_lladdr = MLL_IGNORE; }
|
| bgp_proto MISSING LLADDR IGNORE ';' { BGP_CFG->missing_lladdr = MLL_IGNORE; }
|
||||||
|
/*
|
||||||
| bgp_proto GATEWAY DIRECT ';' { BGP_CFG->gw_mode = GW_DIRECT; }
|
| bgp_proto GATEWAY DIRECT ';' { BGP_CFG->gw_mode = GW_DIRECT; }
|
||||||
| bgp_proto GATEWAY RECURSIVE ';' { BGP_CFG->gw_mode = GW_RECURSIVE; }
|
| bgp_proto GATEWAY RECURSIVE ';' { BGP_CFG->gw_mode = GW_RECURSIVE; }
|
||||||
|
*/
|
||||||
| bgp_proto PATH METRIC bool ';' { BGP_CFG->compare_path_lengths = $4; }
|
| bgp_proto PATH METRIC bool ';' { BGP_CFG->compare_path_lengths = $4; }
|
||||||
| bgp_proto MED METRIC bool ';' { BGP_CFG->med_metric = $4; }
|
| bgp_proto MED METRIC bool ';' { BGP_CFG->med_metric = $4; }
|
||||||
| bgp_proto IGP METRIC bool ';' { BGP_CFG->igp_metric = $4; }
|
| bgp_proto IGP METRIC bool ';' { BGP_CFG->igp_metric = $4; }
|
||||||
| bgp_proto PREFER OLDER bool ';' { BGP_CFG->prefer_older = $4; }
|
| bgp_proto PREFER OLDER bool ';' { BGP_CFG->prefer_older = $4; }
|
||||||
|
/*
|
||||||
| bgp_proto DETERMINISTIC MED bool ';' { BGP_CFG->deterministic_med = $4; }
|
| bgp_proto DETERMINISTIC MED bool ';' { BGP_CFG->deterministic_med = $4; }
|
||||||
|
*/
|
||||||
| bgp_proto DEFAULT BGP_MED expr ';' { BGP_CFG->default_med = $4; }
|
| bgp_proto DEFAULT BGP_MED expr ';' { BGP_CFG->default_med = $4; }
|
||||||
| bgp_proto DEFAULT BGP_LOCAL_PREF expr ';' { BGP_CFG->default_local_pref = $4; }
|
| bgp_proto DEFAULT BGP_LOCAL_PREF expr ';' { BGP_CFG->default_local_pref = $4; }
|
||||||
| bgp_proto SOURCE ADDRESS ipa ';' { BGP_CFG->source_addr = $4; }
|
| bgp_proto SOURCE ADDRESS ipa ';' { BGP_CFG->source_addr = $4; }
|
||||||
|
@ -101,6 +108,7 @@ bgp_proto:
|
||||||
| bgp_proto ROUTE LIMIT expr ';' { BGP_CFG->route_limit = $4; }
|
| bgp_proto ROUTE LIMIT expr ';' { BGP_CFG->route_limit = $4; }
|
||||||
| bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
|
| bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
|
||||||
| bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
|
| bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
|
||||||
|
| bgp_proto SECONDARY bool ';' { BGP_CFG->secondary = $3; }
|
||||||
| bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
|
| bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
|
||||||
| bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; }
|
| bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; }
|
||||||
;
|
;
|
||||||
|
|
Loading…
Reference in a new issue