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.
This commit is contained in:
Ondrej Zajicek (work) 2019-06-30 20:12:59 +02:00
parent 8a68316eb9
commit 1a2ad348f6
13 changed files with 660 additions and 26 deletions

View file

@ -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; }

View file

@ -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))
{

View file

@ -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)

View file

@ -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)
@ -603,6 +664,12 @@ lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *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)
{
@ -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:

View file

@ -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);

View file

@ -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);

View file

@ -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; }

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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_ */

View file

@ -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)
@ -1675,6 +1702,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)

View file

@ -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);