summaryrefslogtreecommitdiff
path: root/proto/ospf
diff options
context:
space:
mode:
Diffstat (limited to 'proto/ospf')
-rw-r--r--proto/ospf/config.Y6
-rw-r--r--proto/ospf/dbdes.c6
-rw-r--r--proto/ospf/iface.c8
-rw-r--r--proto/ospf/lsalib.c75
-rw-r--r--proto/ospf/lsalib.h15
-rw-r--r--proto/ospf/lsupd.c20
-rw-r--r--proto/ospf/neighbor.c194
-rw-r--r--proto/ospf/ospf.c60
-rw-r--r--proto/ospf/ospf.h32
-rw-r--r--proto/ospf/rt.c180
-rw-r--r--proto/ospf/rt.h1
-rw-r--r--proto/ospf/topology.c86
-rw-r--r--proto/ospf/topology.h3
13 files changed, 660 insertions, 26 deletions
diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y
index 36fbd5f1..e4bacc29 100644
--- a/proto/ospf/config.Y
+++ b/proto/ospf/config.Y
@@ -200,6 +200,7 @@ CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL, STUBNET, HIDDEN, SUMMARY, TAG, EXTERNAL)
CF_KEYWORDS(WAIT, DELAY, LSADB, ECMP, LIMIT, WEIGHT, NSSA, TRANSLATOR, STABILITY)
CF_KEYWORDS(GLOBAL, LSID, ROUTER, SELF, INSTANCE, REAL, NETMASK, TX, PRIORITY, LENGTH)
CF_KEYWORDS(MERGE, LSA, SUPPRESSION, MULTICAST, RFC5838, VPN, PE)
+CF_KEYWORDS(GRACEFUL, RESTART, AWARE, TIME)
%type <ld> lsadb_args
%type <i> ospf_variant ospf_af_mc nbma_eligible
@@ -226,6 +227,8 @@ ospf_proto_start: proto_start ospf_variant
OSPF_CFG->tick = OSPF_DEFAULT_TICK;
OSPF_CFG->ospf2 = $2;
OSPF_CFG->af_ext = !$2;
+ OSPF_CFG->gr_mode = OSPF_GR_AWARE;
+ OSPF_CFG->gr_time = OSPF_DEFAULT_GR_TIME;
};
ospf_proto:
@@ -258,6 +261,9 @@ ospf_proto_item:
| RFC5838 bool { OSPF_CFG->af_ext = $2; if (!ospf_cfg_is_v3()) cf_error("RFC5838 option requires OSPFv3"); }
| VPN PE bool { OSPF_CFG->vpn_pe = $3; }
| STUB ROUTER bool { OSPF_CFG->stub_router = $3; }
+ | GRACEFUL RESTART bool { OSPF_CFG->gr_mode = $3; }
+ | GRACEFUL RESTART AWARE { OSPF_CFG->gr_mode = OSPF_GR_AWARE; }
+ | GRACEFUL RESTART TIME expr { OSPF_CFG->gr_time = $4; if (($4 < 1) || ($4 > 1800)) cf_error("Graceful restart time must be in range 1-1800"); }
| ECMP bool { OSPF_CFG->ecmp = $2 ? OSPF_DEFAULT_ECMP_LIMIT : 0; }
| ECMP bool LIMIT expr { OSPF_CFG->ecmp = $2 ? $4 : 0; }
| MERGE EXTERNAL bool { OSPF_CFG->merge_external = $3; }
diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c
index a1559782..b39595d9 100644
--- a/proto/ospf/dbdes.c
+++ b/proto/ospf/dbdes.c
@@ -215,7 +215,7 @@ ospf_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n)
ASSERT((n->state == NEIGHBOR_EXSTART) || (n->state == NEIGHBOR_EXCHANGE));
- if (n->ifa->oa->rt == NULL)
+ if (!n->ifa->oa->rt && !p->gr_recovery)
return;
ospf_prepare_dbdes(p, n);
@@ -279,6 +279,10 @@ ospf_process_dbdes(struct ospf_proto *p, struct ospf_packet *pkt, struct ospf_ne
if (LSA_SCOPE(lsa_type) == LSA_SCOPE_RES)
DROP1("LSA with invalid scope");
+ /* RFC 3623 2.2 (2) special case - check for my router-LSA (GR recovery) */
+ if ((lsa_type == LSA_T_RT) && (lsa.rt == p->router_id))
+ n->got_my_rt_lsa = 1;
+
en = ospf_hash_find(p->gr, lsa_domain, lsa.id, lsa.rt, lsa_type);
if (!en || (lsa_comp(&lsa, &(en->lsa)) == CMP_NEWER))
{
diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c
index 388c91c8..f5c69199 100644
--- a/proto/ospf/iface.c
+++ b/proto/ospf/iface.c
@@ -772,6 +772,14 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
ifa->cf = new;
ifa->marked = 0;
+ /* Cancel GR peers if GR is disabled */
+ if (!p->gr_mode && p->gr_count)
+ {
+ struct ospf_neighbor *n, *nx;
+ WALK_LIST_DELSAFE(n, nx, ifa->neigh_list)
+ if (n->gr_active)
+ ospf_neigh_cancel_graceful_restart(n);
+ }
/* HELLO TIMER */
if (ifa->helloint != new->helloint)
diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c
index 7ddf64e3..7767700f 100644
--- a/proto/ospf/lsalib.c
+++ b/proto/ospf/lsalib.c
@@ -12,6 +12,9 @@
#include "lib/fletcher16.h"
+#define HDRLEN sizeof(struct ospf_lsa_header)
+
+
#ifndef CPU_BIG_ENDIAN
void
lsa_hton_hdr(struct ospf_lsa_header *h, struct ospf_lsa_header *n)
@@ -61,7 +64,6 @@ lsa_ntoh_body(void *n, void *h, u16 len)
#endif /* little endian */
-
int
lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa)
{
@@ -147,11 +149,13 @@ static const u16 lsa_v2_types[] = {
/* Maps OSPFv2 opaque types to OSPFv3 function codes */
static const u16 opaque_lsa_types[] = {
+ [LSA_OT_GR] = LSA_T_GR,
[LSA_OT_RI] = LSA_T_RI_,
};
/* Maps (subset of) OSPFv3 function codes to OSPFv2 opaque types */
static const u8 opaque_lsa_types_inv[] = {
+ [LSA_T_GR] = LSA_OT_GR,
[LSA_T_RI_] = LSA_OT_RI,
};
@@ -168,7 +172,13 @@ lsa_get_type_domain_(u32 type, u32 id, struct ospf_iface *ifa, u32 *otype, u32 *
uint code;
if (LSA_FUNCTION(type) == LSA_T_OPAQUE_)
if (code = LOOKUP(opaque_lsa_types, id >> 24))
+ {
type = code | LSA_UBIT | LSA_SCOPE(type);
+
+ /* Hack for Grace-LSA: It does not use U-bit for link-scoped LSAs */
+ if (type == (LSA_T_GR | LSA_UBIT))
+ type = LSA_T_GR;
+ }
}
else
{
@@ -196,6 +206,13 @@ lsa_get_type_domain_(u32 type, u32 id, struct ospf_iface *ifa, u32 *otype, u32 *
}
}
+int
+lsa_is_opaque(u32 type)
+{
+ u32 fn = LSA_FUNCTION(type);
+ return LOOKUP(opaque_lsa_types_inv, fn) || (fn == LSA_T_OPAQUE_);
+}
+
u32
lsa_get_opaque_type(u32 type)
{
@@ -267,6 +284,51 @@ lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2)
}
+#define LSA_TLV_LENGTH(tlv) \
+ (sizeof(struct ospf_tlv) + BIRD_ALIGN((tlv)->length, 4))
+
+#define LSA_NEXT_TLV(tlv) \
+ ((struct ospf_tlv *) ((byte *) (tlv) + LSA_TLV_LENGTH(tlv)))
+
+#define LSA_WALK_TLVS(tlv,buf,len) \
+ for(struct ospf_tlv *tlv = (void *) (buf); \
+ (byte *) tlv < (byte *) (buf) + (len); \
+ tlv = LSA_NEXT_TLV(tlv))
+
+struct ospf_tlv *
+lsa_get_tlv(struct top_hash_entry *en, uint type)
+{
+ LSA_WALK_TLVS(tlv, en->lsa_body, en->lsa.length - HDRLEN)
+ if (tlv->type == type)
+ return tlv;
+
+ return NULL;
+}
+
+int
+lsa_validate_tlvs(byte *buf, uint len)
+{
+ byte *pos = buf;
+ byte *end = buf + len;
+
+ while (pos < end)
+ {
+ if ((pos + sizeof(struct ospf_tlv)) > end)
+ return 0;
+
+ struct ospf_tlv *tlv = (void *) pos;
+ uint len = LSA_TLV_LENGTH(tlv);
+
+ if ((pos + len) > end)
+ return 0;
+
+ pos += len;
+ }
+
+ return 1;
+}
+
+
static inline int
lsa_walk_rt2(struct ospf_lsa_rt_walk *rt)
{
@@ -408,7 +470,6 @@ lsa_parse_ext(struct top_hash_entry *en, int ospf2, int af, struct ospf_lsa_ext_
}
}
-#define HDRLEN sizeof(struct ospf_lsa_header)
static int
lsa_validate_rt2(struct ospf_lsa_header *lsa, struct ospf_lsa_rt *body)
@@ -604,6 +665,12 @@ lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *body)
}
static int
+lsa_validate_gr(struct ospf_lsa_header *lsa, void *body)
+{
+ return lsa_validate_tlvs(body, lsa->length - HDRLEN);
+}
+
+static int
lsa_validate_ri(struct ospf_lsa_header *lsa UNUSED, struct ospf_lsa_net *body UNUSED)
{
/*
@@ -643,6 +710,8 @@ lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body)
case LSA_T_EXT:
case LSA_T_NSSA:
return lsa_validate_ext2(lsa, body);
+ case LSA_T_GR:
+ return lsa_validate_gr(lsa, body);
case LSA_T_RI_LINK:
case LSA_T_RI_AREA:
case LSA_T_RI_AS:
@@ -674,6 +743,8 @@ lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body)
return lsa_validate_link(lsa, body);
case LSA_T_PREFIX:
return lsa_validate_prefix(lsa, body);
+ case LSA_T_GR:
+ return lsa_validate_gr(lsa, body);
case LSA_T_RI_LINK:
case LSA_T_RI_AREA:
case LSA_T_RI_AS:
diff --git a/proto/ospf/lsalib.h b/proto/ospf/lsalib.h
index af8901ce..eca138d7 100644
--- a/proto/ospf/lsalib.h
+++ b/proto/ospf/lsalib.h
@@ -44,10 +44,7 @@ static inline void lsa_get_type_domain(struct ospf_lsa_header *lsa, struct ospf_
static inline u32 lsa_get_etype(struct ospf_lsa_header *h, struct ospf_proto *p)
{ return ospf_is_v2(p) ? (h->type_raw & LSA_T_V2_MASK) : h->type_raw; }
-/* Assuming OSPFv2 - All U-bit LSAs are mapped to Opaque LSAs */
-static inline int lsa_is_opaque(u32 type)
-{ return !!(type & LSA_UBIT); }
-
+int lsa_is_opaque(u32 type);
u32 lsa_get_opaque_type(u32 type);
int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa);
int lsa_is_acceptable(u32 type, struct ospf_neighbor *n, struct ospf_proto *p);
@@ -58,6 +55,16 @@ u16 lsa_verify_checksum(const void *lsa_n, int lsa_len);
#define CMP_SAME 0
#define CMP_OLDER -1
int lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2);
+
+struct ospf_tlv * lsa_get_tlv(struct top_hash_entry *en, uint type);
+
+static inline u32
+lsa_get_tlv_u32(struct top_hash_entry *en, uint type)
+{
+ struct ospf_tlv *tlv = lsa_get_tlv(en, type);
+ return (tlv && (tlv->length == 4)) ? tlv->data[0] : 0;
+}
+
void lsa_walk_rt_init(struct ospf_proto *po, struct top_hash_entry *act, struct ospf_lsa_rt_walk *rt);
int lsa_walk_rt(struct ospf_lsa_rt_walk *rt);
void lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, int af, net_addr *net, u8 *pxopts, u32 *metric);
diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c
index 7318b751..fafe4872 100644
--- a/proto/ospf/lsupd.c
+++ b/proto/ospf/lsupd.c
@@ -185,6 +185,13 @@ static int ospf_flood_lsupd(struct ospf_proto *p, struct top_hash_entry **lsa_li
static void
ospf_enqueue_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_iface *ifa)
{
+ /* Exception for local Grace-LSA, they are flooded synchronously */
+ if ((en->lsa_type == LSA_T_GR) && (en->lsa.rt == p->router_id))
+ {
+ ospf_flood_lsupd(p, &en, 1, 1, ifa);
+ return;
+ }
+
if (ifa->flood_queue_used == ifa->flood_queue_size)
{
/* If we already have full queue, we send some packets */
@@ -591,8 +598,9 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa,
}
/* 13. (5f) - handle self-originated LSAs, see also 13.4. */
- if ((lsa.rt == p->router_id) ||
- (ospf_is_v2(p) && (lsa_type == LSA_T_NET) && ospf_addr_is_local(p, ifa->oa, ipa_from_u32(lsa.id))))
+ if (!p->gr_recovery &&
+ ((lsa.rt == p->router_id) ||
+ (ospf_is_v2(p) && (lsa_type == LSA_T_NET) && ospf_addr_is_local(p, ifa->oa, ipa_from_u32(lsa.id)))))
{
OSPF_TRACE(D_EVENTS, "Received unexpected self-originated LSA");
ospf_advance_lsa(p, en, &lsa, lsa_type, lsa_domain, body);
@@ -629,6 +637,14 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa,
if (lsa_type == LSA_T_LINK)
ospf_notify_net_lsa(ifa);
+ /* RFC 3623 3.1 - entering graceful restart helper mode */
+ if (lsa_type == LSA_T_GR)
+ ospf_neigh_notify_grace_lsa(n, en);
+
+ /* Link received pre-restart router LSA */
+ if (p->gr_recovery && (lsa_type == LSA_T_RT) && (lsa.rt == p->router_id))
+ ifa->oa->rt = en;
+
/* 13. (5b) - flood new LSA */
int flood_back = ospf_flood_lsa(p, en, n);
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; }
diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c
index f3eabb47..968d7aa0 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -92,7 +92,9 @@
* - RFC 2328 - main OSPFv2 standard
* - RFC 5340 - main OSPFv3 standard
* - RFC 3101 - OSPFv2 NSSA areas
+ * - RFC 3623 - OSPFv2 Graceful Restart
* - RFC 4576 - OSPFv2 VPN loop prevention
+ * - RFC 5187 - OSPFv3 Graceful Restart
* - RFC 5250 - OSPFv2 Opaque LSAs
* - RFC 5709 - OSPFv2 HMAC-SHA Cryptographic Authentication
* - RFC 5838 - OSPFv3 Support of Address Families
@@ -207,7 +209,6 @@ ospf_area_remove(struct ospf_area *oa)
mb_free(oa);
}
-
struct ospf_area *
ospf_find_area(struct ospf_proto *p, u32 aid)
{
@@ -228,6 +229,37 @@ ospf_find_vlink(struct ospf_proto *p, u32 voa, u32 vid)
return NULL;
}
+static void
+ospf_start_gr_recovery(struct ospf_proto *p)
+{
+ OSPF_TRACE(D_EVENTS, "Graceful restart started");
+
+ p->gr_recovery = 1;
+ p->gr_timeout = current_time() + (p->gr_time S);
+ channel_graceful_restart_lock(p->p.main_channel);
+ p->p.main_channel->gr_wait = 1;
+
+ /* NOTE: We should get end of grace period from non-volatile storage */
+}
+
+void
+ospf_stop_gr_recovery(struct ospf_proto *p)
+{
+ p->gr_recovery = 0;
+ p->gr_timeout = 0;
+ channel_graceful_restart_unlock(p->p.main_channel);
+
+ /* Reorigination of router/network LSAs is already scheduled */
+ ospf_mark_lsadb(p);
+
+ /*
+ * NOTE: We should move channel_graceful_restart_unlock() to the end of
+ * ospf_disp() in order to have local LSA reorigination / LSAdb cleanup /
+ * routing table recomputation before official end of GR. It does not matter
+ * when we are single-threaded.
+ */
+}
+
static int
ospf_start(struct proto *P)
{
@@ -246,6 +278,8 @@ ospf_start(struct proto *P)
p->asbr = c->asbr;
p->vpn_pe = c->vpn_pe;
p->ecmp = c->ecmp;
+ p->gr_mode = c->gr_mode;
+ p->gr_time = c->gr_time;
p->tick = c->tick;
p->disp_timer = tm_new_init(P->pool, ospf_disp, p, p->tick S, 0);
tm_start(p->disp_timer, 100 MS);
@@ -267,6 +301,10 @@ ospf_start(struct proto *P)
p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 };
p->log_lsa_tbf = (struct tbf){ .rate = 4, .burst = 20 };
+ /* Lock the channel when in GR recovery mode */
+ if (p->p.gr_recovery && (p->gr_mode == OSPF_GR_ABLE))
+ ospf_start_gr_recovery(p);
+
WALK_LIST(ac, c->area_list)
ospf_area_add(p, ac);
@@ -398,6 +436,9 @@ ospf_disp(timer * timer)
{
struct ospf_proto *p = timer->data;
+ if (p->gr_recovery)
+ ospf_update_gr_recovery(p);
+
/* Originate or flush local topology LSAs */
ospf_update_topology(p);
@@ -475,9 +516,18 @@ ospf_shutdown(struct proto *P)
OSPF_TRACE(D_EVENTS, "Shutdown requested");
- /* And send to all my neighbors 1WAY */
- WALK_LIST(ifa, p->iface_list)
- ospf_iface_shutdown(ifa);
+ if ((P->down_code == PDC_CMD_GR_DOWN) && (p->gr_mode == OSPF_GR_ABLE))
+ {
+ /* Originate Grace LSAs */
+ WALK_LIST(ifa, p->iface_list)
+ ospf_originate_gr_lsa(p, ifa);
+ }
+ else
+ {
+ /* Send to all my neighbors 1WAY */
+ WALK_LIST(ifa, p->iface_list)
+ ospf_iface_shutdown(ifa);
+ }
/* Cleanup locked rta entries */
FIB_WALK(&p->rtf, ort, nf)
@@ -664,6 +714,8 @@ ospf_reconfigure(struct proto *P, struct proto_config *CF)
p->merge_external = new->merge_external;
p->asbr = new->asbr;
p->ecmp = new->ecmp;
+ p->gr_mode = new->gr_mode;
+ p->gr_time = new->gr_time;
p->tick = new->tick;
p->disp_timer->recurrent = p->tick S;
tm_start(p->disp_timer, 10 MS);
diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h
index 7fac47c8..aac13512 100644
--- a/proto/ospf/ospf.h
+++ b/proto/ospf/ospf.h
@@ -75,6 +75,7 @@
#define OSPF_DEFAULT_TICK 1
#define OSPF_DEFAULT_STUB_COST 1000
#define OSPF_DEFAULT_ECMP_LIMIT 16
+#define OSPF_DEFAULT_GR_TIME 120
#define OSPF_DEFAULT_TRANSINT 40
#define OSPF_MIN_PKT_SIZE 256
@@ -82,6 +83,9 @@
#define OSPF_VLINK_ID_OFFSET 0x80000000
+#define OSPF_GR_ABLE 1
+#define OSPF_GR_AWARE 2
+
struct ospf_config
{
struct proto_config c;
@@ -97,7 +101,9 @@ struct ospf_config
u8 abr;
u8 asbr;
u8 vpn_pe;
- int ecmp;
+ u8 gr_mode; /* Graceful restart mode (OSPF_GR_*) */
+ uint gr_time; /* Graceful restart interval */
+ uint ecmp;
list area_list; /* list of area configs (struct ospf_area_config) */
list vlink_list; /* list of configured vlinks (struct ospf_iface_patt) */
};
@@ -216,6 +222,9 @@ struct ospf_proto
list area_list; /* List of OSPF areas (struct ospf_area) */
int areano; /* Number of area I belong to */
int padj; /* Number of neighbors in Exchange or Loading state */
+ int gr_count; /* Number of neighbors in graceful restart state */
+ int gr_recovery; /* Graceful restart recovery is active */
+ btime gr_timeout; /* The end time of grace restart recovery */
struct fib rtf; /* Routing table */
struct idm idm; /* OSPFv3 LSA ID map */
u8 ospf2; /* OSPF v2 or v3 */
@@ -228,6 +237,8 @@ struct ospf_proto
u8 asbr; /* May i originate any ext/NSSA lsa? */
u8 vpn_pe; /* Should we do VPN PE specific behavior (RFC 4577)? */
u8 ecmp; /* Maximal number of nexthops in ECMP route, or 0 */
+ u8 gr_mode; /* Graceful restart mode (OSPF_GR_*) */
+ uint gr_time; /* Graceful restart interval */
u64 csn64; /* Last used cryptographic sequence number */
struct ospf_area *backbone; /* If exists */
event *flood_event; /* Event for flooding LS updates */
@@ -346,6 +357,8 @@ struct ospf_neighbor
pool *pool;
struct ospf_iface *ifa;
u8 state;
+ u8 gr_active; /* We act as GR helper for the neighbor */
+ u8 got_my_rt_lsa; /* Received my Rt-LSA in DBDES exchanged */
timer *inactim; /* Inactivity timer */
u8 imms; /* I, M, Master/slave received */
u8 myimms; /* I, M Master/slave */
@@ -388,6 +401,7 @@ struct ospf_neighbor
#define ACKL_DIRECT 0
#define ACKL_DELAY 1
timer *ackd_timer; /* Delayed ack timer */
+ timer *gr_timer; /* Graceful restart timer, non-NULL only if gr_active */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
void *ldd_buffer; /* Last database description packet */
u32 ldd_bsize; /* Buffer size for ldd_buffer */
@@ -555,6 +569,7 @@ struct ospf_auth3
#define LSA_T_NSSA 0x2007
#define LSA_T_LINK 0x0008
#define LSA_T_PREFIX 0x2009
+#define LSA_T_GR 0x000B
#define LSA_T_RI_ 0x000C
#define LSA_T_RI_LINK 0x800C
#define LSA_T_RI_AREA 0xA00C
@@ -569,6 +584,7 @@ struct ospf_auth3
/* OSPFv2 Opaque LSA Types */
/* https://www.iana.org/assignments/ospf-opaque-types/ospf-opaque-types.xhtml#ospf-opaque-types-2 */
+#define LSA_OT_GR 0x03
#define LSA_OT_RI 0x04
#define LSA_FUNCTION_MASK 0x1FFF
@@ -613,6 +629,12 @@ struct ospf_auth3
#define LSA_EXT3_FBIT 0x02000000
#define LSA_EXT3_TBIT 0x01000000
+/* OSPF Grace LSA (GR) TLVs */
+/* https://www.iana.org/assignments/ospfv2-parameters/ospfv2-parameters.xhtml#ospfv2-parameters-13 */
+#define LSA_GR_PERIOD 1
+#define LSA_GR_REASON 2
+#define LSA_GR_ADDRESS 3
+
/* OSPF Router Information (RI) TLVs */
/* https://www.iana.org/assignments/ospf-parameters/ospf-parameters.xhtml#ri-tlv */
#define LSA_RI_RIC 1
@@ -959,6 +981,8 @@ static inline int oa_is_ext(struct ospf_area *oa)
static inline int oa_is_nssa(struct ospf_area *oa)
{ return oa->options & OPT_N; }
+void ospf_stop_gr_recovery(struct ospf_proto *p);
+
void ospf_sh_neigh(struct proto *P, char *iff);
void ospf_sh(struct proto *P);
void ospf_sh_iface(struct proto *P, char *iff);
@@ -990,12 +1014,18 @@ static inline struct nbma_node * find_nbma_node(struct ospf_iface *ifa, ip_addr
/* neighbor.c */
struct ospf_neighbor *ospf_neighbor_new(struct ospf_iface *ifa);
void ospf_neigh_sm(struct ospf_neighbor *n, int event);
+void ospf_neigh_cancel_graceful_restart(struct ospf_neighbor *n);
+void ospf_neigh_notify_grace_lsa(struct ospf_neighbor *n, struct top_hash_entry *en);
+void ospf_neigh_lsadb_changed_(struct ospf_proto *p, struct top_hash_entry *en);
void ospf_dr_election(struct ospf_iface *ifa);
struct ospf_neighbor *find_neigh(struct ospf_iface *ifa, u32 rid);
struct ospf_neighbor *find_neigh_by_ip(struct ospf_iface *ifa, ip_addr ip);
void ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd);
void ospf_sh_neigh_info(struct ospf_neighbor *n);
+static inline void ospf_neigh_lsadb_changed(struct ospf_proto *p, struct top_hash_entry *en)
+{ if (p->gr_count) ospf_neigh_lsadb_changed_(p, en); }
+
/* packet.c */
void ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type);
int ospf_rx_hook(sock * sk, uint size);
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;
+}
diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h
index 589d2bc5..094e125b 100644
--- a/proto/ospf/rt.h
+++ b/proto/ospf/rt.h
@@ -130,6 +130,7 @@ static inline int rt_is_nssa(ort *nf)
void ospf_rt_spf(struct ospf_proto *p);
void ospf_rt_initort(struct fib_node *fn);
+void ospf_update_gr_recovery(struct ospf_proto *p);
#endif /* _BIRD_OSPF_RT_H_ */
diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c
index 1579f496..efd03b54 100644
--- a/proto/ospf/topology.c
+++ b/proto/ospf/topology.c
@@ -83,7 +83,10 @@ ospf_install_lsa(struct ospf_proto *p, struct ospf_lsa_header *lsa, u32 type, u3
en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn, en->lsa.age);
if (change)
+ {
+ ospf_neigh_lsadb_changed(p, en);
ospf_schedule_rtcalc(p);
+ }
return en;
}
@@ -243,6 +246,7 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa
en->lsa.age = 0;
en->init_age = 0;
en->inst_time = current_time();
+ en->dirty = 0;
lsa_generate_checksum(&en->lsa, en->lsa_body);
OSPF_TRACE(D_EVENTS, "Originating LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
@@ -251,7 +255,10 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa
ospf_flood_lsa(p, en, NULL);
if (en->mode == LSA_M_BASIC)
+ {
+ ospf_neigh_lsadb_changed(p, en);
ospf_schedule_rtcalc(p);
+ }
return 1;
}
@@ -321,7 +328,8 @@ ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa)
if ((en->lsa.age < LSA_MAXAGE) &&
(lsa_length == en->lsa.length) &&
!memcmp(lsa_body, en->lsa_body, lsa_blen) &&
- (!ospf_is_v2(p) || (lsa->opts == lsa_get_options(&en->lsa))))
+ (!ospf_is_v2(p) || (lsa->opts == lsa_get_options(&en->lsa))) &&
+ !en->dirty)
goto drop;
lsa_body = lsab_flush(p);
@@ -433,7 +441,10 @@ ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en)
ospf_flood_lsa(p, en, NULL);
if (en->mode == LSA_M_BASIC)
+ {
+ ospf_neigh_lsadb_changed(p, en);
ospf_schedule_rtcalc(p);
+ }
en->mode = LSA_M_BASIC;
}
@@ -509,6 +520,12 @@ ospf_update_lsadb(struct ospf_proto *p)
continue;
}
+ if (en->dirty)
+ {
+ ospf_flush_lsa(p, en);
+ continue;
+ }
+
if ((en->lsa.rt == p->router_id) && (real_age >= LSREFRESHTIME))
{
ospf_refresh_lsa(p, en);
@@ -525,6 +542,16 @@ ospf_update_lsadb(struct ospf_proto *p)
}
}
+void
+ospf_mark_lsadb(struct ospf_proto *p)
+{
+ struct top_hash_entry *en;
+
+ /* Mark all local LSAs as dirty */
+ WALK_SLIST(en, p->lsal)
+ if (en->lsa.rt == p->router_id)
+ en->dirty = 1;
+}
static u32
ort_to_lsaid(struct ospf_proto *p, ort *nf)
@@ -1676,6 +1703,59 @@ ospf_originate_prefix_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa)
/*
+ * Grace LSA handling
+ * Type = LSA_T_GR, opaque type = LSA_OT_GR
+ */
+
+static inline void
+ospf_add_gr_period_tlv(struct ospf_proto *p, uint period)
+{
+ struct ospf_tlv *tlv = lsab_allocz(p, sizeof(struct ospf_tlv) + sizeof(u32));
+ tlv->type = LSA_GR_PERIOD;
+ tlv->length = 4;
+ tlv->data[0] = period;
+}
+
+static inline void
+ospf_add_gr_reason_tlv(struct ospf_proto *p, uint reason)
+{
+ struct ospf_tlv *tlv = lsab_allocz(p, sizeof(struct ospf_tlv) + sizeof(u32));
+ tlv->type = LSA_GR_REASON;
+ tlv->length = 1;
+ tlv->data[0] = reason << 24;
+}
+
+static inline void
+ospf_add_gr_address_tlv(struct ospf_proto *p, ip4_addr addr)
+{
+ struct ospf_tlv *tlv = lsab_allocz(p, sizeof(struct ospf_tlv) + sizeof(u32));
+ tlv->type = LSA_GR_ADDRESS;
+ tlv->length = 4;
+ tlv->data[0] = ip4_to_u32(addr);
+}
+
+void
+ospf_originate_gr_lsa(struct ospf_proto *p, struct ospf_iface *ifa)
+{
+ struct ospf_new_lsa lsa = {
+ .type = LSA_T_GR,
+ .dom = ifa->iface_id,
+ .id = ospf_is_v2(p) ? 0 : ifa->iface_id,
+ .ifa = ifa
+ };
+
+ ospf_add_gr_period_tlv(p, p->gr_time);
+ ospf_add_gr_reason_tlv(p, 0);
+
+ uint t = ifa->type;
+ if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP)))
+ ospf_add_gr_address_tlv(p, ipa_to_ip4(ifa->addr->ip));
+
+ ospf_originate_lsa(p, &lsa);
+}
+
+
+/*
* Router Information LSA handling
* Type = LSA_T_RI_AREA, opaque type = LSA_OT_RI
*/
@@ -1718,6 +1798,10 @@ ospf_update_topology(struct ospf_proto *p)
struct ospf_area *oa;
struct ospf_iface *ifa;
+ /* No LSA reorigination during GR recovery */
+ if (p->gr_recovery)
+ return;
+
WALK_LIST(oa, p->area_list)
{
if (oa->update_rt_lsa)
diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h
index fd70239d..ffae436a 100644
--- a/proto/ospf/topology.h
+++ b/proto/ospf/topology.h
@@ -33,6 +33,7 @@ struct top_hash_entry
u32 lb_id; /* Interface ID of link back iface (for bcast or NBMA networks) */
u32 dist; /* Distance from the root */
int ret_count; /* Number of retransmission lists referencing the entry */
+ u8 dirty; /* Will be flushed during next LSAdb update unless reoriginated*/
u8 color;
#define OUTSPF 0
#define CANDIDATE 1
@@ -180,6 +181,7 @@ struct top_hash_entry * ospf_originate_lsa(struct ospf_proto *p, struct ospf_new
void ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body);
void ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en);
void ospf_update_lsadb(struct ospf_proto *p);
+void ospf_mark_lsadb(struct ospf_proto *p);
static inline void ospf_flush2_lsa(struct ospf_proto *p, struct top_hash_entry **en)
{ if (*en) { ospf_flush_lsa(p, *en); *en = NULL; } }
@@ -187,6 +189,7 @@ static inline void ospf_flush2_lsa(struct ospf_proto *p, struct top_hash_entry *
void ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric);
void ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, u32 drid, int metric, u32 options);
void ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 mode, u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit, int dn);
+void ospf_originate_gr_lsa(struct ospf_proto *p, struct ospf_iface *ifa);
void ospf_rt_notify(struct proto *P, struct channel *ch, net *n, rte *new, rte *old);
void ospf_update_topology(struct ospf_proto *p);