From: rgirada Date: Fri, 21 Aug 2020 18:14:43 +0000 (-0700) Subject: ospfd: Grace LSA processing X-Git-Tag: base_7.6~433^2~5 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=ad6869921751c4c33874db2ecf7164aacb8662df;p=mirror%2Ffrr.git ospfd: Grace LSA processing Description: 1. Grace LSA processing. 2. Validations to become a Helper. rfc3623 section 3.1 Signed-off-by: Rajesh Girada --- diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index 58afb2b392..5acae555ef 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -337,6 +337,45 @@ int ospf_flood(struct ospf *ospf, struct ospf_neighbor *nbr, SET_FLAG(new->flags, OSPF_LSA_RECEIVED); (void)ospf_lsa_is_self_originated(ospf, new); /* Let it set the flag */ + /* Received Grace LSA */ + if (IS_GRACE_LSA(new)) { + + if (IS_LSA_MAXAGE(new)) { + + /* Handling Max age grace LSA.*/ + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, Received a maxage GRACE-LSA from router %s", + __PRETTY_FUNCTION__, + inet_ntoa(new->data->adv_router)); + + if (current) { + ospf_process_maxage_grace_lsa(ospf, new, nbr); + } else { + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, Grace LSA doesn't exist in lsdb, so discarding grace lsa", + __PRETTY_FUNCTION__); + return -1; + } + } else { + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, Received a GRACE-LSA from router %s", + __PRETTY_FUNCTION__, + inet_ntoa(new->data->adv_router)); + + if (ospf_process_grace_lsa(ospf, new, nbr) + == OSPF_GR_NOT_HELPER) { + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, Not moving to HELPER role, So discarding grace LSA", + __PRETTY_FUNCTION__); + return -1; + } + } + } + /* Install the new LSA in the link state database (replacing the current database copy). This may cause the routing table calculation to be scheduled. In addition, diff --git a/ospfd/ospf_gr_helper.c b/ospfd/ospf_gr_helper.c index 8bb740a155..ea68d6acd6 100644 --- a/ospfd/ospf_gr_helper.c +++ b/ospfd/ospf_gr_helper.c @@ -50,6 +50,31 @@ #include "ospfd/ospf_ism.h" #include "ospfd/ospf_gr_helper.h" +const char *ospf_exit_reason_desc[] = { + "Unknown reason", + "Helper inprogress", + "Topology Change", + "Grace timer expairy", + "Successful graceful restart", +}; + +const char *ospf_restart_reason_desc[] = { + "Unknown restart", + "Software restart", + "Software reload/upgrade", + "Switch to redundant control processor", +}; + +const char *ospf_rejected_reason_desc[] = { + "Unknown reason", + "Helper support disabled", + "Neighbour is not in FULL state", + "Supports only planned restart but received for unplanned", + "Topo change due to change in lsa rxmt list", + "LSA age is more than Grace interval", +}; + +static bool ospf_check_change_in_rxmt_list(struct ospf_neighbor *nbr); static unsigned int ospf_enable_rtr_hash_key(const void *data) { @@ -105,9 +130,9 @@ void ospf_gr_helper_init(struct ospf *ospf) if (IS_DEBUG_OSPF_GR_HELPER) zlog_debug("%s, GR Helper init.", __PRETTY_FUNCTION__); - ospf->is_helper_supported = OSPF_FALSE; - ospf->strict_lsa_check = OSPF_TRUE; - ospf->only_planned_restart = OSPF_FALSE; + ospf->is_helper_supported = OSPF_GR_FALSE; + ospf->strict_lsa_check = OSPF_GR_TRUE; + ospf->only_planned_restart = OSPF_GR_FALSE; ospf->supported_grace_time = OSPF_MAX_GRACE_INTERVAL; ospf->last_exit_reason = OSPF_GR_HELPER_EXIT_NONE; ospf->active_restarter_cnt = 0; @@ -134,3 +159,379 @@ void ospf_gr_helper_stop(struct ospf *ospf) ospf_enable_rtr_hash_destroy(ospf); } + +/* + * Extracting tlv info from GRACE LSA. + * + * lsa + * ospf grace lsa + * + * Returns: + * interval : grace interval. + * addr : RESTARTER address. + * reason : Restarting reason. + */ +static int ospf_extract_grace_lsa_fields(struct ospf_lsa *lsa, + uint32_t *interval, + struct in_addr *addr, uint8_t *reason) +{ + struct lsa_header *lsah = NULL; + struct tlv_header *tlvh = NULL; + struct grace_tlv_graceperiod *grace_period; + struct grace_tlv_restart_reason *gr_reason; + struct grace_tlv_restart_addr *restart_addr; + uint16_t length = 0; + int sum = 0; + + lsah = (struct lsa_header *)lsa->data; + + length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE; + + for (tlvh = TLV_HDR_TOP(lsah); sum < length; + tlvh = TLV_HDR_NEXT(tlvh)) { + switch (ntohs(tlvh->type)) { + case GRACE_PERIOD_TYPE: + grace_period = (struct grace_tlv_graceperiod *)tlvh; + *interval = ntohl(grace_period->interval); + sum += TLV_SIZE(tlvh); + + /* Check if grace interval is valid */ + if (*interval > OSPF_MAX_GRACE_INTERVAL + || *interval < OSPF_MIN_GRACE_INTERVAL) + return OSPF_GR_FAILURE; + break; + case RESTART_REASON_TYPE: + gr_reason = (struct grace_tlv_restart_reason *)tlvh; + *reason = gr_reason->reason; + sum += TLV_SIZE(tlvh); + + if (*reason >= OSPF_GR_INVALID_REASON_CODE) + return OSPF_GR_FAILURE; + break; + case RESTARTER_IP_ADDR_TYPE: + restart_addr = (struct grace_tlv_restart_addr *)tlvh; + addr->s_addr = restart_addr->addr.s_addr; + sum += TLV_SIZE(tlvh); + break; + default: + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, Malformed packet.Invalid TLV type:%d", + __PRETTY_FUNCTION__, ntohs(tlvh->type)); + return OSPF_GR_FAILURE; + } + } + + return OSPF_GR_SUCCESS; +} + +/* + * Grace timer expiry handler. + * HELPER aborts its role at grace timer expiry. + * + * thread + * thread pointer + * + * Returns: + * Nothing + */ +static int ospf_handle_grace_timer_expiry(struct thread *thread) +{ + struct ospf_neighbor *nbr = THREAD_ARG(thread); + + nbr->gr_helper_info.t_grace_timer = NULL; + + ospf_gr_helper_exit(nbr, OSPF_GR_HELPER_GRACE_TIMEOUT); + return OSPF_GR_SUCCESS; +} + +/* + * Process Grace LSA.If it is eligible move to HELPER role. + * Ref rfc3623 section 3.1 + * + * ospf + * Ospf pointer. + * + * lsa + * Grace LSA received from RESTARTER. + * + * nbr + * ospf neighbour which requets the router to act as + * HELPER. + * + * Returns: + * status. + * If supported as HELPER : OSPF_GR_HELPER_INPROGRESS + * If Not supported as HELPER : OSPF_GR_HELPER_NONE + */ +int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa, + struct ospf_neighbor *nbr) +{ + struct in_addr restart_addr = {0}; + uint8_t restart_reason = 0; + uint32_t grace_interval = 0; + uint32_t actual_grace_interval = 0; + struct advRtr lookup; + struct ospf_neighbor *restarter = NULL; + struct ospf_interface *oi = nbr->oi; + int ret; + + + /* Extract the grace lsa packet fields */ + ret = ospf_extract_grace_lsa_fields(lsa, &grace_interval, &restart_addr, + &restart_reason); + if (ret != OSPF_GR_SUCCESS) { + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug("%s, Wrong Grace LSA packet.", + __PRETTY_FUNCTION__); + return OSPF_GR_NOT_HELPER; + } + + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, Grace LSA received from %s, grace interval:%u, restartreason :%s", + __PRETTY_FUNCTION__, inet_ntoa(restart_addr), + grace_interval, ospf_restart_reason_desc[restart_reason]); + + /* Incase of broadcast links, if RESTARTER is DR_OTHER, + * grace LSA might be received from DR, so need to get + * actual neighbour info , here RESTARTER. + */ + if (oi->type != OSPF_IFTYPE_POINTOPOINT) { + restarter = ospf_nbr_lookup_by_addr(oi->nbrs, &restart_addr); + + if (!restarter) { + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, Restarter is not a nbr(%s) for this router.", + __PRETTY_FUNCTION__, + inet_ntoa(restart_addr)); + return OSPF_GR_NOT_HELPER; + } + } else + restarter = nbr; + + /* Verify Helper enabled globally */ + if (!ospf->is_helper_supported) { + /* Verify that Helper support is enabled for the + * current neighbour router-id. + */ + lookup.advRtrAddr.s_addr = restarter->router_id.s_addr; + + if (!hash_lookup(ospf->enable_rtr_list, &lookup)) { + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, HELPER support is disabled, So not a HELPER", + __PRETTY_FUNCTION__); + restarter->gr_helper_info.rejected_reason = + OSPF_HELPER_SUPPORT_DISABLED; + return OSPF_GR_NOT_HELPER; + } + } + + + /* Check neighbour is in FULL state and + * became a adjacency. + */ + if (!IS_NBR_STATE_FULL(restarter)) { + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, This Neighbour %s is not in FULL state.", + __PRETTY_FUNCTION__, inet_ntoa(restarter->src)); + restarter->gr_helper_info.rejected_reason = + OSPF_HELPER_NOT_A_VALID_NEIGHBOUR; + return OSPF_GR_NOT_HELPER; + } + + /* Based on the restart reason from grace lsa + * check the current router is supporting or not + */ + if (ospf->only_planned_restart + && !OSPF_GR_IS_PLANNED_RESTART(restart_reason)) { + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, Router supports only planned restarts but received the GRACE LSA for an unplanned restart.", + __PRETTY_FUNCTION__); + restarter->gr_helper_info.rejected_reason = + OSPF_HELPER_PLANNED_ONLY_RESTART; + return OSPF_GR_NOT_HELPER; + } + + /* Check the retranmission list of this + * neighbour, check any change in lsas. + */ + if (ospf->strict_lsa_check && !ospf_ls_retransmit_isempty(restarter) + && ospf_check_change_in_rxmt_list(restarter)) { + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, Changed LSA in Rxmt list. So not Helper.", + __PRETTY_FUNCTION__); + restarter->gr_helper_info.rejected_reason = + OSPF_HELPER_TOPO_CHANGE_RTXMT_LIST; + return OSPF_GR_NOT_HELPER; + } + + /*LSA age must be less than the grace period */ + if (ntohs(lsa->data->ls_age) >= grace_interval) { + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, Grace LSA age(%d) is more than the graceinterval(%d)", + __PRETTY_FUNCTION__, lsa->data->ls_age, + grace_interval); + restarter->gr_helper_info.rejected_reason = + OSPF_HELPER_LSA_AGE_MORE; + return OSPF_GR_NOT_HELPER; + } + + /* check supported grace period configured + * if configured, use this to start the grace + * timer otherwise use the interval received + * in grace LSA packet. + */ + actual_grace_interval = grace_interval; + if (grace_interval > ospf->supported_grace_time) { + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, Received grace period %d is larger than supported grace %d", + __PRETTY_FUNCTION__, grace_interval, + ospf->supported_grace_time); + actual_grace_interval = ospf->supported_grace_time; + } + + if (OSPF_GR_IS_ACTIVE_HELPER(restarter)) { + if (restarter->gr_helper_info.t_grace_timer) + THREAD_OFF(restarter->gr_helper_info.t_grace_timer); + + if (ospf->active_restarter_cnt > 0) + ospf->active_restarter_cnt--; + + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, Router is already acting as a HELPER for this nbr,so restart the grace timer", + __PRETTY_FUNCTION__); + } else { + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug( + "%s, This Router becomes a HELPER for the neighbour %s", + __PRETTY_FUNCTION__, inet_ntoa(restarter->src)); + } + + /* Became a Helper to the RESTART neighbour. + * Change the helper status. + */ + restarter->gr_helper_info.gr_helper_status = OSPF_GR_ACTIVE_HELPER; + restarter->gr_helper_info.recvd_grace_period = grace_interval; + restarter->gr_helper_info.actual_grace_period = actual_grace_interval; + restarter->gr_helper_info.gr_restart_reason = restart_reason; + restarter->gr_helper_info.rejected_reason = OSPF_HELPER_REJECTED_NONE; + + /* Incremnet the active restarer count */ + ospf->active_restarter_cnt++; + + if (IS_DEBUG_OSPF_GR_HELPER) + zlog_debug("%s, Grace timer started.interval:%d", + __PRETTY_FUNCTION__, actual_grace_interval); + + /* Start the grace timer */ + thread_add_timer(master, ospf_handle_grace_timer_expiry, restarter, + actual_grace_interval, + &restarter->gr_helper_info.t_grace_timer); + + return OSPF_GR_ACTIVE_HELPER; +} + +/* + * API to check any change in the neighbor's + * retransmission list. + * + * nbr + * ospf neighbor + * + * Returns: + * TRUE - if any change in the lsa. + * FALSE - no change in the lsas. + */ +static bool ospf_check_change_in_rxmt_list(struct ospf_neighbor *nbr) +{ + struct route_node *rn; + struct ospf_lsa *lsa; + struct route_table *tbl; + + tbl = nbr->ls_rxmt.type[OSPF_ROUTER_LSA].db; + LSDB_LOOP (tbl, rn, lsa) + if (lsa->to_be_acknowledged) + return OSPF_GR_TRUE; + tbl = nbr->ls_rxmt.type[OSPF_NETWORK_LSA].db; + LSDB_LOOP (tbl, rn, lsa) + if (lsa->to_be_acknowledged) + return OSPF_GR_TRUE; + + tbl = nbr->ls_rxmt.type[OSPF_SUMMARY_LSA].db; + LSDB_LOOP (tbl, rn, lsa) + if (lsa->to_be_acknowledged) + return OSPF_GR_TRUE; + + tbl = nbr->ls_rxmt.type[OSPF_ASBR_SUMMARY_LSA].db; + LSDB_LOOP (tbl, rn, lsa) + if (lsa->to_be_acknowledged) + return OSPF_GR_TRUE; + + tbl = nbr->ls_rxmt.type[OSPF_AS_EXTERNAL_LSA].db; + LSDB_LOOP (tbl, rn, lsa) + if (lsa->to_be_acknowledged) + return OSPF_GR_TRUE; + + tbl = nbr->ls_rxmt.type[OSPF_AS_NSSA_LSA].db; + LSDB_LOOP (tbl, rn, lsa) + if (lsa->to_be_acknowledged) + return OSPF_GR_TRUE; + + return OSPF_GR_FALSE; +} + +/* + * Api to exit from HELPER role to take all actions + * required at exit. + * Ref rfc3623 section 3.2 + * + * ospf + * Ospf pointer. + * + * nbr + * Ospf neighbour for which it is acting as HELPER. + * + * reason + * The reason for exiting from HELPER. + * + * Returns: + * Nothing. + */ +void ospf_gr_helper_exit(struct ospf_neighbor *nbr, + enum ospf_helper_exit_reason reason) +{ + return; +} + +/* + * Process Maxage Grace LSA. + * It is a indication for successfull completion of GR. + * If router acting as HELPER, It exits from helper role. + * + * ospf + * Ospf pointer. + * + * lsa + * Grace LSA received from RESTARTER. + * + * nbr + * ospf neighbour which requets the router to act as + * HELPER. + * + * Returns: + * Nothing. + */ +void ospf_process_maxage_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa, + struct ospf_neighbor *nbr) +{ + return; +} diff --git a/ospfd/ospf_gr_helper.h b/ospfd/ospf_gr_helper.h index ac41243253..21cbb2ac18 100644 --- a/ospfd/ospf_gr_helper.h +++ b/ospfd/ospf_gr_helper.h @@ -154,4 +154,10 @@ struct advRtr { extern void ospf_gr_helper_init(struct ospf *); extern void ospf_gr_helper_stop(struct ospf *ospf); -#endif /* _ZEBRA_OSPF_GR_HELPER_H */ +extern int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa, + struct ospf_neighbor *nbr); +extern void ospf_gr_helper_exit(struct ospf_neighbor *nbr, + enum ospf_helper_exit_reason reason); +void ospf_process_maxage_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa, + struct ospf_neighbor *nbr); +#endif /* _ZEBRA_OSPF_HELPER_H */ diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index 4033659bff..61afb169fb 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -119,6 +119,9 @@ struct ospf_lsa { /* VRF Id */ vrf_id_t vrf_id; + + /*For topo chg detection in HELPER role*/ + bool to_be_acknowledged; }; /* OSPF LSA Link Type. */ diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c index dffbfb7d17..03277ae61c 100644 --- a/ospfd/ospf_nsm.c +++ b/ospfd/ospf_nsm.c @@ -144,7 +144,7 @@ static void nsm_timer_set(struct ospf_neighbor *nbr) /* 10.4 of RFC2328, indicate whether an adjacency is appropriate with * the given neighbour */ -static int nsm_should_adj(struct ospf_neighbor *nbr) +int nsm_should_adj(struct ospf_neighbor *nbr) { struct ospf_interface *oi = nbr->oi; diff --git a/ospfd/ospf_nsm.h b/ospfd/ospf_nsm.h index dcfba84d8b..c219ba7386 100644 --- a/ospfd/ospf_nsm.h +++ b/ospfd/ospf_nsm.h @@ -81,7 +81,7 @@ extern void ospf_check_nbr_loading(struct ospf_neighbor *); extern int ospf_db_summary_isempty(struct ospf_neighbor *); extern int ospf_db_summary_count(struct ospf_neighbor *); extern void ospf_db_summary_clear(struct ospf_neighbor *); - +extern int nsm_should_adj(struct ospf_neighbor *nbr); DECLARE_HOOK(ospf_nsm_change, (struct ospf_neighbor * on, int state, int oldstate), (on, state, oldstate))