mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-11-17 08:38:42 +00:00
Fixes several minor bugs in kernel syncer.
This commit is contained in:
parent
9ba2798c65
commit
c9df01d321
@ -1618,7 +1618,7 @@ kernel table.
|
|||||||
<p>Because the kernel protocol is partially integrated with the
|
<p>Because the kernel protocol is partially integrated with the
|
||||||
connected routing table, there are two limitations - it is not
|
connected routing table, there are two limitations - it is not
|
||||||
possible to connect more kernel protocols to the same routing table
|
possible to connect more kernel protocols to the same routing table
|
||||||
and changing route attributes (even the kernel ones) in an export
|
and changing route destination/gateway in an export
|
||||||
filter of a kernel protocol does not work. Both limitations can be
|
filter of a kernel protocol does not work. Both limitations can be
|
||||||
overcome using another routing table and the pipe protocol.
|
overcome using another routing table and the pipe protocol.
|
||||||
|
|
||||||
|
@ -189,7 +189,8 @@ krt_sock_send(int cmd, rte *e)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
krt_set_notify(struct krt_proto *p UNUSED, net *n, rte *new, rte *old)
|
krt_set_notify(struct krt_proto *p UNUSED, net *n, rte *new, rte *old,
|
||||||
|
struct ea_list *eattrs UNUSED)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
@ -612,7 +612,7 @@ nh_bufsize(struct mpnh *nh)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nl_send_route(struct krt_proto *p, rte *e, int new)
|
nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new)
|
||||||
{
|
{
|
||||||
eattr *ea;
|
eattr *ea;
|
||||||
net *net = e->net;
|
net *net = e->net;
|
||||||
@ -639,13 +639,18 @@ nl_send_route(struct krt_proto *p, rte *e, int new)
|
|||||||
r.r.rtm_scope = RT_SCOPE_UNIVERSE;
|
r.r.rtm_scope = RT_SCOPE_UNIVERSE;
|
||||||
nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix);
|
nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix);
|
||||||
|
|
||||||
if (ea = ea_find(a->eattrs, EA_KRT_METRIC))
|
u32 metric = 0;
|
||||||
nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, ea->u.data);
|
if (new && e->attrs->source == RTS_INHERIT)
|
||||||
|
metric = e->u.krt.metric;
|
||||||
|
if (ea = ea_find(eattrs, EA_KRT_METRIC))
|
||||||
|
metric = ea->u.data;
|
||||||
|
if (metric != 0)
|
||||||
|
nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, metric);
|
||||||
|
|
||||||
if (ea = ea_find(a->eattrs, EA_KRT_PREFSRC))
|
if (ea = ea_find(eattrs, EA_KRT_PREFSRC))
|
||||||
nl_add_attr_ipa(&r.h, sizeof(r), RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
|
nl_add_attr_ipa(&r.h, sizeof(r), RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
|
||||||
|
|
||||||
if (ea = ea_find(a->eattrs, EA_KRT_REALM))
|
if (ea = ea_find(eattrs, EA_KRT_REALM))
|
||||||
nl_add_attr_u32(&r.h, sizeof(r), RTA_FLOW, ea->u.data);
|
nl_add_attr_u32(&r.h, sizeof(r), RTA_FLOW, ea->u.data);
|
||||||
|
|
||||||
switch (a->dest)
|
switch (a->dest)
|
||||||
@ -686,15 +691,22 @@ nl_send_route(struct krt_proto *p, rte *e, int new)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
krt_set_notify(struct krt_proto *p, net *n, rte *new, rte *old)
|
krt_set_notify(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NULL for eattr of the old route is a little hack, but we don't
|
||||||
|
* get proper eattrs for old in rt_notify() anyway. NULL means no
|
||||||
|
* extended route attributes and therefore matches if the kernel
|
||||||
|
* route has any of them.
|
||||||
|
*/
|
||||||
|
|
||||||
if (old)
|
if (old)
|
||||||
nl_send_route(p, old, 0);
|
nl_send_route(p, old, NULL, 0);
|
||||||
|
|
||||||
if (new)
|
if (new)
|
||||||
err = nl_send_route(p, new, 1);
|
err = nl_send_route(p, new, eattrs, 1);
|
||||||
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
n->n.flags |= KRF_SYNC_ERROR;
|
n->n.flags |= KRF_SYNC_ERROR;
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include "nest/iface.h"
|
#include "nest/iface.h"
|
||||||
#include "nest/route.h"
|
#include "nest/route.h"
|
||||||
#include "nest/protocol.h"
|
#include "nest/protocol.h"
|
||||||
|
#include "filter/filter.h"
|
||||||
#include "lib/timer.h"
|
#include "lib/timer.h"
|
||||||
#include "conf/conf.h"
|
#include "conf/conf.h"
|
||||||
#include "lib/string.h"
|
#include "lib/string.h"
|
||||||
@ -53,18 +54,18 @@
|
|||||||
#include "unix.h"
|
#include "unix.h"
|
||||||
#include "krt.h"
|
#include "krt.h"
|
||||||
|
|
||||||
static int krt_uptodate(rte *k, rte *e);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Global resources
|
* Global resources
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pool *krt_pool;
|
pool *krt_pool;
|
||||||
|
static linpool *krt_filter_lp;
|
||||||
|
|
||||||
void
|
void
|
||||||
krt_io_init(void)
|
krt_io_init(void)
|
||||||
{
|
{
|
||||||
krt_pool = rp_new(&root_pool, "Kernel Syncer");
|
krt_pool = rp_new(&root_pool, "Kernel Syncer");
|
||||||
|
krt_filter_lp = lp_new(krt_pool, 4080);
|
||||||
krt_if_io_init();
|
krt_if_io_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,12 +279,30 @@ krt_trace_in_rl(struct rate_limit *rl, struct krt_proto *p, rte *e, char *msg)
|
|||||||
|
|
||||||
static struct rate_limit rl_alien_seen, rl_alien_updated, rl_alien_created, rl_alien_ignored;
|
static struct rate_limit rl_alien_seen, rl_alien_updated, rl_alien_created, rl_alien_ignored;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* krt_same_key() specifies what (aside from the net) is the key in
|
||||||
|
* kernel routing tables. It should be OS-dependent, this is for
|
||||||
|
* Linux. It is important for asynchronous alien updates, because a
|
||||||
|
* positive update is implicitly a negative one for any old route with
|
||||||
|
* the same key.
|
||||||
|
*/
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
krt_same_key(rte *a, rte *b)
|
krt_same_key(rte *a, rte *b)
|
||||||
{
|
{
|
||||||
return a->u.krt.proto == b->u.krt.proto &&
|
return a->u.krt.metric == b->u.krt.metric;
|
||||||
a->u.krt.metric == b->u.krt.metric &&
|
}
|
||||||
a->u.krt.type == b->u.krt.type;
|
|
||||||
|
static inline int
|
||||||
|
krt_uptodate(rte *a, rte *b)
|
||||||
|
{
|
||||||
|
if (a->attrs != b->attrs)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (a->u.krt.proto != b->u.krt.proto)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -308,6 +327,7 @@ krt_learn_announce_delete(struct krt_proto *p, net *n)
|
|||||||
rte_update(p->p.table, n, &p->p, &p->p, NULL);
|
rte_update(p->p.table, n, &p->p, &p->p, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called when alien route is discovered during scan */
|
||||||
static void
|
static void
|
||||||
krt_learn_scan(struct krt_proto *p, rte *e)
|
krt_learn_scan(struct krt_proto *p, rte *e)
|
||||||
{
|
{
|
||||||
@ -315,7 +335,7 @@ krt_learn_scan(struct krt_proto *p, rte *e)
|
|||||||
net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen);
|
net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen);
|
||||||
rte *m, **mm;
|
rte *m, **mm;
|
||||||
|
|
||||||
e->attrs->source = RTS_INHERIT;
|
e->attrs = rta_lookup(e->attrs);
|
||||||
|
|
||||||
for(mm=&n->routes; m = *mm; mm=&m->next)
|
for(mm=&n->routes; m = *mm; mm=&m->next)
|
||||||
if (krt_same_key(m, e))
|
if (krt_same_key(m, e))
|
||||||
@ -340,7 +360,6 @@ krt_learn_scan(struct krt_proto *p, rte *e)
|
|||||||
krt_trace_in_rl(&rl_alien_created, p, e, "[alien] created");
|
krt_trace_in_rl(&rl_alien_created, p, e, "[alien] created");
|
||||||
if (!m)
|
if (!m)
|
||||||
{
|
{
|
||||||
e->attrs = rta_lookup(e->attrs);
|
|
||||||
e->next = n->routes;
|
e->next = n->routes;
|
||||||
n->routes = e;
|
n->routes = e;
|
||||||
e->u.krt.seen = 1;
|
e->u.krt.seen = 1;
|
||||||
@ -416,7 +435,7 @@ krt_learn_async(struct krt_proto *p, rte *e, int new)
|
|||||||
net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen);
|
net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen);
|
||||||
rte *g, **gg, *best, **bestp, *old_best;
|
rte *g, **gg, *best, **bestp, *old_best;
|
||||||
|
|
||||||
e->attrs->source = RTS_INHERIT;
|
e->attrs = rta_lookup(e->attrs);
|
||||||
|
|
||||||
old_best = n->routes;
|
old_best = n->routes;
|
||||||
for(gg=&n->routes; g = *gg; gg = &g->next)
|
for(gg=&n->routes; g = *gg; gg = &g->next)
|
||||||
@ -438,7 +457,7 @@ krt_learn_async(struct krt_proto *p, rte *e, int new)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
krt_trace_in(p, e, "[alien async] created");
|
krt_trace_in(p, e, "[alien async] created");
|
||||||
e->attrs = rta_lookup(e->attrs);
|
|
||||||
e->next = n->routes;
|
e->next = n->routes;
|
||||||
n->routes = e;
|
n->routes = e;
|
||||||
}
|
}
|
||||||
@ -538,7 +557,8 @@ krt_flush_routes(struct krt_proto *p)
|
|||||||
if ((n->n.flags & KRF_INSTALLED) &&
|
if ((n->n.flags & KRF_INSTALLED) &&
|
||||||
a->source != RTS_DEVICE && a->source != RTS_INHERIT)
|
a->source != RTS_DEVICE && a->source != RTS_INHERIT)
|
||||||
{
|
{
|
||||||
krt_set_notify(p, e->net, NULL, e);
|
/* FIXME: this does not work if gw is changed in export filter */
|
||||||
|
krt_set_notify(p, e->net, NULL, e, NULL);
|
||||||
n->n.flags &= ~KRF_INSTALLED;
|
n->n.flags &= ~KRF_INSTALLED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -547,7 +567,7 @@ krt_flush_routes(struct krt_proto *p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
krt_uptodate(rte *k, rte *e)
|
krt_same_dest(rte *k, rte *e)
|
||||||
{
|
{
|
||||||
rta *ka = k->attrs, *ea = e->attrs;
|
rta *ka = k->attrs, *ea = e->attrs;
|
||||||
|
|
||||||
@ -559,6 +579,8 @@ krt_uptodate(rte *k, rte *e)
|
|||||||
return ipa_equal(ka->gw, ea->gw);
|
return ipa_equal(ka->gw, ea->gw);
|
||||||
case RTD_DEVICE:
|
case RTD_DEVICE:
|
||||||
return !strcmp(ka->iface->name, ea->iface->name);
|
return !strcmp(ka->iface->name, ea->iface->name);
|
||||||
|
case RTD_MULTIPATH:
|
||||||
|
return mpnh_same(ka->nexthops, ea->nexthops);
|
||||||
default:
|
default:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -611,10 +633,12 @@ 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) && old)
|
||||||
{
|
{
|
||||||
if (krt_uptodate(e, old))
|
/* There may be changes in route attributes, we ignore that.
|
||||||
verdict = KRF_SEEN;
|
Also, this does not work well if gw is changed in export filter */
|
||||||
else
|
if ((net->n.flags & KRF_SYNC_ERROR) || ! krt_same_dest(e, old))
|
||||||
verdict = KRF_UPDATE;
|
verdict = KRF_UPDATE;
|
||||||
|
else
|
||||||
|
verdict = KRF_SEEN;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
verdict = KRF_DELETE;
|
verdict = KRF_DELETE;
|
||||||
@ -624,7 +648,7 @@ krt_got_route(struct krt_proto *p, rte *e)
|
|||||||
net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict;
|
net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict;
|
||||||
if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
|
if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
|
||||||
{
|
{
|
||||||
/* Get a cached copy of attributes and link the route */
|
/* Get a cached copy of attributes and temporarily link the route */
|
||||||
rta *a = e->attrs;
|
rta *a = e->attrs;
|
||||||
a->source = RTS_DUMMY;
|
a->source = RTS_DUMMY;
|
||||||
e->attrs = rta_lookup(a);
|
e->attrs = rta_lookup(a);
|
||||||
@ -635,6 +659,25 @@ krt_got_route(struct krt_proto *p, rte *e)
|
|||||||
rte_free(e);
|
rte_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
krt_export_rte(struct krt_proto *p, rte **new, ea_list **tmpa)
|
||||||
|
{
|
||||||
|
struct filter *filter = p->p.out_filter;
|
||||||
|
|
||||||
|
if (! *new)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (filter == FILTER_REJECT)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (filter == FILTER_ACCEPT)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
struct proto *src = (*new)->attrs->proto;
|
||||||
|
*tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(*new, krt_filter_lp) : NULL;
|
||||||
|
return f_run(filter, new, tmpa, krt_filter_lp, FF_FORCE_TMPATTR) <= F_ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
krt_prune(struct krt_proto *p)
|
krt_prune(struct krt_proto *p)
|
||||||
{
|
{
|
||||||
@ -645,16 +688,28 @@ krt_prune(struct krt_proto *p)
|
|||||||
{
|
{
|
||||||
net *n = (net *) f;
|
net *n = (net *) f;
|
||||||
int verdict = f->flags & KRF_VERDICT_MASK;
|
int verdict = f->flags & KRF_VERDICT_MASK;
|
||||||
rte *new, *old;
|
rte *new, *new0, *old;
|
||||||
|
ea_list *tmpa = NULL;
|
||||||
|
|
||||||
if (verdict != KRF_CREATE && verdict != KRF_SEEN && verdict != KRF_IGNORE)
|
if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
|
||||||
{
|
{
|
||||||
|
/* Get a dummy route from krt_got_route() */
|
||||||
old = n->routes;
|
old = n->routes;
|
||||||
n->routes = old->next;
|
n->routes = old->next;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
old = NULL;
|
old = NULL;
|
||||||
new = n->routes;
|
|
||||||
|
new = new0 = n->routes;
|
||||||
|
if (verdict == KRF_CREATE || verdict == KRF_UPDATE)
|
||||||
|
{
|
||||||
|
/* We have to run export filter to get proper 'new' route */
|
||||||
|
if (! krt_export_rte(p, &new, &tmpa))
|
||||||
|
{
|
||||||
|
/* Route rejected, should not happen (KRF_INSTALLED) but to be sure .. */
|
||||||
|
verdict = (verdict == KRF_CREATE) ? KRF_IGNORE : KRF_DELETE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (verdict)
|
switch (verdict)
|
||||||
{
|
{
|
||||||
@ -662,7 +717,7 @@ krt_prune(struct krt_proto *p)
|
|||||||
if (new && (f->flags & KRF_INSTALLED))
|
if (new && (f->flags & KRF_INSTALLED))
|
||||||
{
|
{
|
||||||
krt_trace_in(p, new, "reinstalling");
|
krt_trace_in(p, new, "reinstalling");
|
||||||
krt_set_notify(p, n, new, NULL);
|
krt_set_notify(p, n, new, NULL, tmpa);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case KRF_SEEN:
|
case KRF_SEEN:
|
||||||
@ -671,17 +726,21 @@ krt_prune(struct krt_proto *p)
|
|||||||
break;
|
break;
|
||||||
case KRF_UPDATE:
|
case KRF_UPDATE:
|
||||||
krt_trace_in(p, new, "updating");
|
krt_trace_in(p, new, "updating");
|
||||||
krt_set_notify(p, n, new, old);
|
krt_set_notify(p, n, new, old, tmpa);
|
||||||
break;
|
break;
|
||||||
case KRF_DELETE:
|
case KRF_DELETE:
|
||||||
krt_trace_in(p, old, "deleting");
|
krt_trace_in(p, old, "deleting");
|
||||||
krt_set_notify(p, n, NULL, old);
|
krt_set_notify(p, n, NULL, old, NULL);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
bug("krt_prune: invalid route status");
|
bug("krt_prune: invalid route status");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (old)
|
if (old)
|
||||||
rte_free(old);
|
rte_free(old);
|
||||||
|
if (new != new0)
|
||||||
|
rte_free(new);
|
||||||
|
lp_flush(krt_filter_lp);
|
||||||
f->flags &= ~KRF_VERDICT_MASK;
|
f->flags &= ~KRF_VERDICT_MASK;
|
||||||
}
|
}
|
||||||
FIB_WALK_END;
|
FIB_WALK_END;
|
||||||
@ -707,7 +766,7 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new)
|
|||||||
if (new)
|
if (new)
|
||||||
{
|
{
|
||||||
krt_trace_in(p, e, "[redirect] deleting");
|
krt_trace_in(p, e, "[redirect] deleting");
|
||||||
krt_set_notify(p, net, NULL, e);
|
krt_set_notify(p, net, NULL, e, NULL);
|
||||||
}
|
}
|
||||||
/* If !new, it is probably echo of our deletion */
|
/* If !new, it is probably echo of our deletion */
|
||||||
break;
|
break;
|
||||||
@ -781,7 +840,7 @@ krt_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
krt_notify(struct proto *P, struct rtable *table UNUSED, net *net,
|
krt_notify(struct proto *P, struct rtable *table UNUSED, net *net,
|
||||||
rte *new, rte *old, struct ea_list *attrs UNUSED)
|
rte *new, rte *old, struct ea_list *eattrs)
|
||||||
{
|
{
|
||||||
struct krt_proto *p = (struct krt_proto *) P;
|
struct krt_proto *p = (struct krt_proto *) P;
|
||||||
|
|
||||||
@ -794,7 +853,7 @@ krt_notify(struct proto *P, struct rtable *table UNUSED, net *net,
|
|||||||
else
|
else
|
||||||
net->n.flags &= ~KRF_INSTALLED;
|
net->n.flags &= ~KRF_INSTALLED;
|
||||||
if (p->initialized) /* Before first scan we don't touch the routes */
|
if (p->initialized) /* Before first scan we don't touch the routes */
|
||||||
krt_set_notify(p, net, new, old);
|
krt_set_notify(p, net, new, old, eattrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -908,7 +967,7 @@ krt_shutdown(struct proto *P)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct ea_list *
|
static struct ea_list *
|
||||||
krt_make_tmp_attrs(struct rte *rt, struct linpool *pool)
|
krt_make_tmp_attrs(rte *rt, struct linpool *pool)
|
||||||
{
|
{
|
||||||
struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr));
|
struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr));
|
||||||
|
|
||||||
@ -930,12 +989,18 @@ krt_make_tmp_attrs(struct rte *rt, struct linpool *pool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
krt_store_tmp_attrs(struct rte *rt, struct ea_list *attrs)
|
krt_store_tmp_attrs(rte *rt, struct ea_list *attrs)
|
||||||
{
|
{
|
||||||
/* EA_KRT_SOURCE is read-only */
|
/* EA_KRT_SOURCE is read-only */
|
||||||
rt->u.krt.metric = ea_get_int(attrs, EA_KRT_METRIC, 0);
|
rt->u.krt.metric = ea_get_int(attrs, EA_KRT_METRIC, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
krt_rte_same(rte *a, rte *b)
|
||||||
|
{
|
||||||
|
/* src is always KRT_SRC_ALIEN and type is irrelevant */
|
||||||
|
return (a->u.krt.proto == b->u.krt.proto) && (a->u.krt.metric == b->u.krt.metric);
|
||||||
|
}
|
||||||
|
|
||||||
static struct proto *
|
static struct proto *
|
||||||
krt_init(struct proto_config *c)
|
krt_init(struct proto_config *c)
|
||||||
@ -947,6 +1012,7 @@ krt_init(struct proto_config *c)
|
|||||||
p->p.store_tmp_attrs = krt_store_tmp_attrs;
|
p->p.store_tmp_attrs = krt_store_tmp_attrs;
|
||||||
p->p.import_control = krt_import_control;
|
p->p.import_control = krt_import_control;
|
||||||
p->p.rt_notify = krt_notify;
|
p->p.rt_notify = krt_notify;
|
||||||
|
p->p.rte_same = krt_rte_same;
|
||||||
|
|
||||||
return &p->p;
|
return &p->p;
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ void krt_set_start(struct krt_proto *, int);
|
|||||||
void krt_set_shutdown(struct krt_proto *, int);
|
void krt_set_shutdown(struct krt_proto *, int);
|
||||||
|
|
||||||
int krt_capable(rte *e);
|
int krt_capable(rte *e);
|
||||||
void krt_set_notify(struct krt_proto *x, net *net, rte *new, rte *old);
|
void krt_set_notify(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs);
|
||||||
|
|
||||||
/* krt-iface.c */
|
/* krt-iface.c */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user