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(SECONDARY, 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
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:
| 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; }
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);
if (LSA_SCOPE(lsa_type) == LSA_SCOPE_RES)
DROP1(bad_dbdes, "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))
{
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)
#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)
#endif /* little endian */
-
int
lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa)
{
/* 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,
};
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
{
}
}
+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)
{
}
+#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)
{
}
}
-#define HDRLEN sizeof(struct ospf_lsa_header)
static int
lsa_validate_rt2(struct ospf_lsa_header *lsa, struct ospf_lsa_rt *body)
return lsa_validate_pxlist(lsa, body->pxcount, sizeof(struct ospf_lsa_prefix), (u8 *) 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)
{
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:
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:
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);
#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);
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 */
}
/* 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);
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);
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
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);
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);
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);
}
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);
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; }
* - 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
mb_free(oa);
}
-
struct ospf_area *
ospf_find_area(struct ospf_proto *p, u32 aid)
{
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)
{
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);
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);
{
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);
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)
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);
#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
#define OSPF_VLINK_ID_OFFSET 0x80000000
+#define OSPF_GR_ABLE 1
+#define OSPF_GR_AWARE 2
+
struct ospf_config
{
struct proto_config c;
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) */
};
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 */
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 */
struct ospf_area *backbone; /* If exists */
event *flood_event; /* Event for flooding LS updates */
void *lsab; /* LSA buffer used when originating router LSAs */
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 */
#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 */
#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
/* 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
#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
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);
/* 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);
#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);
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)
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);
}
}
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);
}
}
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;
/* 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;
/* 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;
/* 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;
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)",
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.
+ */
+
+ #undef DROP
+ #define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0)
+ #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;
+}
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_ */
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;
}
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",
ospf_flood_lsa(p, en, NULL);
if (en->mode == LSA_M_BASIC)
+ {
+ ospf_neigh_lsadb_changed(p, en);
ospf_schedule_rtcalc(p);
+ }
return 1;
}
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);
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;
}
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);
}
}
+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)
}
+/*
+ * 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
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)
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
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; } }
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, ea_list *attrs);
void ospf_update_topology(struct ospf_proto *p);