summaryrefslogtreecommitdiff
path: root/proto/ospf/rt.c
diff options
context:
space:
mode:
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;
+}