summaryrefslogtreecommitdiff
path: root/proto/ospf/rt.c
diff options
context:
space:
mode:
authorOndrej Zajicek (work) <santiago@crfreenet.org>2019-06-30 20:12:59 +0200
committerOndrej Zajicek (work) <santiago@crfreenet.org>2019-06-30 21:30:41 +0200
commit1a2ad348f660b150265f6df759a07de8a2b6de2f (patch)
treed64846d56a79178a34e865440b3b87d970c13fd4 /proto/ospf/rt.c
parent8a68316eb96be1fecf91ca395f3321aa99997ad2 (diff)
OSPF: Support for graceful restart
Implement OSPFv2 (RFC 3623) and OSPFv3 (RFC 5187) graceful restart, for both restarting and helper sides. Graceful restart is initiated by 'graceful down' command.
Diffstat (limited to 'proto/ospf/rt.c')
-rw-r--r--proto/ospf/rt.c180
1 files changed, 172 insertions, 8 deletions
diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c
index 6ddd6c9f..126ef201 100644
--- a/proto/ospf/rt.c
+++ b/proto/ospf/rt.c
@@ -10,7 +10,7 @@
#include "ospf.h"
-static void add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry *par, u32 dist, int i, uint lif, uint nif);
+static void add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry *par, u32 dist, int i, uint data, uint lif, uint nif);
static void rt_sync(struct ospf_proto *p);
@@ -392,6 +392,40 @@ px_pos_to_ifa(struct ospf_area *oa, int pos)
return NULL;
}
+static inline struct ospf_iface *
+rt_find_iface2(struct ospf_area *oa, uint data)
+{
+ ip_addr addr = ipa_from_u32(data);
+
+ /* We should handle it differently for unnumbered PTP links */
+ struct ospf_iface *ifa;
+ WALK_LIST(ifa, oa->po->iface_list)
+ if ((ifa->oa == oa) && ifa->addr && (ipa_equal(ifa->addr->ip, addr)))
+ return ifa;
+
+ return NULL;
+}
+
+static inline struct ospf_iface *
+rt_find_iface3(struct ospf_area *oa, uint lif)
+{
+ struct ospf_iface *ifa;
+ WALK_LIST(ifa, oa->po->iface_list)
+ if ((ifa->oa == oa) && (ifa->iface_id == lif))
+ return ifa;
+
+ return NULL;
+}
+
+static struct ospf_iface *
+rt_find_iface(struct ospf_area *oa, int pos, uint data, uint lif)
+{
+ if (0)
+ return rt_pos_to_ifa(oa, pos);
+ else
+ return ospf_is_v2(oa->po) ? rt_find_iface2(oa, data) : rt_find_iface3(oa, lif);
+}
+
static void
add_network(struct ospf_area *oa, net_addr *net, int metric, struct top_hash_entry *en, int pos)
@@ -503,7 +537,7 @@ spfa_process_rt(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_entr
break;
}
- add_cand(oa, tmp, act, act->dist + rtl.metric, i, rtl.lif, rtl.nif);
+ add_cand(oa, tmp, act, act->dist + rtl.metric, i, rtl.data, rtl.lif, rtl.nif);
}
}
@@ -526,7 +560,7 @@ spfa_process_net(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_ent
for (i = 0; i < cnt; i++)
{
tmp = ospf_hash_find_rt(p->gr, oa->areaid, ln->routers[i]);
- add_cand(oa, tmp, act, act->dist, -1, 0, 0);
+ add_cand(oa, tmp, act, act->dist, -1, 0, 0, 0);
}
}
@@ -1708,7 +1742,7 @@ link_lsa_lladdr(struct ospf_proto *p, struct top_hash_entry *en)
static struct nexthop *
calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
- struct top_hash_entry *par, int pos, uint lif, uint nif)
+ struct top_hash_entry *par, int pos, uint data, uint lif, uint nif)
{
struct ospf_proto *p = oa->po;
struct nexthop *pn = par->nhs;
@@ -1735,7 +1769,7 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
/* The first case - local network */
if ((en->lsa_type == LSA_T_NET) && (par == oa->rt))
{
- ifa = rt_pos_to_ifa(oa, pos);
+ ifa = rt_find_iface(oa, pos, data, lif);
if (!ifa)
return NULL;
@@ -1748,7 +1782,7 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
/* The second case - ptp or ptmp neighbor */
if ((en->lsa_type == LSA_T_RT) && (par == oa->rt))
{
- ifa = rt_pos_to_ifa(oa, pos);
+ ifa = rt_find_iface(oa, pos, data, lif);
if (!ifa)
return NULL;
@@ -1838,7 +1872,7 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
/* Add LSA into list of candidates in Dijkstra's algorithm */
static void
add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry *par,
- u32 dist, int pos, uint lif, uint nif)
+ u32 dist, int pos, uint data, uint lif, uint nif)
{
struct ospf_proto *p = oa->po;
node *prev, *n;
@@ -1871,7 +1905,7 @@ add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry
if (!link_back(oa, en, par, lif, nif))
return;
- struct nexthop *nhs = calc_next_hop(oa, en, par, pos, lif, nif);
+ struct nexthop *nhs = calc_next_hop(oa, en, par, pos, data, lif, nif);
if (!nhs)
{
log(L_WARN "%s: Cannot find next hop for LSA (Type: %04x, Id: %R, Rt: %R)",
@@ -2086,3 +2120,133 @@ again2:
if (en->mode == LSA_M_STALE)
ospf_flush_lsa(p, en);
}
+
+
+/* RFC 3623 2.2 - checking for graceful restart termination conditions */
+void
+ospf_update_gr_recovery(struct ospf_proto *p)
+{
+ struct top_hash_entry *rt, *net, *nbr;
+ struct ospf_lsa_rt_walk rtl;
+ struct ospf_neighbor *n;
+ struct ospf_iface *ifa;
+ struct ospf_area *oa;
+ const char *err_dsc = NULL;
+ uint i, j, missing = 0, err_val = 0;
+
+ /*
+ * We check here for three cases:
+ * RFC 3623 2.2 (1) - success when all adjacencies are established
+ * RFC 3623 2.2 (2) - failure when inconsistent LSA was received
+ * RFC 3623 2.2 (3) - grace period timeout
+ *
+ * It is handled by processing pre-restart local router-LSA and adjacent
+ * network-LSAs, checking neighbor association for referenced routers (1)
+ * and checking back links from their router-LSAs (2).
+ *
+ * TODO: Use timer for grace period timeout. We avoided that as function
+ * ospf_stop_gr_recovery() called from ospf_disp() makes ending of graceful
+ * restart uninterrupted by other events.
+ */
+
+ #define CONTINUE { missing++; continue; }
+
+ if (current_time() > p->gr_timeout)
+ goto timeout;
+
+ WALK_LIST(oa, p->area_list)
+ {
+ /* Get the router-LSA */
+ rt = oa->rt;
+ if (!rt || (rt->lsa.age == LSA_MAXAGE))
+ CONTINUE;
+
+ for (lsa_walk_rt_init(p, rt, &rtl), i = 0; lsa_walk_rt(&rtl); i++)
+ {
+ if (rtl.type == LSART_STUB)
+ continue;
+
+ ifa = rt_find_iface(oa, i, rtl.data, rtl.lif);
+ if (!ifa)
+ DROP("inconsistent interface", ospf_is_v2(p) ? rtl.data : rtl.lif);
+
+ switch (rtl.type)
+ {
+ case LSART_NET:
+ /* Find the network-LSA */
+ net = ospf_hash_find_net(p->gr, oa->areaid, rtl.id, rtl.nif);
+ if (!net)
+ CONTINUE;
+
+ if (!link_back(oa, net, rt, rtl.lif, rtl.nif))
+ DROP("Inconsistent network-LSA", net->lsa.id);
+
+ if (ifa->state == OSPF_IS_DR)
+ {
+ /* Find all neighbors from the network-LSA */
+ struct ospf_lsa_net *net_body = net->lsa_body;
+ uint cnt = lsa_net_count(&net->lsa);
+ for (j = 0; j < cnt; i++)
+ {
+ n = find_neigh(ifa, net_body->routers[j]);
+ if (!n || (n->state != NEIGHBOR_FULL))
+ CONTINUE;
+
+ if (!n->got_my_rt_lsa)
+ DROP("not received my router-LSA", n->rid);
+
+ nbr = ospf_hash_find_rt(p->gr, oa->areaid, n->rid);
+ if (!link_back(oa, nbr, net, 0, 0))
+ DROP("inconsistent router-LSA", n->rid);
+ }
+ }
+ else
+ {
+ /* Find the DR (by IP for OSPFv2) */
+ n = ospf_is_v2(p) ?
+ find_neigh_by_ip(ifa, ipa_from_u32(rtl.id)) :
+ find_neigh(ifa, rtl.id);
+ if (!n || (n->state != NEIGHBOR_FULL))
+ CONTINUE;
+
+ if (!n->got_my_rt_lsa)
+ DROP("not received my router-LSA", n->rid);
+ }
+ break;
+
+ case LSART_VLNK:
+ case LSART_PTP:
+ /* Find the PtP peer */
+ n = find_neigh(ifa, rtl.id);
+ if (!n || (n->state != NEIGHBOR_FULL))
+ CONTINUE;
+
+ if (!n->got_my_rt_lsa)
+ DROP("not received my router-LSA", n->rid);
+
+ nbr = ospf_hash_find_rt(p->gr, oa->areaid, rtl.id);
+ if (!link_back(oa, nbr, rt, rtl.lif, rtl.nif))
+ DROP("inconsistent router-LSA", rtl.id);
+ }
+ }
+ }
+
+ #undef CONTINUE
+
+ if (missing)
+ return;
+
+ OSPF_TRACE(D_EVENTS, "Graceful restart finished");
+ ospf_stop_gr_recovery(p);
+ return;
+
+drop:
+ log(L_INFO "%s: Graceful restart ended - %s (%R)", p->p.name, err_dsc, err_val);
+ ospf_stop_gr_recovery(p);
+ return;
+
+timeout:
+ log(L_INFO "%s: Graceful restart ended - grace period expired", p->p.name);
+ ospf_stop_gr_recovery(p);
+ return;
+}