{
STATIC_ATTR;
ACCESS_RTE;
- struct rta *rta = (*fs->rte)->attrs;
+ ACCESS_EATTRS;
+ struct rta *rta = fs->rte->attrs;
switch (sa.sa_code)
{
- case SA_GW: RESULT(sa.type, ip, rta->nh.gw); break;
- case SA_NET: RESULT(sa.type, net, (*fs->rte)->net->n.addr); break;
- case SA_PROTO: RESULT(sa.type, s, (*fs->rte)->src->proto->name); break;
+ case SA_NET: RESULT(sa.type, net, fs->rte->net); break;
+ case SA_PROTO: RESULT(sa.type, s, fs->rte->src->proto->name); break;
case SA_DEST: RESULT(sa.type, i, rta->dest); break;
- case SA_IFNAME: RESULT(sa.type, s, rta->nh.iface ? rta->nh.iface->name : ""); break;
- case SA_IFINDEX: RESULT(sa.type, i, rta->nh.iface ? rta->nh.iface->index : 0); break;
- case SA_WEIGHT: RESULT(sa.type, i, rta->nh.weight + 1); break;
- case SA_GW_MPLS: RESULT(sa.type, i, rta->nh.labels ? rta->nh.label[0] : MPLS_NULL); break;
-
default:
- bug("Invalid static attribute access (%u/%u)", sa.type, sa.sa_code);
+ {
+ struct eattr *nh_ea = ea_find(*fs->eattrs, &ea_gen_nexthop);
+ struct nexthop *nh = nh_ea ? &((struct nexthop_adata *) nh_ea->u.ptr)->nh : NULL;
+
+ switch (sa.sa_code)
+ {
+ case SA_GW:
+ RESULT(sa.type, ip, nh ? nh->gw : IPA_NONE);
+ break;
+ case SA_IFNAME:
+ RESULT(sa.type, s, (nh && nh->iface) ? nh->iface->name : "");
+ break;
+ case SA_IFINDEX:
+ RESULT(sa.type, i, (nh && nh->iface) ? nh->iface->index : 0);
+ break;
+ case SA_WEIGHT:
+ RESULT(sa.type, i, (nh ? nh->weight : 0) + 1);
+ break;
+ case SA_GW_MPLS:
+ RESULT(sa.type, i, (nh && nh->labels) ? nh->label[0] : MPLS_NULL);
+ break;
+ default:
+ bug("Invalid static attribute access (%u/%u)", sa.type, sa.sa_code);
+ }
+ }
}
}
}
f_rta_cow(fs);
{
- struct rta *rta = (*fs->rte)->attrs;
+ struct rta *rta = fs->rte->attrs;
- switch (sa.sa_code)
+ if (sa.sa_code == SA_DEST)
+ {
+ int i = v1.val.i;
+ if ((i != RTD_BLACKHOLE) && (i != RTD_UNREACHABLE) && (i != RTD_PROHIBIT))
+ runtime( "Destination can be changed only to blackhole, unreachable or prohibit" );
+
+ rta->dest = i;
+ ea_unset_attr(fs->eattrs, 1, &ea_gen_nexthop);
+ }
+ else
{
+ union {
+ struct nexthop_adata nha;
+ struct {
+ struct adata ad;
+ struct nexthop nh;
+ u32 label;
+ };
+ } nha;
+
+ nha.ad = (struct adata) {
+ .length = sizeof (struct nexthop_adata) - sizeof (struct adata),
+ };
+
+ eattr *a = NULL;
+
+ switch (sa.sa_code)
+ {
case SA_GW:
{
+ struct eattr *nh_ea = ea_find(*fs->eattrs, &ea_gen_nexthop);
+
ip_addr ip = v1.val.ip;
- struct iface *ifa = ipa_is_link_local(ip) ? rta->nh.iface : NULL;
+ struct iface *ifa = (ipa_is_link_local(ip) && nh_ea) ?
+ ((struct nexthop_adata *) nh_ea->u.ptr)->nh.iface : NULL;
+
- neighbor *n = neigh_find((*fs->rte)->src->proto, ip, ifa, 0);
+ neighbor *n = neigh_find(fs->rte->src->proto, ip, ifa, 0);
if (!n || (n->scope == SCOPE_HOST))
runtime( "Invalid gw address" );
ea_set_attr_u32(&a0.eattrs, &ea_gen_preference, 0, c->preference);
ea_set_attr_u32(&a0.eattrs, &ea_gen_source, 0, RTS_DEVICE);
+ ea_set_attr_data(&a0.eattrs, &ea_gen_nexthop, 0, nhad.ad.data, nhad.ad.length);
- a = rta_lookup(&a0);
- e = rte_get_temp(a, src);
- e->pflags = 0;
- rte_update2(c, net, e, src);
+ rte e0 = {
+ .attrs = rta_lookup(&a0),
+ .src = src,
+ };
+
+ rte_update(c, net, &e0, src);
}
}
}
}
- /* Find new_best */
- if ((new_changed == old_changed) || (old_best == old_changed))
- {
- /* Feed or old_best changed -> find first accepted by filters */
- for (rte *r = net->routes; rte_is_valid(r); r = r->next)
- if (new_best = export_filter(c, r, &new_free, 0))
+ /* Check obsolete routes for previously exported */
+ if (!old_best)
+ if (rpe && rpe->old && bmap_test(&c->export_map, rpe->old->rte.id))
+ old_best = &rpe->old->rte;
+
+/* for (; rpe; rpe = atomic_load_explicit(&rpe->next, memory_order_relaxed))
+ {
+ if (rpe->old && bmap_test(&hook->accept_map, rpe->old->id))
+ {
+ old_best = &rpe->old.rte;
break;
- }
- else
- {
- /* Other cases -> either new_changed, or old_best (and nothing changed) */
- if (new_first && (new_changed = export_filter(c, new_changed, &new_free, 0)))
- new_best = new_changed;
- else
- return;
- }
+ }
+ if (rpe == rpe_last)
+ break;
+ }
+ */
+
+ /* Nothing to export */
if (!new_best && !old_best)
- return;
+ {
+ DBG("rt_notify_accepted: nothing to export\n");
+ goto done;
+ }
- do_rt_notify(c, net, new_best, old_best, refeed);
+ do_rt_notify(c, n, new_best, old_best);
- /* Discard temporary rte */
- if (new_free)
- rte_free(new_free);
+done:
+ /* Drop the old stored rejection if applicable.
+ * new->id == old->id happens when updating hostentries. */
+ if (rpe && rpe->old && (!rpe->new || (rpe->new->rte.id != rpe->old->rte.id)))
+ bmap_clear(&c->export_reject_map, rpe->old->rte.id);
}
-
- static struct nexthop *
- nexthop_merge_rta(struct nexthop *nhs, rta *a, linpool *pool, int max)
- {
- return nexthop_merge(nhs, &(a->nh), 1, 0, max, pool);
- }
-
rte *
-rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent)
+rt_export_merged(struct channel *c, struct rte **feed, uint count, linpool *pool, int silent)
{
+ _Thread_local static rte rloc;
+
// struct proto *p = c->proto;
- struct nexthop *nhs = NULL;
+ struct nexthop_adata *nhs = NULL;
- rte *best0, *best, *rt0, *rt, *tmp;
-
- best0 = net->routes;
- *rt_free = NULL;
+ rte *best0 = feed[0];
+ rte *best = NULL;
if (!rte_is_valid(best0))
return NULL;
- best = export_filter(c, best0, rt_free, silent);
+ /* Already rejected, no need to re-run the filter */
+ if (!c->refeeding && bmap_test(&c->export_reject_map, best0->id))
+ return NULL;
+
+ rloc = *best0;
+ best = export_filter(c, &rloc, silent);
+
+ if (!best)
+ /* Best route doesn't pass the filter */
+ return NULL;
- if (!best || !rte_is_reachable(best))
+ if (!rte_is_reachable(best))
+ /* Unreachable routes can't be merged */
return best;
- for (rt0 = best0->next; rt0; rt0 = rt0->next)
+ for (uint i = 1; i < count; i++)
{
- if (!rte_mergable(best0, rt0))
+ if (!rte_mergable(best0, feed[i]))
continue;
- rt = export_filter(c, rt0, &tmp, 1);
+ rte tmp0 = *feed[i];
+ rte *tmp = export_filter(c, &tmp0, 1);
- if (!rt)
+ if (!tmp || !rte_is_reachable(tmp))
continue;
- nhs = nexthop_merge_rta(nhs, tmp->attrs, pool, c->merge_limit);
- if (rte_is_reachable(rt))
- {
- eattr *nhea = ea_find(rt->attrs->eattrs, &ea_gen_nexthop);
- ASSERT_DIE(nhea);
-
- if (nhs)
- nhs = nexthop_merge(nhs, (struct nexthop_adata *) nhea->u.ptr, c->merge_limit, pool);
- else
- nhs = (struct nexthop_adata *) nhea->u.ptr;
- }
++ eattr *nhea = ea_find(tmp->attrs->eattrs, &ea_gen_nexthop);
++ ASSERT_DIE(nhea);
+
- if (tmp)
- rte_free(tmp);
++ if (nhs)
++ nhs = nexthop_merge(nhs, (struct nexthop_adata *) nhea->u.ptr, c->merge_limit, pool);
++ else
++ nhs = (struct nexthop_adata *) nhea->u.ptr;
}
-
if (nhs)
{
- nhs = nexthop_merge_rta(nhs, best->attrs, pool, c->merge_limit);
+ eattr *nhea = ea_find(best->attrs->eattrs, &ea_gen_nexthop);
+ ASSERT_DIE(nhea);
- if (nhs->next)
- {
- best->attrs = rta_cow(best->attrs, pool);
- nexthop_link(best->attrs, nhs);
- }
+ nhs = nexthop_merge(nhs, (struct nexthop_adata *) nhea->u.ptr, c->merge_limit, pool);
+
- best = rte_cow_rta(best, pool);
++ best->attrs = rta_cow(best->attrs, pool);
+ ea_set_attr(&best->attrs->eattrs,
+ EA_LITERAL_DIRECT_ADATA(&ea_gen_nexthop, 0, &nhs->ad));
}
- if (best != best0)
- *rt_free = best;
-
return best;
}
return 0;
}
- if ((e->attrs->dest == RTD_UNICAST) && !nexthop_is_sorted(&(e->attrs->nh)))
+ eattr *nhea = ea_find(e->attrs->eattrs, &ea_gen_nexthop);
+ if ((!nhea) != (e->attrs->dest != RTD_UNICAST))
+ {
+ log(L_WARN "Ignoring route %N with destination %d and %snexthop received via %s",
- n->n.addr, e->attrs->dest, (nhea ? "" : "no "), e->sender->proto->name);
++ n, e->attrs->dest, (nhea ? "" : "no "), ch->proto->name);
+ return 0;
+ }
+
+ if ((e->attrs->dest == RTD_UNICAST) &&
+ !nexthop_is_sorted((struct nexthop_adata *) nhea->u.ptr))
{
log(L_WARN "Ignoring unsorted multipath route %N received via %s",
- n->n.addr, e->sender->proto->name);
+ n, ch->proto->name);
return 0;
}
if (!he->src)
return a->dest != RTD_UNREACHABLE;
+ eattr *he_nh_ea = ea_find(he->src->eattrs, &ea_gen_nexthop);
+ eattr *a_nh_ea = ea_find(a->eattrs, &ea_gen_nexthop);
+
return (a->dest != he->dest) ||
(ea_get_int(a->eattrs, &ea_gen_igp_metric, IGP_METRIC_UNKNOWN) != he->igp_metric) ||
- (!he->nexthop_linkable) || !nexthop_same(&(a->nh), &(he->src->nh));
+ (!he->nexthop_linkable) ||
+ (!he_nh_ea != !a_nh_ea) ||
+ (he_nh_ea && a_nh_ea && !adata_same(he_nh_ea->u.ptr, a_nh_ea->u.ptr));
}
-static inline rte *
-rt_next_hop_update_rte(rtable *tab UNUSED, rte *old)
+static inline struct rte_storage *
+rt_next_hop_update_rte(rtable *tab, net *n, rte *old)
{
if (!rta_next_hop_outdated(old->attrs))
return NULL;
.eattrs = &eattrs.l,
};
- /*
- * If we cannot find a reachable neighbour, set the entry to be onlink. This
- * makes it possible to, e.g., assign /32 addresses on a mesh interface and
- * have routing work.
- */
- if (!neigh_find(&p->p, r->next_hop, r->neigh->ifa->iface, 0))
- a0.nh.flags = RNF_ONLINK;
-
- rta *a = rta_lookup(&a0);
- rte *rte = rte_get_temp(a, p->p.main_source);
+ rte e0 = {
+ .attrs = &a0,
+ .src = p->p.main_source,
+ };
e->unreachable = 0;
- rte_update2(c, e->n.addr, rte, p->p.main_source);
+ rte_update(c, e->n.addr, &e0, p->p.main_source);
}
else if (e->valid && (e->router_id != p->router_id))
{
uint tag = ea_get_int(a->eattrs, &ea_ospf_tag, 0);
ip_addr fwd = IPA_NONE;
- if ((a->dest == RTD_UNICAST) && use_gw_for_fwaddr(p, a->nh.gw, a->nh.iface))
- fwd = a->nh.gw;
+ if (a->dest == RTD_UNICAST)
+ {
+ eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop);
+ if (!nhea)
+ {
+ log(L_ERR "%s: Unicast route without nexthop for %N",
- p->p.name, n->n.addr);
++ p->p.name, n);
+ return;
+ }
+
+ struct nexthop_adata *nhad = (struct nexthop_adata *) nhea->u.ptr;
+ if (use_gw_for_fwaddr(p, nhad->nh.gw, nhad->nh.iface))
+ fwd = nhad->nh.gw;
+ }
/* NSSA-LSA with P-bit set must have non-zero forwarding address */
if (oa && ipa_zero(fwd))
}
static int
- nl_send_route(struct krt_proto *p, const rte *e, int op, int dest, struct nexthop *nh)
-nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop_adata *nh)
++nl_send_route(struct krt_proto *p, const rte *e, int op, int dest, struct nexthop_adata *nh)
{
eattr *ea;
- net *net = e->net;
rta *a = e->attrs;
ea_list *eattrs = a->eattrs;
- int bufsize = 128 + KRT_METRICS_MAX*8 + nh_bufsize(&(a->nh));
+
+ int bufsize = 128 + KRT_METRICS_MAX*8 + (nh ? nh_bufsize(nh) : 0);
u32 priority = 0;
struct {
if (a[RTA_MULTIPATH])
{
- struct nexthop *nh = nl_parse_multipath(s, p, net, a[RTA_MULTIPATH], i->rtm_family, krt_src);
- struct nexthop_adata *nh = nl_parse_multipath(s, p, n, a[RTA_MULTIPATH], i->rtm_family, krt_src);
++ struct nexthop_adata *nh = nl_parse_multipath(s, p, net, a[RTA_MULTIPATH], i->rtm_family, krt_src);
if (!nh)
SKIP("strange RTA_MULTIPATH\n");
if ((i->rtm_flags & RTNH_F_DEAD) && (krt_src != KRT_SRC_BIRD))
SKIP("ignore RTNH_F_DEAD\n");
- ra->nh.iface = if_find_by_index(oif);
- if (!ra->nh.iface)
+ nhad.nh.iface = if_find_by_index(oif);
+ if (!nhad.nh.iface)
{
- log(L_ERR "KRT: Received route %N with unknown ifindex %u", net->n.addr, oif);
+ log(L_ERR "KRT: Received route %N with unknown ifindex %u", net, oif);
return;
}
return;
if (i->rtm_flags & RTNH_F_ONLINK)
- ra->nh.flags |= RNF_ONLINK;
+ nhad.nh.flags |= RNF_ONLINK;
neighbor *nbr;
- nbr = neigh_find(&p->p, ra->nh.gw, ra->nh.iface,
- (ra->nh.flags & RNF_ONLINK) ? NEF_ONLINK : 0);
+ nbr = neigh_find(&p->p, nhad.nh.gw, nhad.nh.iface,
+ (nhad.nh.flags & RNF_ONLINK) ? NEF_ONLINK : 0);
if (!nbr || (nbr->scope == SCOPE_HOST))
{
- log(L_ERR "KRT: Received route %N with strange next-hop %I", net, ra->nh.gw);
- log(L_ERR "KRT: Received route %N with strange next-hop %I", net->n.addr,
++ log(L_ERR "KRT: Received route %N with strange next-hop %I", net,
+ nhad.nh.gw);
return;
}
}
if (!s->net)
{
/* Store the new route */
- s->net = net;
+ s->net = lp_alloc(s->pool, net->length);
+ net_copy(s->net, net);
+
s->attrs = ra;
+
+ ea_set_attr_data(&ra->eattrs, &ea_gen_nexthop, 0,
+ nhad.ad.data, nhad.ad.length);
+
s->proto = p;
s->new = new;
s->krt_src = krt_src;