#include "nest/route.h"
#include "nest/protocol.h"
#include "filter/filter.h"
-#include "sysdep/unix/timer.h"
#include "conf/conf.h"
#include "lib/string.h"
+#include "lib/timer.h"
#include "unix.h"
#include "krt.h"
krt_io_init(void)
{
krt_pool = rp_new(&root_pool, "Kernel Syncer");
- krt_filter_lp = lp_new(krt_pool, 4080);
+ krt_filter_lp = lp_new_default(krt_pool);
init_list(&krt_proto_list);
krt_sys_io_init();
}
struct kif_proto *kif_proto;
static struct kif_config *kif_cf;
static timer *kif_scan_timer;
-static bird_clock_t kif_last_shot;
+static btime kif_last_shot;
+
+static struct kif_iface_config kif_default_iface = {};
+
+struct kif_iface_config *
+kif_get_iface_config(struct iface *iface)
+{
+ struct kif_config *cf = (void *) (kif_proto->p.cf);
+ struct kif_iface_config *ic = (void *) iface_patt_find(&cf->iface_list, iface, NULL);
+ return ic ?: &kif_default_iface;
+}
static void
kif_scan(timer *t)
struct kif_proto *p = t->data;
KRT_TRACE(p, D_EVENTS, "Scanning interfaces");
- kif_last_shot = now;
+ kif_last_shot = current_time();
kif_do_scan(p);
}
static void
kif_force_scan(void)
{
- if (kif_proto && kif_last_shot + 2 < now)
+ if (kif_proto && ((kif_last_shot + 2 S) < current_time()))
{
kif_scan(kif_scan_timer);
tm_start(kif_scan_timer, ((struct kif_config *) kif_proto->p.cf)->scan_time);
void
kif_request_scan(void)
{
- if (kif_proto && kif_scan_timer->expires > now)
- tm_start(kif_scan_timer, 1);
-}
-
-static inline int
-prefer_addr(struct ifa *a, struct ifa *b)
-{
- int sa = a->scope > SCOPE_LINK;
- int sb = b->scope > SCOPE_LINK;
-
- if (sa < sb)
- return 0;
- else if (sa > sb)
- return 1;
- else
- return ipa_compare(a->ip, b->ip) < 0;
-}
-
-static inline struct ifa *
-find_preferred_ifa(struct iface *i, const net_addr *n)
-{
- struct ifa *a, *b = NULL;
-
- WALK_LIST(a, i->addrs)
- {
- if (!(a->flags & IA_SECONDARY) &&
- (!n || ipa_in_netX(a->ip, n)) &&
- (!b || prefer_addr(a, b)))
- b = a;
- }
-
- return b;
-}
-
-struct ifa *
-kif_choose_primary(struct iface *i)
-{
- struct kif_config *cf = (struct kif_config *) (kif_proto->p.cf);
- struct kif_primary_item *it;
- struct ifa *a;
-
- WALK_LIST(it, cf->primary)
- {
- if (!it->pattern || patmatch(it->pattern, i->name))
- if (a = find_preferred_ifa(i, &it->addr))
- return a;
- }
-
- if (a = kif_get_primary_ip(i))
- return a;
-
- return find_preferred_ifa(i, NULL);
+ if (kif_proto && (kif_scan_timer->expires > (current_time() + 1 S)))
+ tm_start(kif_scan_timer, 1 S);
}
-
static struct proto *
kif_init(struct proto_config *c)
{
kif_sys_start(p);
/* Start periodic interface scanning */
- kif_scan_timer = tm_new(P->pool);
- kif_scan_timer->hook = kif_scan;
- kif_scan_timer->data = p;
- kif_scan_timer->recurrent = KIF_CF->scan_time;
+ kif_scan_timer = tm_new_init(P->pool, kif_scan, p, KIF_CF->scan_time, 0);
kif_scan(kif_scan_timer);
tm_start(kif_scan_timer, KIF_CF->scan_time);
tm_start(kif_scan_timer, n->scan_time);
}
- if (!EMPTY_LIST(o->primary) || !EMPTY_LIST(n->primary))
+ if (!EMPTY_LIST(o->iface_list) || !EMPTY_LIST(n->iface_list))
{
/* This is hack, we have to update a configuration
* to the new value just now, because it is used
- * for recalculation of primary addresses.
+ * for recalculation of preferred addresses.
*/
p->cf = new;
- ifa_recalc_all_primary_addresses();
+ if_recalc_all_preferred_addresses();
}
return 1;
cf_error("Kernel device protocol already defined");
kif_cf = (struct kif_config *) proto_config_new(&proto_unix_iface, class);
- kif_cf->scan_time = 60;
- init_list(&kif_cf->primary);
+ kif_cf->scan_time = 60 S;
+ init_list(&kif_cf->iface_list);
kif_sys_init_config(kif_cf);
return (struct proto_config *) kif_cf;
struct kif_config *d = (struct kif_config *) dest;
struct kif_config *s = (struct kif_config *) src;
- /* Copy primary addr list */
- cfg_copy_list(&d->primary, &s->primary, sizeof(struct kif_primary_item));
+ /* Copy interface config list */
+ cfg_copy_list(&d->iface_list, &s->iface_list, sizeof(struct kif_iface_config));
/* Fix sysdep parts */
kif_sys_copy_config(d, s);
}
-
struct protocol proto_unix_iface = {
.name = "Device",
.template = "device%d",
+ .class = PROTOCOL_DEVICE,
.proto_size = sizeof(struct kif_proto),
.config_size = sizeof(struct kif_config),
.preconfig = kif_preconfig,
net *n = e->net;
rta *aa = rta_clone(e->attrs);
rte *ee = rte_get_temp(aa);
- ee->pflags = 0;
+ ee->pflags = EA_ID_FLAG(EA_KRT_SOURCE) | EA_ID_FLAG(EA_KRT_METRIC);
ee->u.krt = e->u.krt;
rte_update(&p->p, n->n.addr, ee);
}
krt_learn_init(struct krt_proto *p)
{
if (KRT_CF->learn)
- rt_setup(p->p.pool, &p->krt_table, "Inherited", NULL);
+ {
+ struct rtable_config *cf = mb_allocz(p->p.pool, sizeof(struct rtable_config));
+ cf->name = "Inherited";
+ cf->addr_type = p->p.net_type;
+
+ rt_setup(p->p.pool, &p->krt_table, cf);
+ }
}
static void
* Routes
*/
+static inline int
+krt_is_installed(struct krt_proto *p, net *n)
+{
+ return n->routes && bmap_test(&p->p.main_channel->export_map, n->routes->id);
+}
+
static void
krt_flush_routes(struct krt_proto *p)
{
KRT_TRACE(p, D_EVENTS, "Flushing kernel routes");
FIB_WALK(&t->fib, net, n)
{
- rte *e = n->routes;
- if (rte_is_valid(e) && (n->n.flags & KRF_INSTALLED))
+ if (krt_is_installed(p, n))
{
/* FIXME: this does not work if gw is changed in export filter */
- krt_replace_rte(p, e->net, NULL, e, NULL);
- n->n.flags &= ~KRF_INSTALLED;
+ krt_replace_rte(p, n, NULL, n->routes);
}
}
FIB_WALK_END;
}
static struct rte *
-krt_export_net(struct krt_proto *p, net *net, rte **rt_free, ea_list **tmpa)
+krt_export_net(struct krt_proto *p, net *net, rte **rt_free)
{
struct channel *c = p->p.main_channel;
- struct filter *filter = c->out_filter;
+ const struct filter *filter = c->out_filter;
rte *rt;
if (c->ra_mode == RA_MERGED)
- return rt_export_merged(c, net, rt_free, tmpa, krt_filter_lp, 1);
+ return rt_export_merged(c, net, rt_free, krt_filter_lp, 1);
rt = net->routes;
*rt_free = NULL;
if (filter == FILTER_REJECT)
return NULL;
- struct proto *src = rt->attrs->src->proto;
- *tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(rt, krt_filter_lp) : NULL;
+ rte_make_tmp_attrs(&rt, krt_filter_lp, NULL);
- /* We could run krt_import_control() here, but it is already handled by KRF_INSTALLED */
+ /* We could run krt_preexport() here, but it is already handled by krt_is_installed() */
if (filter == FILTER_ACCEPT)
goto accept;
- if (f_run(filter, &rt, tmpa, krt_filter_lp, FF_FORCE_TMPATTR) > F_ACCEPT)
+ if (f_run(filter, &rt, krt_filter_lp, FF_SILENT) > F_ACCEPT)
goto reject;
if (ka->dest != ea->dest)
return 0;
- switch (ka->dest)
- {
- case RTD_ROUTER:
- return ipa_equal(ka->gw, ea->gw);
- case RTD_DEVICE:
- return !strcmp(ka->iface->name, ea->iface->name);
- case RTD_MULTIPATH:
- return mpnh_same(ka->nexthops, ea->nexthops);
- default:
- return 1;
- }
+
+ if (ka->dest == RTD_UNICAST)
+ return nexthop_same(&(ka->nh), &(ea->nh));
+
+ return 1;
}
/*
void
krt_got_route(struct krt_proto *p, rte *e)
{
- net *net = e->net;
- int verdict;
+ rte *new = NULL, *rt_free = NULL;
+ net *n = e->net;
#ifdef KRT_ALLOW_LEARN
switch (e->u.krt.src)
{
case KRT_SRC_KERNEL:
- verdict = KRF_IGNORE;
- goto sentenced;
+ goto ignore;
case KRT_SRC_REDIRECT:
- verdict = KRF_DELETE;
- goto sentenced;
+ goto delete;
case KRT_SRC_ALIEN:
if (KRT_CF->learn)
#endif
/* The rest is for KRT_SRC_BIRD (or KRT_SRC_UNKNOWN) */
- if (net->n.flags & KRF_VERDICT_MASK)
- {
- /* Route to this destination was already seen. Strange, but it happens... */
- krt_trace_in(p, e, "already seen");
- rte_free(e);
- return;
- }
+ /* We wait for the initial feed to have correct installed state */
if (!p->ready)
- {
- /* We wait for the initial feed to have correct KRF_INSTALLED flag */
- verdict = KRF_IGNORE;
- goto sentenced;
- }
+ goto ignore;
- if (net->n.flags & KRF_INSTALLED)
- {
- rte *new, *rt_free;
- ea_list *tmpa;
+ if (!krt_is_installed(p, n))
+ goto delete;
- new = krt_export_net(p, net, &rt_free, &tmpa);
+ new = krt_export_net(p, n, &rt_free);
- /* TODO: There also may be changes in route eattrs, we ignore that for now. */
+ /* Rejected by filters */
+ if (!new)
+ goto delete;
- if (!new)
- verdict = KRF_DELETE;
- else if ((net->n.flags & KRF_SYNC_ERROR) || !krt_same_dest(e, new))
- verdict = KRF_UPDATE;
- else
- verdict = KRF_SEEN;
+ /* Route to this destination was already seen. Strange, but it happens... */
+ if (bmap_test(&p->seen_map, new->id))
+ goto aseen;
- if (rt_free)
- rte_free(rt_free);
+ /* Mark route as seen */
+ bmap_set(&p->seen_map, new->id);
- lp_flush(krt_filter_lp);
- }
- else
- verdict = KRF_DELETE;
+ /* TODO: There also may be changes in route eattrs, we ignore that for now. */
+ if (!bmap_test(&p->sync_map, new->id) || !krt_same_dest(e, new))
+ goto update;
- sentenced:
- krt_trace_in(p, e, ((char *[]) { "?", "seen", "will be updated", "will be removed", "ignored" }) [verdict]);
- net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict;
- if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
- {
- /* Get a cached copy of attributes and temporarily link the route */
- rta *a = e->attrs;
- a->source = RTS_DUMMY;
- e->attrs = rta_lookup(a);
- e->next = net->routes;
- net->routes = e;
- }
- else
- rte_free(e);
+ goto seen;
+
+seen:
+ krt_trace_in(p, e, "seen");
+ goto done;
+
+aseen:
+ krt_trace_in(p, e, "already seen");
+ goto done;
+
+ignore:
+ krt_trace_in(p, e, "ignored");
+ goto done;
+
+update:
+ krt_trace_in(p, new, "updating");
+ krt_replace_rte(p, n, new, e);
+ goto done;
+
+delete:
+ krt_trace_in(p, e, "deleting");
+ krt_replace_rte(p, n, NULL, e);
+ goto done;
+
+done:
+ rte_free(e);
+
+ if (rt_free)
+ rte_free(rt_free);
+
+ lp_flush(krt_filter_lp);
+}
+
+static void
+krt_init_scan(struct krt_proto *p)
+{
+ bmap_reset(&p->seen_map, 1024);
}
static void
KRT_TRACE(p, D_EVENTS, "Pruning table %s", t->name);
FIB_WALK(&t->fib, net, n)
+ {
+ if (p->ready && krt_is_installed(p, n) && !bmap_test(&p->seen_map, n->routes->id))
{
- int verdict = n->n.flags & KRF_VERDICT_MASK;
- rte *new, *old, *rt_free = NULL;
- ea_list *tmpa = NULL;
-
- if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
- {
- /* Get a dummy route from krt_got_route() */
- old = n->routes;
- n->routes = old->next;
- }
- else
- old = NULL;
-
- if (verdict == KRF_CREATE || verdict == KRF_UPDATE)
- {
- /* We have to run export filter to get proper 'new' route */
- new = krt_export_net(p, n, &rt_free, &tmpa);
+ rte *rt_free = NULL;
+ rte *new = krt_export_net(p, n, &rt_free);
- if (!new)
- verdict = (verdict == KRF_CREATE) ? KRF_IGNORE : KRF_DELETE;
- else
- tmpa = ea_append(tmpa, new->attrs->eattrs);
- }
- else
- new = NULL;
-
- switch (verdict)
- {
- case KRF_CREATE:
- if (new && (n->n.flags & KRF_INSTALLED))
- {
- krt_trace_in(p, new, "reinstalling");
- krt_replace_rte(p, n, new, NULL, tmpa);
- }
- break;
- case KRF_SEEN:
- case KRF_IGNORE:
- /* Nothing happens */
- break;
- case KRF_UPDATE:
- krt_trace_in(p, new, "updating");
- krt_replace_rte(p, n, new, old, tmpa);
- break;
- case KRF_DELETE:
- krt_trace_in(p, old, "deleting");
- krt_replace_rte(p, n, NULL, old, NULL);
- break;
- default:
- bug("krt_prune: invalid route status");
- }
+ if (new)
+ {
+ krt_trace_in(p, new, "installing");
+ krt_replace_rte(p, n, new, NULL);
+ }
- if (old)
- rte_free(old);
if (rt_free)
rte_free(rt_free);
+
lp_flush(krt_filter_lp);
- n->n.flags &= ~KRF_VERDICT_MASK;
}
+ }
FIB_WALK_END;
#ifdef KRT_ALLOW_LEARN
switch (e->u.krt.src)
{
case KRT_SRC_BIRD:
- ASSERT(0); /* Should be filtered by the back end */
+ /* Should be filtered by the back end */
+ bug("BIRD originated routes should not get here.");
case KRT_SRC_REDIRECT:
if (new)
{
krt_trace_in(p, e, "[redirect] deleting");
- krt_replace_rte(p, net, NULL, e, NULL);
+ krt_replace_rte(p, net, NULL, e);
}
/* If !new, it is probably echo of our deletion */
break;
krt_scan(timer *t UNUSED)
{
struct krt_proto *p;
+ node *n;
kif_force_scan();
p = SKIP_BACK(struct krt_proto, krt_node, HEAD(krt_proto_list));
KRT_TRACE(p, D_EVENTS, "Scanning routing table");
+ WALK_LIST2(p, n, krt_proto_list, krt_node)
+ krt_init_scan(p);
+
krt_do_scan(NULL);
- void *q;
- WALK_LIST(q, krt_proto_list)
- {
- p = SKIP_BACK(struct krt_proto, krt_node, q);
+ WALK_LIST2(p, n, krt_proto_list, krt_node)
krt_prune(p);
- }
}
static void
krt_scan_timer_start(struct krt_proto *p)
{
if (!krt_scan_count)
- krt_scan_timer = tm_new_set(krt_pool, krt_scan, NULL, 0, KRT_CF->scan_time);
+ krt_scan_timer = tm_new_init(krt_pool, krt_scan, NULL, KRT_CF->scan_time, 0);
krt_scan_count++;
- tm_start(krt_scan_timer, 1);
+ tm_start(krt_scan_timer, 1 S);
}
static void
-krt_scan_timer_stop(struct krt_proto *p)
+krt_scan_timer_stop(struct krt_proto *p UNUSED)
{
krt_scan_count--;
kif_force_scan();
KRT_TRACE(p, D_EVENTS, "Scanning routing table");
+ krt_init_scan(p);
krt_do_scan(p);
krt_prune(p);
}
static void
krt_scan_timer_start(struct krt_proto *p)
{
- p->scan_timer = tm_new_set(p->p.pool, krt_scan, p, 0, KRT_CF->scan_time);
- tm_start(p->scan_timer, 1);
+ p->scan_timer = tm_new_init(p->p.pool, krt_scan, p, KRT_CF->scan_time, 0);
+ tm_start(p->scan_timer, 1 S);
}
static void
* Updates
*/
-static struct ea_list *
-krt_make_tmp_attrs(rte *rt, struct linpool *pool)
+static void
+krt_make_tmp_attrs(struct rte *rt, struct linpool *pool)
{
- struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr));
-
- l->next = NULL;
- l->flags = EALF_SORTED;
- l->count = 2;
-
- l->attrs[0].id = EA_KRT_SOURCE;
- l->attrs[0].flags = 0;
- l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP;
- l->attrs[0].u.data = rt->u.krt.proto;
-
- l->attrs[1].id = EA_KRT_METRIC;
- l->attrs[1].flags = 0;
- l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP;
- l->attrs[1].u.data = rt->u.krt.metric;
-
- return l;
+ rte_init_tmp_attrs(rt, pool, 2);
+ rte_make_tmp_attr(rt, EA_KRT_SOURCE, EAF_TYPE_INT, rt->u.krt.proto);
+ rte_make_tmp_attr(rt, EA_KRT_METRIC, EAF_TYPE_INT, rt->u.krt.metric);
}
static void
-krt_store_tmp_attrs(rte *rt, struct ea_list *attrs)
+krt_store_tmp_attrs(struct rte *rt, struct linpool *pool)
{
- /* EA_KRT_SOURCE is read-only */
- rt->u.krt.metric = ea_get_int(attrs, EA_KRT_METRIC, 0);
+ rte_init_tmp_attrs(rt, pool, 2);
+ rt->u.krt.proto = rte_store_tmp_attr(rt, EA_KRT_SOURCE);
+ rt->u.krt.metric = rte_store_tmp_attr(rt, EA_KRT_METRIC);
}
static int
-krt_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool)
+krt_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
{
- struct krt_proto *p = (struct krt_proto *) P;
+ // struct krt_proto *p = (struct krt_proto *) P;
rte *e = *new;
if (e->attrs->src->proto == P)
- {
-#ifdef CONFIG_SINGLE_ROUTE
- /*
- * Implicit withdraw - when the imported kernel route becomes the best one,
- * we know that the previous one exported to the kernel was already removed,
- * but if we processed the update as usual, we would send withdraw to the
- * kernel, which would remove the new imported route instead.
- *
- * We will remove KRT_INSTALLED flag, which stops such withdraw to be
- * processed in krt_rt_notify() and krt_replace_rte().
- */
- if (e == e->net->routes)
- e->net->n.flags &= ~KRF_INSTALLED;
-#endif
- return -1;
- }
-
- if (!KRT_CF->devroutes &&
- (e->attrs->dest == RTD_DEVICE) &&
- (e->attrs->source != RTS_STATIC_DEVICE))
return -1;
if (!krt_capable(e))
static void
krt_rt_notify(struct proto *P, struct channel *ch UNUSED, net *net,
- rte *new, rte *old, struct ea_list *eattrs)
+ rte *new, rte *old)
{
struct krt_proto *p = (struct krt_proto *) P;
if (config->shutdown)
return;
- if (!(net->n.flags & KRF_INSTALLED))
- old = NULL;
- if (new)
- net->n.flags |= KRF_INSTALLED;
- else
- net->n.flags &= ~KRF_INSTALLED;
+
+#ifdef CONFIG_SINGLE_ROUTE
+ /*
+ * Implicit withdraw - when the imported kernel route becomes the best one,
+ * we know that the previous one exported to the kernel was already removed,
+ * but if we processed the update as usual, we would send withdraw to the
+ * kernel, which would remove the new imported route instead.
+ */
+ rte *best = net->routes;
+ if (!new && best && (best->attrs->src->proto == P))
+ return;
+#endif
+
if (p->initialized) /* Before first scan we don't touch the routes */
- krt_replace_rte(p, net, new, old, eattrs);
+ krt_replace_rte(p, net, new, old);
}
static void
cf_error("All kernel syncers must use the same table scan interval");
#endif
- struct rtable_config *tab = proto_cf_main_channel(CF)->table;
+ struct channel_config *cc = proto_cf_main_channel(CF);
+ struct rtable_config *tab = cc->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;
+ if (cf->merge_paths)
+ {
+ cc->ra_mode = RA_MERGED;
+ cc->merge_limit = cf->merge_paths;
+ }
+
krt_sys_postconfig(cf);
}
p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(CF));
- p->p.import_control = krt_import_control;
+ p->p.preexport = krt_preexport;
p->p.rt_notify = krt_rt_notify;
p->p.if_notify = krt_if_notify;
p->p.reload_routes = krt_reload_routes;
switch (p->p.net_type)
{
- case NET_IP4: p->af = AF_INET; break;
- case NET_IP6: p->af = AF_INET6; break;
- default: ASSERT(0);
+ case NET_IP4: p->af = AF_INET; break;
+ case NET_IP6: p->af = AF_INET6; break;
+ case NET_IP6_SADR: p->af = AF_INET6; break;
+#ifdef AF_MPLS
+ case NET_MPLS: p->af = AF_MPLS; break;
+#endif
+ default: log(L_ERR "KRT: Tried to start with strange net type: %d", p->p.net_type); return PS_START; break;
}
+ bmap_init(&p->sync_map, p->p.pool, 1024);
+ bmap_init(&p->seen_map, p->p.pool, 1024);
add_tail(&krt_proto_list, &p->krt_node);
#ifdef KRT_ALLOW_LEARN
krt_scan_timer_stop(p);
/* FIXME we should flush routes even when persist during reconfiguration */
- if (p->initialized && !KRT_CF->persist)
+ if (p->initialized && !KRT_CF->persist && (P->down_code != PDC_CMD_GR_DOWN))
krt_flush_routes(p);
p->ready = 0;
krt_sys_shutdown(p);
rem_node(&p->krt_node);
+ bmap_free(&p->sync_map);
return PS_DOWN;
}
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;
+ return o->scan_time == n->scan_time && o->learn == n->learn;
}
struct proto_config *
#endif
krt_cf = (struct krt_config *) proto_config_new(&proto_unix_kernel, class);
- krt_cf->scan_time = 60;
+ krt_cf->scan_time = 60 S;
krt_sys_init_config(krt_cf);
return (struct proto_config *) krt_cf;
}
+#ifdef CONFIG_IP6_SADR_KERNEL
+#define MAYBE_IP6_SADR NB_IP6_SADR
+#else
+#define MAYBE_IP6_SADR 0
+#endif
+
+#ifdef HAVE_MPLS_KERNEL
+#define MAYBE_MPLS NB_MPLS
+#else
+#define MAYBE_MPLS 0
+#endif
+
struct protocol proto_unix_kernel = {
.name = "Kernel",
.template = "kernel%d",
- .attr_class = EAP_KRT,
+ .class = PROTOCOL_KERNEL,
.preference = DEF_PREF_INHERITED,
- .channel_mask = NB_IP,
+ .channel_mask = NB_IP | MAYBE_IP6_SADR | MAYBE_MPLS,
.proto_size = sizeof(struct krt_proto),
.config_size = sizeof(struct krt_config),
.preconfig = krt_preconfig,