Implement onlink flag for nexthops

Add proper support for per-nexthop onlink flag in routes to handle next
hop addresses that are not covered by interface IP ranges. Supported by
kernel and static protocols.

Thanks to Vincent Bernat for the idea.
This commit is contained in:
Ondrej Zajicek (work) 2017-07-04 23:36:21 +02:00
parent 5220cb63e3
commit a1f5e514ef
8 changed files with 49 additions and 13 deletions

View file

@ -366,12 +366,16 @@ struct nexthop {
ip_addr gw; /* Next hop */ ip_addr gw; /* Next hop */
struct iface *iface; /* Outgoing interface */ struct iface *iface; /* Outgoing interface */
struct nexthop *next; struct nexthop *next;
byte flags;
byte weight; byte weight;
byte labels_orig; /* Number of labels before hostentry was applied */ byte labels_orig; /* Number of labels before hostentry was applied */
byte labels; /* Number of all labels */ byte labels; /* Number of all labels */
u32 label[0]; u32 label[0];
}; };
#define RNF_ONLINK 0x1 /* Gateway is onlink regardless of IP ranges */
struct rte_src { struct rte_src {
struct rte_src *next; /* Hash chain */ struct rte_src *next; /* Hash chain */
struct proto *proto; /* Protocol the source is based on */ struct proto *proto; /* Protocol the source is based on */

View file

@ -171,7 +171,9 @@ nexthop__same(struct nexthop *x, struct nexthop *y)
{ {
for (; x && y; x = x->next, y = y->next) for (; x && y; x = x->next, y = y->next)
{ {
if (!ipa_equal(x->gw, y->gw) || (x->iface != y->iface) || (x->weight != y->weight) || (x->labels != y->labels)) if (!ipa_equal(x->gw, y->gw) || (x->iface != y->iface) ||
(x->flags != y->flags) || (x->weight != y->weight) ||
(x->labels != y->labels))
return 0; return 0;
for (int i = 0; i < x->labels; i++) for (int i = 0; i < x->labels; i++)
@ -193,6 +195,8 @@ nexthop_compare_node(struct nexthop *x, struct nexthop *y)
if (!y) if (!y)
return -1; return -1;
/* Should we also compare flags ? */
r = ((int) y->weight) - ((int) x->weight); r = ((int) y->weight) - ((int) x->weight);
if (r) if (r)
return r; return r;

View file

@ -70,6 +70,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
for (nh = &(a->nh); nh; nh = nh->next) for (nh = &(a->nh); nh; nh = nh->next)
{ {
char mpls[MPLS_MAX_LABEL_STACK*12 + 5], *lsp = mpls; char mpls[MPLS_MAX_LABEL_STACK*12 + 5], *lsp = mpls;
char *onlink = (nh->flags & RNF_ONLINK) ? " onlink" : "";
if (nh->labels) if (nh->labels)
{ {
@ -80,9 +81,11 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
*lsp = '\0'; *lsp = '\0';
if (a->nh.next) if (a->nh.next)
cli_printf(c, -1007, "\tvia %I%s on %s weight %d", nh->gw, mpls, nh->iface->name, nh->weight + 1); cli_printf(c, -1007, "\tvia %I%s on %s%s weight %d",
nh->gw, mpls, nh->iface->name, onlink, nh->weight + 1);
else else
cli_printf(c, -1007, "\tvia %I%s on %s", nh->gw, mpls, nh->iface->name); cli_printf(c, -1007, "\tvia %I%s on %s%s",
nh->gw, mpls, nh->iface->name, onlink);
} }
if (d->verbose) if (d->verbose)

View file

@ -1819,7 +1819,10 @@ no_nexthop:
} }
} }
if (ipa_nonzero(nh->gw)) if (ipa_nonzero(nh->gw))
nhp->gw = nh->gw; /* Router nexthop */ {
nhp->gw = nh->gw; /* Router nexthop */
nhp->flags |= (nh->flags & RNF_ONLINK);
}
else if (ipa_nonzero(he->link)) else if (ipa_nonzero(he->link))
nhp->gw = he->link; /* Device nexthop with link-local address known */ nhp->gw = he->link; /* Device nexthop with link-local address known */
else else

View file

@ -44,7 +44,7 @@ static_route_finish(void)
CF_DECLS CF_DECLS
CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK) CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK)
CF_KEYWORDS(WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD, MPLS) CF_KEYWORDS(ONLINK, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD, MPLS)
CF_GRAMMAR CF_GRAMMAR
@ -87,6 +87,9 @@ stat_nexthop:
| stat_nexthop MPLS label_stack { | stat_nexthop MPLS label_stack {
this_snh->mls = $3; this_snh->mls = $3;
} }
| stat_nexthop ONLINK bool {
this_snh->onlink = $3;
}
| stat_nexthop WEIGHT expr { | stat_nexthop WEIGHT expr {
this_snh->weight = $3 - 1; this_snh->weight = $3 - 1;
if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256"); if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256");

View file

@ -71,6 +71,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
struct nexthop *nh = allocz(NEXTHOP_MAX_SIZE); struct nexthop *nh = allocz(NEXTHOP_MAX_SIZE);
nh->gw = r2->via; nh->gw = r2->via;
nh->iface = r2->neigh->iface; nh->iface = r2->neigh->iface;
nh->flags = r2->onlink ? RNF_ONLINK : 0;
nh->weight = r2->weight; nh->weight = r2->weight;
if (r2->mls) if (r2->mls)
{ {
@ -205,7 +206,8 @@ static_add_rte(struct static_proto *p, struct static_route *r)
for (r2 = r; r2; r2 = r2->mp_next) for (r2 = r; r2; r2 = r2->mp_next)
{ {
n = ipa_nonzero(r2->via) ? n = ipa_nonzero(r2->via) ?
neigh_find2(&p->p, &r2->via, r2->iface, NEF_STICKY) : neigh_find2(&p->p, &r2->via, r2->iface,
NEF_STICKY | (r2->onlink ? NEF_ONLINK : 0)) :
neigh_find_iface(&p->p, r2->iface); neigh_find_iface(&p->p, r2->iface);
if (!n) if (!n)
@ -267,8 +269,9 @@ static_same_dest(struct static_route *x, struct static_route *y)
{ {
if (!ipa_equal(x->via, y->via) || if (!ipa_equal(x->via, y->via) ||
(x->iface != y->iface) || (x->iface != y->iface) ||
(x->use_bfd != y->use_bfd) || (x->onlink != y->onlink) ||
(x->weight != y->weight) || (x->weight != y->weight) ||
(x->use_bfd != y->use_bfd) ||
(!x->mls != !y->mls) || (!x->mls != !y->mls) ||
((x->mls) && (y->mls) && (x->mls->len != y->mls->len))) ((x->mls) && (y->mls) && (x->mls->len != y->mls->len)))
return 0; return 0;
@ -614,11 +617,13 @@ static_show_rt(struct static_route *r)
for (r2 = r; r2; r2 = r2->mp_next) for (r2 = r; r2; r2 = r2->mp_next)
{ {
if (r2->iface && ipa_zero(r2->via)) if (r2->iface && ipa_zero(r2->via))
cli_msg(-1009, "\tdev %s%s%s", r2->iface->name, cli_msg(-1009, "\tdev %s%s", r2->iface->name,
r2->bfd_req ? " (bfd)" : "", r2->active ? "" : " (dormant)"); r2->active ? "" : " (dormant)");
else else
cli_msg(-1009, "\tvia %I%J%s%s", r2->via, r2->iface, cli_msg(-1009, "\tvia %I%J%s%s%s", r2->via, r2->iface,
r2->bfd_req ? " (bfd)" : "", r2->active ? "" : " (dormant)"); r2->onlink ? " onlink" : "",
r2->bfd_req ? " (bfd)" : "",
r2->active ? "" : " (dormant)");
} }
break; break;
} }

View file

@ -43,6 +43,7 @@ struct static_route {
byte dest; /* Destination type (RTD_*) */ byte dest; /* Destination type (RTD_*) */
byte state; /* State of route announcement (SRS_*) */ byte state; /* State of route announcement (SRS_*) */
byte active; /* Next hop is active (nbr/iface/BFD available) */ byte active; /* Next hop is active (nbr/iface/BFD available) */
byte onlink; /* Gateway is onlink regardless of IP ranges */
byte weight; /* Multipath next hop weight */ byte weight; /* Multipath next hop weight */
byte use_bfd; /* Configured to use BFD */ byte use_bfd; /* Configured to use BFD */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */ struct bfd_request *bfd_req; /* BFD request, if BFD is used */

View file

@ -622,6 +622,9 @@ nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct nexthop *nh, int af)
nl_add_nexthop(h, bufsize, nh, af); nl_add_nexthop(h, bufsize, nh, af);
if (nh->flags & RNF_ONLINK)
rtnh->rtnh_flags |= RTNH_F_ONLINK;
nl_close_nexthop(h, rtnh); nl_close_nexthop(h, rtnh);
} }
@ -660,6 +663,7 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
rv->next = NULL; rv->next = NULL;
last = &(rv->next); last = &(rv->next);
rv->flags = 0;
rv->weight = nh->rtnh_hops; rv->weight = nh->rtnh_hops;
rv->iface = if_find_by_index(nh->rtnh_ifindex); rv->iface = if_find_by_index(nh->rtnh_ifindex);
if (!rv->iface) if (!rv->iface)
@ -672,9 +676,12 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
{ {
rv->gw = rta_get_ipa(a[RTA_GATEWAY]); rv->gw = rta_get_ipa(a[RTA_GATEWAY]);
if (nh->rtnh_flags & RTNH_F_ONLINK)
rv->flags |= RNF_ONLINK;
neighbor *nbr; neighbor *nbr;
nbr = neigh_find2(&p->p, &rv->gw, rv->iface, nbr = neigh_find2(&p->p, &rv->gw, rv->iface,
(nh->rtnh_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0); (rv->flags & RNF_ONLINK) ? NEF_ONLINK : 0);
if (!nbr || (nbr->scope == SCOPE_HOST)) if (!nbr || (nbr->scope == SCOPE_HOST))
return NULL; return NULL;
} }
@ -1228,6 +1235,9 @@ dest:
{ {
nl_add_attr_u32(&r->h, rsize, RTA_OIF, nh->iface->index); nl_add_attr_u32(&r->h, rsize, RTA_OIF, nh->iface->index);
nl_add_nexthop(&r->h, rsize, nh, p->af); nl_add_nexthop(&r->h, rsize, nh, p->af);
if (nh->flags & RNF_ONLINK)
r->r.rtm_flags |= RTNH_F_ONLINK;
} }
break; break;
case RTD_BLACKHOLE: case RTD_BLACKHOLE:
@ -1543,9 +1553,12 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
if ((i->rtm_family == AF_INET6) && ipa_in_netX(ra->nh.gw, (net_addr *) &sit)) if ((i->rtm_family == AF_INET6) && ipa_in_netX(ra->nh.gw, (net_addr *) &sit))
return; return;
if (i->rtm_flags & RTNH_F_ONLINK)
ra->nh.flags |= RNF_ONLINK;
neighbor *nbr; neighbor *nbr;
nbr = neigh_find2(&p->p, &(ra->nh.gw), ra->nh.iface, nbr = neigh_find2(&p->p, &(ra->nh.gw), ra->nh.iface,
(i->rtm_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0); (ra->nh.flags & RNF_ONLINK) ? NEF_ONLINK : 0);
if (!nbr || (nbr->scope == SCOPE_HOST)) if (!nbr || (nbr->scope == SCOPE_HOST))
{ {
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->n.addr,