static struct static_route *this_srt, *this_srt_nh, *last_srt_nh;
static struct f_inst **this_srt_last_cmd;
+static void
+static_route_finish(void)
+{
+ struct static_route *r;
+
+ /* Update undefined use_bfd entries in multipath nexthops */
+ if (this_srt->dest == RTD_MULTIPATH)
+ for (r = this_srt->mp_next; r; r = r->mp_next)
+ if (r->use_bfd < 0)
+ r->use_bfd = this_srt->use_bfd;
+}
+
CF_DECLS
CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK)
-CF_KEYWORDS(MULTIPATH, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE)
+CF_KEYWORDS(MULTIPATH, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD)
CF_GRAMMAR
| static_proto proto_item ';'
| static_proto CHECK LINK bool ';' { STATIC_CFG->check_link = $4; }
| static_proto IGP TABLE rtable ';' { STATIC_CFG->igp_table = $4; }
- | static_proto stat_route stat_route_opt_list ';'
+ | static_proto stat_route stat_route_opt_list ';' { static_route_finish(); }
;
stat_route0: ROUTE prefix {
this_srt_nh->via = $2;
this_srt_nh->via_if = $3;
this_srt_nh->if_name = (void *) this_srt; /* really */
+ this_srt_nh->use_bfd = -1; /* undefined */
}
| stat_multipath1 WEIGHT expr {
this_srt_nh->masklen = $3 - 1; /* really */
if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256");
}
+ | stat_multipath1 BFD bool {
+ this_srt_nh->use_bfd = $3; cf_check_bfd($3);
+ }
;
stat_multipath:
stat_route_item:
cmd { *this_srt_last_cmd = $1; this_srt_last_cmd = &($1->next); }
+ | BFD bool ';' { this_srt->use_bfd = $2; cf_check_bfd($2); }
;
stat_route_opts:
r->installed = 0;
}
+static void
+static_bfd_notify(struct bfd_request *req);
+
+static void
+static_update_bfd(struct proto *p, struct static_route *r)
+{
+ struct neighbor *nb = r->neigh;
+ int bfd_up = (nb->scope > 0) && r->use_bfd;
+
+ if (bfd_up && !r->bfd_req)
+ {
+ // ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip;
+ r->bfd_req = bfd_request_session(p->pool, r->via, nb->ifa->ip, nb->iface,
+ static_bfd_notify, r);
+ }
+
+ if (!bfd_up && r->bfd_req)
+ {
+ rfree(r->bfd_req);
+ r->bfd_req = NULL;
+ }
+}
+
static int
static_decide(struct static_config *cf, struct static_route *r)
{
if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP))
return 0;
+ if (r->bfd_req && r->bfd_req->state != BFD_STATE_UP)
+ return 0;
+
return 1;
}
r->chain = n->data;
n->data = r;
r->neigh = n;
+
+ static_update_bfd(p, r);
if (static_decide(cf, r))
static_install(p, r, n->iface);
else
r2->chain = n->data;
n->data = r2;
r2->neigh = n;
+
+ static_update_bfd(p, r2);
r2->installed = static_decide(cf, r2);
count += r2->installed;
}
}
}
+static void
+static_rte_cleanup(struct proto *p, struct static_route *r)
+{
+ struct static_route *r2;
+
+ if (r->bfd_req)
+ {
+ rfree(r->bfd_req);
+ r->bfd_req = NULL;
+ }
+
+ if (r->dest == RTD_MULTIPATH)
+ for (r2 = r->mp_next; r2; r2 = r2->mp_next)
+ if (r2->bfd_req)
+ {
+ rfree(r2->bfd_req);
+ r2->bfd_req = NULL;
+ }
+}
+
static int
static_start(struct proto *p)
{
WALK_LIST(r, cf->iface_routes)
r->installed = 0;
WALK_LIST(r, cf->other_routes)
+ {
+ static_rte_cleanup(p, r);
r->installed = 0;
+ }
return PS_DOWN;
}
rt_unlock_table(cf->igp_table->table);
}
+static void
+static_update_rte(struct proto *p, struct static_route *r)
+{
+ switch (r->dest)
+ {
+ case RTD_ROUTER:
+ if (static_decide((struct static_config *) p->cf, r))
+ static_install(p, r, r->neigh->iface);
+ else
+ static_remove(p, r);
+ break;
+
+ case RTD_NONE: /* a part of multipath route */
+ {
+ int decision = static_decide((struct static_config *) p->cf, r);
+ if (decision == r->installed)
+ break; /* no change */
+ r->installed = decision;
+
+ struct static_route *r1, *r2;
+ int count = 0;
+ r1 = (void *) r->if_name; /* really */
+ for (r2 = r1->mp_next; r2; r2 = r2->mp_next)
+ count += r2->installed;
+
+ if (count)
+ {
+ /* Set of nexthops changed - force reinstall */
+ r1->installed = 0;
+ static_install(p, r1, NULL);
+ }
+ else
+ static_remove(p, r1);
+
+ break;
+ }
+ }
+}
static void
static_neigh_notify(struct neighbor *n)
DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
for(r=n->data; r; r=r->chain)
- switch (r->dest)
- {
- case RTD_ROUTER:
- if (static_decide((struct static_config *) p->cf, r))
- static_install(p, r, n->iface);
- else
- static_remove(p, r);
- break;
+ {
+ static_update_bfd(p, r);
+ static_update_rte(p, r);
+ }
+}
- case RTD_NONE: /* a part of multipath route */
- {
- int decision = static_decide((struct static_config *) p->cf, r);
- if (decision == r->installed)
- break; /* no change */
- r->installed = decision;
-
- struct static_route *r1, *r2;
- int count = 0;
- r1 = (void *) r->if_name; /* really */
- for (r2 = r1->mp_next; r2; r2 = r2->mp_next)
- count += r2->installed;
-
- if (count)
- {
- /* Set of nexthops changed - force reinstall */
- r1->installed = 0;
- static_install(p, r1, NULL);
- }
- else
- static_remove(p, r1);
+static void
+static_bfd_notify(struct bfd_request *req)
+{
+ struct static_route *r = req->data;
+ struct proto *p = r->neigh->proto;
- break;
- }
- }
+ // if (req->down) TRACE(D_EVENTS, "BFD session down for nbr %I on %s", XXXX);
+
+ static_update_rte(p, r);
}
static void
for (x = x->mp_next, y = y->mp_next;
x && y;
x = x->mp_next, y = y->mp_next)
- if (!ipa_equal(x->via, y->via) || (x->via_if != y->via_if))
+ if (!ipa_equal(x->via, y->via) || (x->via_if != y->via_if) || (x->use_bfd != y->use_bfd))
return 0;
return !x && !y;
WALK_LIST(r, n->other_routes)
static_add(p, n, r);
+ WALK_LIST(r, o->other_routes)
+ static_rte_cleanup(p, r);
+
return 1;
}
case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break;
default: bsprintf(via, "???");
}
- cli_msg(-1009, "%I/%d %s%s", r->net, r->masklen, via, r->installed ? "" : " (dormant)");
+ cli_msg(-1009, "%I/%d %s%s%s", r->net, r->masklen, via,
+ r->bfd_req ? " (bfd)" : "", r->installed ? "" : " (dormant)");
struct static_route *r2;
if (r->dest == RTD_MULTIPATH)
for (r2 = r->mp_next; r2; r2 = r2->mp_next)
- cli_msg(-1009, "\tvia %I%J weight %d%s", r2->via, r2->via_if, r2->masklen + 1, /* really */
- r2->installed ? "" : " (dormant)");
+ cli_msg(-1009, "\tvia %I%J weight %d%s%s", r2->via, r2->via_if, r2->masklen + 1, /* really */
+ r2->bfd_req ? " (bfd)" : "", r2->installed ? "" : " (dormant)");
}
void