summaryrefslogtreecommitdiff
path: root/proto/ospf/neighbor.c
diff options
context:
space:
mode:
Diffstat (limited to 'proto/ospf/neighbor.c')
-rw-r--r--proto/ospf/neighbor.c194
1 files changed, 191 insertions, 3 deletions
diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c
index c143b130..50ef6a49 100644
--- a/proto/ospf/neighbor.c
+++ b/proto/ospf/neighbor.c
@@ -28,6 +28,8 @@ static void dbdes_timer_hook(timer *t);
static void lsrq_timer_hook(timer *t);
static void lsrt_timer_hook(timer *t);
static void ackd_timer_hook(timer *t);
+static void ospf_neigh_stop_graceful_restart_(struct ospf_neighbor *n);
+static void graceful_restart_timeout(timer *t);
static void
@@ -163,7 +165,7 @@ ospf_neigh_chstate(struct ospf_neighbor *n, u8 state)
if (old_state == NEIGHBOR_FULL)
ifa->fadj--;
- if (ifa->fadj != old_fadj)
+ if ((ifa->fadj != old_fadj) && !n->gr_active)
{
/* RFC 2328 12.4 Event 4 - neighbor enters/leaves Full state */
ospf_notify_rt_lsa(ifa->oa);
@@ -182,6 +184,7 @@ ospf_neigh_chstate(struct ospf_neighbor *n, u8 state)
n->dds++;
n->myimms = DBDES_IMMS;
+ n->got_my_rt_lsa = 0;
tm_start(n->dbdes_timer, 0);
tm_start(n->ackd_timer, ifa->rxmtint S / 2);
@@ -191,9 +194,9 @@ ospf_neigh_chstate(struct ospf_neighbor *n, u8 state)
n->myimms &= ~DBDES_I;
/* Generate NeighborChange event if needed, see RFC 2328 9.2 */
- if ((state == NEIGHBOR_2WAY) && (old_state < NEIGHBOR_2WAY))
+ if ((state == NEIGHBOR_2WAY) && (old_state < NEIGHBOR_2WAY) && !n->gr_active)
ospf_iface_sm(ifa, ISM_NEICH);
- if ((state < NEIGHBOR_2WAY) && (old_state >= NEIGHBOR_2WAY))
+ if ((state < NEIGHBOR_2WAY) && (old_state >= NEIGHBOR_2WAY) && !n->gr_active)
ospf_iface_sm(ifa, ISM_NEICH);
}
@@ -291,6 +294,17 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event)
case INM_KILLNBR:
case INM_LLDOWN:
case INM_INACTTIM:
+ if (n->gr_active && (event == INM_INACTTIM))
+ {
+ /* Just down the neighbor, but do not remove it */
+ reset_lists(p, n);
+ ospf_neigh_chstate(n, NEIGHBOR_DOWN);
+ break;
+ }
+
+ if (n->gr_active)
+ ospf_neigh_stop_graceful_restart_(n);
+
/* No need for reset_lists() */
ospf_neigh_chstate(n, NEIGHBOR_DOWN);
ospf_neigh_down(n);
@@ -356,6 +370,180 @@ can_do_adj(struct ospf_neighbor *n)
return i;
}
+static void
+ospf_neigh_start_graceful_restart(struct ospf_neighbor *n, uint gr_time)
+{
+ struct ospf_proto *p = n->ifa->oa->po;
+
+ OSPF_TRACE(D_EVENTS, "Neighbor %R on %s started graceful restart",
+ n->rid, n->ifa->ifname);
+
+ n->gr_active = 1;
+ p->gr_count++;
+
+ n->gr_timer = tm_new_init(n->pool, graceful_restart_timeout, n, 0, 0);
+ tm_start(n->gr_timer, gr_time S);
+}
+
+static void
+ospf_neigh_stop_graceful_restart_(struct ospf_neighbor *n)
+{
+ struct ospf_proto *p = n->ifa->oa->po;
+ struct ospf_iface *ifa = n->ifa;
+
+ n->gr_active = 0;
+ p->gr_count--;
+
+ rfree(n->gr_timer);
+ n->gr_timer = NULL;
+
+ ospf_notify_rt_lsa(ifa->oa);
+ ospf_notify_net_lsa(ifa);
+
+ if (ifa->type == OSPF_IT_VLINK)
+ ospf_notify_rt_lsa(ifa->voa);
+
+ ospf_iface_sm(ifa, ISM_NEICH);
+}
+
+static void
+ospf_neigh_stop_graceful_restart(struct ospf_neighbor *n)
+{
+ struct ospf_proto *p = n->ifa->oa->po;
+
+ OSPF_TRACE(D_EVENTS, "Neighbor %R on %s finished graceful restart",
+ n->rid, n->ifa->ifname);
+
+ ospf_neigh_stop_graceful_restart_(n);
+}
+
+void
+ospf_neigh_cancel_graceful_restart(struct ospf_neighbor *n)
+{
+ struct ospf_proto *p = n->ifa->oa->po;
+
+ OSPF_TRACE(D_EVENTS, "Graceful restart canceled for nbr %R on %s",
+ n->rid, n->ifa->ifname);
+
+ ospf_neigh_stop_graceful_restart_(n);
+
+ if (n->state == NEIGHBOR_DOWN)
+ ospf_neigh_down(n);
+}
+
+static void
+graceful_restart_timeout(timer *t)
+{
+ struct ospf_neighbor *n = t->data;
+ struct ospf_proto *p = n->ifa->oa->po;
+
+ OSPF_TRACE(D_EVENTS, "Graceful restart timer expired for nbr %R on %s",
+ n->rid, n->ifa->ifname);
+
+ ospf_neigh_stop_graceful_restart_(n);
+
+ if (n->state == NEIGHBOR_DOWN)
+ ospf_neigh_down(n);
+}
+
+static inline int
+changes_in_lsrtl(struct ospf_neighbor *n)
+{
+ /* This could be improved, see RFC 3623 3.1 (2) */
+
+ struct top_hash_entry *en;
+ WALK_SLIST(en, n->lsrtl)
+ if (LSA_FUNCTION(en->lsa_type) <= LSA_FUNCTION(LSA_T_NSSA))
+ return 1;
+
+ return 0;
+}
+
+void
+ospf_neigh_notify_grace_lsa(struct ospf_neighbor *n, struct top_hash_entry *en)
+{
+ struct ospf_iface *ifa = n->ifa;
+ struct ospf_proto *p = ifa->oa->po;
+
+ /* In OSPFv2, neighbors are identified by either IP or Router ID, based on network type */
+ uint t = ifa->type;
+ if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP)))
+ {
+ struct ospf_tlv *tlv = lsa_get_tlv(en, LSA_GR_ADDRESS);
+ if (!tlv || tlv->length != 4)
+ return;
+
+ ip_addr addr = ipa_from_u32(tlv->data[0]);
+ if (!ipa_equal(n->ip, addr))
+ n = find_neigh_by_ip(ifa, addr);
+ }
+ else
+ {
+ if (n->rid != en->lsa.rt)
+ n = find_neigh(ifa, en->lsa.rt);
+ }
+
+ if (!n)
+ return;
+
+ if (en->lsa.age < LSA_MAXAGE)
+ {
+ u32 period = lsa_get_tlv_u32(en, LSA_GR_PERIOD);
+
+ /* Exception for updating grace period */
+ if (n->gr_active)
+ {
+ tm_start(n->gr_timer, (period S) - (en->lsa.age S));
+ return;
+ }
+
+ /* RFC 3623 3.1 (1) - full adjacency */
+ if (n->state != NEIGHBOR_FULL)
+ return;
+
+ /* RFC 3623 3.1 (2) - no changes in LSADB */
+ if (changes_in_lsrtl(n))
+ return;
+
+ /* RFC 3623 3.1 (3) - grace period not expired */
+ if (en->lsa.age >= period)
+ return;
+
+ /* RFC 3623 3.1 (4) - helper mode allowed */
+ if (!p->gr_mode)
+ return;
+
+ /* RFC 3623 3.1 (5) - no local graceful restart */
+ if (p->p.gr_recovery)
+ return;
+
+ ospf_neigh_start_graceful_restart(n, period - en->lsa.age);
+ }
+ else /* Grace-LSA is flushed */
+ {
+ if (n->gr_active)
+ ospf_neigh_stop_graceful_restart(n);
+ }
+}
+
+void
+ospf_neigh_lsadb_changed_(struct ospf_proto *p, struct top_hash_entry *en)
+{
+ struct ospf_iface *ifa;
+ struct ospf_neighbor *n, *nx;
+
+ if (LSA_FUNCTION(en->lsa_type) > LSA_FUNCTION(LSA_T_NSSA))
+ return;
+
+ /* RFC 3623 3.2 (3) - cancel graceful restart when LSdb changed */
+ WALK_LIST(ifa, p->iface_list)
+ if (lsa_flooding_allowed(en->lsa_type, en->domain, ifa))
+ WALK_LIST_DELSAFE(n, nx, ifa->neigh_list)
+ if (n->gr_active)
+ ospf_neigh_cancel_graceful_restart(n);
+}
+
+
static inline u32 neigh_get_id(struct ospf_proto *p, struct ospf_neighbor *n)
{ return ospf_is_v2(p) ? ipa_to_u32(n->ip) : n->rid; }