From 464e6541b112ba7b3c77dbe8d105fcebb58056dc Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Mon, 12 Apr 2021 17:59:52 -0300 Subject: [PATCH 1/7] lib: fix possible crash on BFD notification Allow sessions to remove themselves from the notifications without risking crashes. Signed-off-by: Rafael Zalamena --- lib/bfd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/bfd.c b/lib/bfd.c index bf3eaa32db..b821094cdd 100644 --- a/lib/bfd.c +++ b/lib/bfd.c @@ -1080,7 +1080,7 @@ static int zclient_bfd_session_reply(ZAPI_CALLBACK_ARGS) static int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS) { - struct bfd_session_params *bsp; + struct bfd_session_params *bsp, *bspn; size_t sessions_updated = 0; struct interface *ifp; int remote_cbit = false; @@ -1137,7 +1137,7 @@ static int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS) now = monotime(NULL); /* Notify all matching sessions about update. */ - TAILQ_FOREACH (bsp, &bsglobal.bsplist, entry) { + TAILQ_FOREACH_SAFE (bsp, &bsglobal.bsplist, entry, bspn) { /* Skip not installed entries. */ if (!bsp->installed) continue; From 1f3e6bf585ac9dcf5a94597c6c44c8aa967393ee Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Mon, 12 Apr 2021 13:48:21 -0300 Subject: [PATCH 2/7] pimd: rework BFD integration Rewrite the BFD integration code to use the new library. Signed-off-by: Rafael Zalamena --- pimd/pim_bfd.c | 289 +++++-------------------------------------- pimd/pim_bfd.h | 35 ++++-- pimd/pim_cmd.c | 47 +------ pimd/pim_iface.h | 8 +- pimd/pim_nb_config.c | 39 +++--- pimd/pim_neighbor.c | 8 +- pimd/pim_neighbor.h | 2 +- 7 files changed, 95 insertions(+), 333 deletions(-) diff --git a/pimd/pim_bfd.c b/pimd/pim_bfd.c index 5e1b9a69e1..2990fcb023 100644 --- a/pimd/pim_bfd.c +++ b/pimd/pim_bfd.c @@ -42,36 +42,36 @@ void pim_bfd_write_config(struct vty *vty, struct interface *ifp) { struct pim_interface *pim_ifp = ifp->info; - struct bfd_info *bfd_info = NULL; - if (!pim_ifp) - return; - - bfd_info = pim_ifp->bfd_info; - if (!bfd_info) + if (!pim_ifp || !pim_ifp->bfd_config.enabled) return; #if HAVE_BFDD == 0 - if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_PARAM_CFG)) - vty_out(vty, " ip pim bfd %d %d %d\n", bfd_info->detect_mult, - bfd_info->required_min_rx, bfd_info->desired_min_tx); + if (pim_ifp->bfd_config.detection_multiplier != BFD_DEF_DETECT_MULT + || pim_ifp->bfd_config.min_rx != BFD_DEF_MIN_RX + || pim_ifp->bfd_config.min_tx != BFD_DEF_MIN_TX) + vty_out(vty, " ip pim bfd %d %d %d\n", + pim_ifp->bfd_config.detection_multiplier, + pim_ifp->bfd_config.min_rx, pim_ifp->bfd_config.min_tx); else #endif /* ! HAVE_BFDD */ vty_out(vty, " ip pim bfd\n"); } -/* - * pim_bfd_show_info - Show BFD info structure - */ -void pim_bfd_show_info(struct vty *vty, void *bfd_info, json_object *json_obj, - bool use_json, int param_only) +static void pim_neighbor_bfd_cb(struct bfd_session_params *bsp, + const struct bfd_session_status *bss, void *arg) { - if (param_only) - bfd_show_param(vty, (struct bfd_info *)bfd_info, 1, 0, use_json, - json_obj); - else - bfd_show_info(vty, (struct bfd_info *)bfd_info, 0, 1, use_json, - json_obj); + struct pim_neighbor *nbr = arg; + + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("%s: status %s old_status %s", __func__, + bfd_get_status_str(bss->state), + bfd_get_status_str(bss->previous_state)); + } + + if (bss->state == BFD_STATUS_DOWN + && bss->previous_state == BFD_STATUS_UP) + pim_neighbor_delete(nbr->interface, nbr, "BFD Session Expired"); } /* @@ -80,60 +80,19 @@ void pim_bfd_show_info(struct vty *vty, void *bfd_info, json_object *json_obj, void pim_bfd_info_nbr_create(struct pim_interface *pim_ifp, struct pim_neighbor *neigh) { - struct bfd_info *nbr_bfd_info = NULL; - /* Check if Pim Interface BFD is enabled */ - if (!pim_ifp || !pim_ifp->bfd_info) + if (!pim_ifp || !pim_ifp->bfd_config.enabled) return; - if (!neigh->bfd_info) - neigh->bfd_info = bfd_info_create(); + if (neigh->bfd_session == NULL) + neigh->bfd_session = bfd_sess_new(pim_neighbor_bfd_cb, neigh); - if (!neigh->bfd_info) - return; - - nbr_bfd_info = neigh->bfd_info; - nbr_bfd_info->detect_mult = pim_ifp->bfd_info->detect_mult; - nbr_bfd_info->desired_min_tx = pim_ifp->bfd_info->desired_min_tx; - nbr_bfd_info->required_min_rx = pim_ifp->bfd_info->required_min_rx; -} - -/* - * pim_bfd_info_free - Free BFD info structure - */ -void pim_bfd_info_free(struct bfd_info **bfd_info) -{ - bfd_info_free(bfd_info); -} - -static void pim_bfd_reg_dereg_nbr(struct pim_neighbor *nbr, int command) -{ - struct pim_interface *pim_ifp = NULL; - struct bfd_info *bfd_info = NULL; - struct zclient *zclient = NULL; - int cbit; - - zclient = pim_zebra_zclient_get(); - - if (!nbr) - return; - pim_ifp = nbr->interface->info; - bfd_info = pim_ifp->bfd_info; - if (!bfd_info) - return; - if (PIM_DEBUG_PIM_TRACE) { - char str[INET_ADDRSTRLEN]; - pim_inet4_dump("", nbr->source_addr, str, - sizeof(str)); - zlog_debug("%s Nbr %s %s with BFD", __func__, str, - bfd_get_command_dbg_str(command)); - } - - cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); - - bfd_peer_sendmsg(zclient, bfd_info, AF_INET, &nbr->source_addr, NULL, - nbr->interface->name, 0, 0, cbit, - command, 0, VRF_DEFAULT); + bfd_sess_set_timers( + neigh->bfd_session, pim_ifp->bfd_config.detection_multiplier, + pim_ifp->bfd_config.min_rx, pim_ifp->bfd_config.min_tx); + bfd_sess_set_ipv4_addrs(neigh->bfd_session, NULL, &neigh->source_addr); + bfd_sess_set_interface(neigh->bfd_session, neigh->interface->name); + bfd_sess_install(neigh->bfd_session); } /* @@ -142,7 +101,7 @@ static void pim_bfd_reg_dereg_nbr(struct pim_neighbor *nbr, int command) * zebra for starting/stopping the monitoring of * the neighbor rechahability. */ -int pim_bfd_reg_dereg_all_nbr(struct interface *ifp, int command) +void pim_bfd_reg_dereg_all_nbr(struct interface *ifp) { struct pim_interface *pim_ifp = NULL; struct listnode *node = NULL; @@ -150,197 +109,17 @@ int pim_bfd_reg_dereg_all_nbr(struct interface *ifp, int command) pim_ifp = ifp->info; if (!pim_ifp) - return -1; - if (!pim_ifp->bfd_info) - return -1; + return; for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { - if (command != ZEBRA_BFD_DEST_DEREGISTER) + if (pim_ifp->bfd_config.enabled) pim_bfd_info_nbr_create(pim_ifp, neigh); else - pim_bfd_info_free((struct bfd_info **)&neigh->bfd_info); - - pim_bfd_reg_dereg_nbr(neigh, command); + bfd_sess_free(&neigh->bfd_session); } - - return 0; -} - -/* - * pim_bfd_trigger_event - Neighbor is registered/deregistered with BFD when - * neighbor state is changed to/from 2way. - */ -void pim_bfd_trigger_event(struct pim_interface *pim_ifp, - struct pim_neighbor *nbr, uint8_t nbr_up) -{ - if (nbr_up) { - pim_bfd_info_nbr_create(pim_ifp, nbr); - pim_bfd_reg_dereg_nbr(nbr, ZEBRA_BFD_DEST_REGISTER); - } else { - pim_bfd_info_free(&nbr->bfd_info); - pim_bfd_reg_dereg_nbr(nbr, ZEBRA_BFD_DEST_DEREGISTER); - } -} - -/* - * pim_bfd_if_param_set - Set the configured BFD paramter values for - * interface. - */ -void pim_bfd_if_param_set(struct interface *ifp, uint32_t min_rx, - uint32_t min_tx, uint8_t detect_mult, int defaults) -{ - struct pim_interface *pim_ifp = ifp->info; - int command = 0; - - if (!pim_ifp) - return; - bfd_set_param(&(pim_ifp->bfd_info), min_rx, min_tx, detect_mult, NULL, - defaults, &command); - - if (pim_ifp->bfd_info) { - if (PIM_DEBUG_PIM_TRACE) - zlog_debug("%s: interface %s has bfd_info", __func__, - ifp->name); - } - if (command) - pim_bfd_reg_dereg_all_nbr(ifp, command); -} - - -/* - * pim_bfd_interface_dest_update - Find the neighbor for which the BFD status - * has changed and bring down the neighbor - * connectivity if the BFD status changed to - * down. - */ -static int pim_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) -{ - struct interface *ifp = NULL; - struct pim_interface *pim_ifp = NULL; - struct prefix p, src_p; - int status; - char msg[100]; - int old_status; - struct bfd_info *bfd_info = NULL; - struct timeval tv; - struct listnode *neigh_node = NULL; - struct listnode *neigh_nextnode = NULL; - struct pim_neighbor *neigh = NULL; - - ifp = bfd_get_peer_info(zclient->ibuf, &p, &src_p, &status, NULL, - vrf_id); - - if ((ifp == NULL) || (p.family != AF_INET)) - return 0; - - pim_ifp = ifp->info; - if (!pim_ifp) - return 0; - - if (!pim_ifp->bfd_info) { - if (PIM_DEBUG_PIM_TRACE) - zlog_debug("%s: pim interface %s BFD is disabled ", - __func__, ifp->name); - return 0; - } - - if (PIM_DEBUG_PIM_TRACE) - zlog_debug("%s: interface %s bfd destination %pFX %s", __func__, - ifp->name, &p, bfd_get_status_str(status)); - - for (ALL_LIST_ELEMENTS(pim_ifp->pim_neighbor_list, neigh_node, - neigh_nextnode, neigh)) { - /* Check neigh address matches with BFD address */ - if (neigh->source_addr.s_addr != p.u.prefix4.s_addr) - continue; - - bfd_info = (struct bfd_info *)neigh->bfd_info; - if (bfd_info->status == status) { - if (PIM_DEBUG_PIM_TRACE) { - char str[INET_ADDRSTRLEN]; - pim_inet4_dump("", neigh->source_addr, - str, sizeof(str)); - zlog_debug("%s: bfd status is same for nbr %s", - __func__, str); - } - continue; - } - old_status = bfd_info->status; - BFD_SET_CLIENT_STATUS(bfd_info->status, status); - monotime(&tv); - bfd_info->last_update = tv.tv_sec; - - if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: status %s old_status %s", __func__, - bfd_get_status_str(status), - bfd_get_status_str(old_status)); - } - if ((status == BFD_STATUS_DOWN) - && (old_status == BFD_STATUS_UP)) { - snprintf(msg, sizeof(msg), "BFD Session Expired"); - pim_neighbor_delete(ifp, neigh, msg); - } - } - return 0; -} - -/* - * pim_bfd_nbr_replay - Replay all the neighbors that have BFD enabled - * to zebra - */ -static int pim_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) -{ - struct interface *ifp = NULL; - struct pim_interface *pim_ifp = NULL; - struct pim_neighbor *neigh = NULL; - struct listnode *neigh_node; - struct listnode *neigh_nextnode; - struct vrf *vrf = NULL; - - /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); - - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - FOR_ALL_INTERFACES (vrf, ifp) { - pim_ifp = ifp->info; - - if (!pim_ifp) - continue; - - if (pim_ifp->pim_sock_fd < 0) - continue; - - for (ALL_LIST_ELEMENTS(pim_ifp->pim_neighbor_list, - neigh_node, neigh_nextnode, - neigh)) { - if (!neigh->bfd_info) - continue; - if (PIM_DEBUG_PIM_TRACE) { - char str[INET_ADDRSTRLEN]; - - pim_inet4_dump("", - neigh->source_addr, str, - sizeof(str)); - zlog_debug( - "%s: Replaying Pim Neigh %s to BFD vrf_id %u", - __func__, str, vrf->vrf_id); - } - pim_bfd_reg_dereg_nbr(neigh, - ZEBRA_BFD_DEST_UPDATE); - } - } - } - return 0; } void pim_bfd_init(void) { - struct zclient *zclient = NULL; - - zclient = pim_zebra_zclient_get(); - - bfd_gbl_init(); - - zclient->interface_bfd_dest_update = pim_bfd_interface_dest_update; - zclient->bfd_dest_replay = pim_bfd_nbr_replay; + bfd_protocol_integration_init(pim_zebra_zclient_get(), router->master); } diff --git a/pimd/pim_bfd.h b/pimd/pim_bfd.h index 962b727f88..3bfbb7486e 100644 --- a/pimd/pim_bfd.h +++ b/pimd/pim_bfd.h @@ -25,16 +25,35 @@ #include "if.h" +/** + * Initializes PIM BFD integration code. + */ void pim_bfd_init(void); + +/** + * Write configuration to `show running-config`. + * + * \param vty the vty output pointer. + * \param ifp the interface pointer that has the configuration. + */ void pim_bfd_write_config(struct vty *vty, struct interface *ifp); -void pim_bfd_show_info(struct vty *vty, void *bfd_info, json_object *json_obj, - bool use_json, int param_only); -void pim_bfd_if_param_set(struct interface *ifp, uint32_t min_rx, - uint32_t min_tx, uint8_t detect_mult, int defaults); -int pim_bfd_reg_dereg_all_nbr(struct interface *ifp, int command); -void pim_bfd_trigger_event(struct pim_interface *pim_ifp, - struct pim_neighbor *nbr, uint8_t nbr_up); + +/** + * Enables or disables all peers BFD sessions. + * + * \param ifp interface pointer. + * \param enable session state to set. + */ +void pim_bfd_reg_dereg_all_nbr(struct interface *ifp); + +/** + * Create and configure peer BFD session if it does not exist. It will use + * the interface configured parameters as the peer configuration. + * + * \param pim_ifp the interface configuration pointer. + * \param neigh the neighbor configuration pointer. + */ void pim_bfd_info_nbr_create(struct pim_interface *pim_ifp, struct pim_neighbor *neigh); -void pim_bfd_info_free(struct bfd_info **bfd_info); + #endif /* _PIM_BFD_H */ diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 4bbe7d35f0..b25b755e1e 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1955,8 +1955,8 @@ static void pim_show_neighbors_single(struct pim_instance *pim, struct vty *vty, vty_out(vty, " Hello Option - T-bit : %s\n", option_t_bit ? "yes" : "no"); - pim_bfd_show_info(vty, neigh->bfd_info, - json_ifp, uj, 0); + bfd_sess_show(vty, json_ifp, + neigh->bfd_session); vty_out(vty, "\n"); } } @@ -9683,12 +9683,7 @@ DEFUN (ip_pim_bfd, PIM_STR "Enables BFD support\n") { - struct bfd_info *bfd_info = NULL; - char default_rx_interval[5]; - char default_tx_interval[5]; - char default_detect_mult[3]; const struct lyd_node *igmp_enable_dnode; - char bfd_xpath[XPATH_MAXLEN + 20]; igmp_enable_dnode = yang_dnode_get(vty->candidate_config->dnode, "%s/frr-igmp:igmp/igmp-enable", @@ -9702,31 +9697,9 @@ DEFUN (ip_pim_bfd, "true"); } - snprintf(default_rx_interval, sizeof(default_rx_interval), "%d", - BFD_DEF_MIN_RX); - snprintf(default_tx_interval, sizeof(default_tx_interval), "%d", - BFD_DEF_MIN_TX); - snprintf(default_detect_mult, sizeof(default_detect_mult), "%d", - BFD_DEF_DETECT_MULT); + nb_cli_enqueue_change(vty, "./bfd", NB_OP_CREATE, NULL); - snprintf(bfd_xpath, sizeof(bfd_xpath), "%s/frr-pim:pim/bfd", - VTY_CURR_XPATH); - bfd_info = nb_running_get_entry(NULL, bfd_xpath, false); - - if (!bfd_info || - !CHECK_FLAG(bfd_info->flags, BFD_FLAG_PARAM_CFG)) { - nb_cli_enqueue_change(vty, "./bfd/min-rx-interval", - NB_OP_MODIFY, default_rx_interval); - nb_cli_enqueue_change(vty, "./bfd/min-tx-interval", - NB_OP_MODIFY, default_tx_interval); - nb_cli_enqueue_change(vty, "./bfd/detect_mult", - NB_OP_MODIFY, - default_detect_mult); - - return nb_cli_apply_changes(vty, "./frr-pim:pim"); - } - - return NB_OK; + return nb_cli_apply_changes(vty, "./frr-pim:pim"); } DEFUN (no_ip_pim_bfd, @@ -9847,19 +9820,8 @@ DEFUN_HIDDEN( int idx_number = 3; int idx_number_2 = 4; int idx_number_3 = 5; - uint32_t rx_val; - uint32_t tx_val; - uint8_t dm_val; - int ret; const struct lyd_node *igmp_enable_dnode; - if ((ret = bfd_validate_param(vty, argv[idx_number]->arg, - argv[idx_number_2]->arg, - argv[idx_number_3]->arg, &dm_val, &rx_val, - &tx_val)) - != CMD_SUCCESS) - return ret; - igmp_enable_dnode = yang_dnode_get(vty->candidate_config->dnode, "%s/frr-igmp:igmp/igmp-enable", VTY_CURR_XPATH); @@ -9872,6 +9834,7 @@ DEFUN_HIDDEN( "true"); } + nb_cli_enqueue_change(vty, "./bfd", NB_OP_CREATE, NULL); nb_cli_enqueue_change(vty, "./bfd/min-rx-interval", NB_OP_MODIFY, argv[idx_number_2]->arg); nb_cli_enqueue_change(vty, "./bfd/min-tx-interval", NB_OP_MODIFY, diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 664ab31949..b1365cab7f 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -158,9 +158,15 @@ struct pim_interface { uint32_t pim_ifstat_bsm_cfg_miss; uint32_t pim_ifstat_ucast_bsm_cfg_miss; uint32_t pim_ifstat_bsm_invalid_sz; - struct bfd_info *bfd_info; bool bsm_enable; /* bsm processing enable */ bool ucast_bsm_accept; /* ucast bsm processing */ + + struct { + bool enabled; + uint32_t min_rx; + uint32_t min_tx; + uint8_t detection_multiplier; + } bfd_config; }; /* diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index f64adc6254..ce270ea6bc 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -1882,11 +1882,19 @@ int lib_interface_pim_hello_holdtime_destroy(struct nb_cb_destroy_args *args) */ int lib_interface_pim_bfd_create(struct nb_cb_create_args *args) { + struct interface *ifp; + struct pim_interface *pim_ifp; + switch (args->event) { case NB_EV_VALIDATE: case NB_EV_PREPARE: case NB_EV_ABORT: + /* NOTHING */ + break; case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + pim_ifp = ifp->info; + pim_ifp->bfd_config.enabled = true; break; } @@ -1912,13 +1920,10 @@ int lib_interface_pim_bfd_destroy(struct nb_cb_destroy_args *args) case NB_EV_PREPARE: break; case NB_EV_APPLY: - ifp = nb_running_get_entry(args->dnode->parent, NULL, true); + ifp = nb_running_get_entry(args->dnode, NULL, true); pim_ifp = ifp->info; - if (pim_ifp->bfd_info) { - pim_bfd_reg_dereg_all_nbr(ifp, - ZEBRA_BFD_DEST_DEREGISTER); - bfd_info_free(&(pim_ifp->bfd_info)); - } + pim_ifp->bfd_config.enabled = false; + pim_bfd_reg_dereg_all_nbr(ifp); break; } @@ -1932,11 +1937,8 @@ void lib_interface_pim_bfd_apply_finish(struct nb_cb_apply_finish_args *args) { struct interface *ifp; struct pim_interface *pim_ifp; - uint32_t min_rx; - uint32_t min_tx; - uint8_t detect_mult; - ifp = nb_running_get_entry(args->dnode->parent, NULL, true); + ifp = nb_running_get_entry(args->dnode, NULL, true); pim_ifp = ifp->info; if (!pim_ifp) { @@ -1944,17 +1946,14 @@ void lib_interface_pim_bfd_apply_finish(struct nb_cb_apply_finish_args *args) return; } - min_rx = yang_dnode_get_uint16(args->dnode, "./min-rx-interval"); - min_tx = yang_dnode_get_uint16(args->dnode, "./min-tx-interval"); - detect_mult = yang_dnode_get_uint8(args->dnode, "./detect_mult"); + pim_ifp->bfd_config.detection_multiplier = + yang_dnode_get_uint8(args->dnode, "./detect_mult"); + pim_ifp->bfd_config.min_rx = + yang_dnode_get_uint16(args->dnode, "./min-rx-interval"); + pim_ifp->bfd_config.min_tx = + yang_dnode_get_uint16(args->dnode, "./min-tx-interval"); - if ((min_rx == BFD_DEF_MIN_RX) && (min_tx == BFD_DEF_MIN_TX) - && (detect_mult == BFD_DEF_DETECT_MULT)) - pim_bfd_if_param_set(ifp, min_rx, min_tx, detect_mult, 1); - else - pim_bfd_if_param_set(ifp, min_rx, min_tx, detect_mult, 0); - - nb_running_set_entry(args->dnode, pim_ifp->bfd_info); + pim_bfd_reg_dereg_all_nbr(ifp); } /* diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c index 167aa3c604..19dc469091 100644 --- a/pimd/pim_neighbor.c +++ b/pimd/pim_neighbor.c @@ -377,7 +377,7 @@ pim_neighbor_new(struct interface *ifp, struct in_addr source_addr, } // Register PIM Neighbor with BFD - pim_bfd_trigger_event(pim_ifp, neigh, 1); + pim_bfd_info_nbr_create(pim_ifp, neigh); return neigh; } @@ -419,8 +419,7 @@ void pim_neighbor_free(struct pim_neighbor *neigh) list_delete(&neigh->upstream_jp_agg); THREAD_OFF(neigh->jp_timer); - if (neigh->bfd_info) - pim_bfd_info_free(&neigh->bfd_info); + bfd_sess_free(&neigh->bfd_session); XFREE(MTYPE_PIM_NEIGHBOR, neigh); } @@ -669,9 +668,6 @@ void pim_neighbor_delete(struct interface *ifp, struct pim_neighbor *neigh, __func__, src_str, ifp->name); } - // De-Register PIM Neighbor with BFD - pim_bfd_trigger_event(pim_ifp, neigh, 0); - listnode_delete(pim_ifp->pim_neighbor_list, neigh); pim_neighbor_free(neigh); diff --git a/pimd/pim_neighbor.h b/pimd/pim_neighbor.h index a4f2e10c88..b461098a60 100644 --- a/pimd/pim_neighbor.h +++ b/pimd/pim_neighbor.h @@ -43,7 +43,7 @@ struct pim_neighbor { struct thread *jp_timer; struct list *upstream_jp_agg; - struct bfd_info *bfd_info; + struct bfd_session_params *bfd_session; }; void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime); From 0d00ac9cb5c24611fb3617a6aa30b04e47937070 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Mon, 12 Apr 2021 16:47:38 -0300 Subject: [PATCH 3/7] topotests: add new basic PIM topology test Test for PIM (re)convergence and BFD usage. Signed-off-by: Rafael Zalamena --- tests/topotests/pim-basic-topo2/__init__.py | 0 tests/topotests/pim-basic-topo2/r1/bfdd.conf | 2 + tests/topotests/pim-basic-topo2/r1/pimd.conf | 4 + tests/topotests/pim-basic-topo2/r1/zebra.conf | 3 + tests/topotests/pim-basic-topo2/r2/bfdd.conf | 2 + tests/topotests/pim-basic-topo2/r2/pimd.conf | 12 + tests/topotests/pim-basic-topo2/r2/zebra.conf | 9 + tests/topotests/pim-basic-topo2/r3/bfdd.conf | 2 + tests/topotests/pim-basic-topo2/r3/pimd.conf | 4 + tests/topotests/pim-basic-topo2/r3/zebra.conf | 3 + tests/topotests/pim-basic-topo2/r4/bfdd.conf | 2 + tests/topotests/pim-basic-topo2/r4/pimd.conf | 4 + tests/topotests/pim-basic-topo2/r4/zebra.conf | 3 + .../pim-basic-topo2/test_pim_basic_topo2.dot | 73 +++++++ .../pim-basic-topo2/test_pim_basic_topo2.png | Bin 0 -> 33496 bytes .../pim-basic-topo2/test_pim_basic_topo2.py | 206 ++++++++++++++++++ 16 files changed, 329 insertions(+) create mode 100644 tests/topotests/pim-basic-topo2/__init__.py create mode 100644 tests/topotests/pim-basic-topo2/r1/bfdd.conf create mode 100644 tests/topotests/pim-basic-topo2/r1/pimd.conf create mode 100644 tests/topotests/pim-basic-topo2/r1/zebra.conf create mode 100644 tests/topotests/pim-basic-topo2/r2/bfdd.conf create mode 100644 tests/topotests/pim-basic-topo2/r2/pimd.conf create mode 100644 tests/topotests/pim-basic-topo2/r2/zebra.conf create mode 100644 tests/topotests/pim-basic-topo2/r3/bfdd.conf create mode 100644 tests/topotests/pim-basic-topo2/r3/pimd.conf create mode 100644 tests/topotests/pim-basic-topo2/r3/zebra.conf create mode 100644 tests/topotests/pim-basic-topo2/r4/bfdd.conf create mode 100644 tests/topotests/pim-basic-topo2/r4/pimd.conf create mode 100644 tests/topotests/pim-basic-topo2/r4/zebra.conf create mode 100644 tests/topotests/pim-basic-topo2/test_pim_basic_topo2.dot create mode 100644 tests/topotests/pim-basic-topo2/test_pim_basic_topo2.png create mode 100644 tests/topotests/pim-basic-topo2/test_pim_basic_topo2.py diff --git a/tests/topotests/pim-basic-topo2/__init__.py b/tests/topotests/pim-basic-topo2/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/pim-basic-topo2/r1/bfdd.conf b/tests/topotests/pim-basic-topo2/r1/bfdd.conf new file mode 100644 index 0000000000..ca61e467dc --- /dev/null +++ b/tests/topotests/pim-basic-topo2/r1/bfdd.conf @@ -0,0 +1,2 @@ +bfd +! diff --git a/tests/topotests/pim-basic-topo2/r1/pimd.conf b/tests/topotests/pim-basic-topo2/r1/pimd.conf new file mode 100644 index 0000000000..ff28648dfa --- /dev/null +++ b/tests/topotests/pim-basic-topo2/r1/pimd.conf @@ -0,0 +1,4 @@ +interface r1-eth0 + ip pim + ip pim bfd +! diff --git a/tests/topotests/pim-basic-topo2/r1/zebra.conf b/tests/topotests/pim-basic-topo2/r1/zebra.conf new file mode 100644 index 0000000000..6bf02a3ee8 --- /dev/null +++ b/tests/topotests/pim-basic-topo2/r1/zebra.conf @@ -0,0 +1,3 @@ +interface r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/pim-basic-topo2/r2/bfdd.conf b/tests/topotests/pim-basic-topo2/r2/bfdd.conf new file mode 100644 index 0000000000..ca61e467dc --- /dev/null +++ b/tests/topotests/pim-basic-topo2/r2/bfdd.conf @@ -0,0 +1,2 @@ +bfd +! diff --git a/tests/topotests/pim-basic-topo2/r2/pimd.conf b/tests/topotests/pim-basic-topo2/r2/pimd.conf new file mode 100644 index 0000000000..0b32ded19a --- /dev/null +++ b/tests/topotests/pim-basic-topo2/r2/pimd.conf @@ -0,0 +1,12 @@ +interface r2-eth0 + ip pim + ip pim bfd +! +interface r2-eth1 + ip pim + ip pim bfd +! +interface r2-eth2 + ip pim + ip pim bfd +! diff --git a/tests/topotests/pim-basic-topo2/r2/zebra.conf b/tests/topotests/pim-basic-topo2/r2/zebra.conf new file mode 100644 index 0000000000..3ceb5f0fc2 --- /dev/null +++ b/tests/topotests/pim-basic-topo2/r2/zebra.conf @@ -0,0 +1,9 @@ +interface r2-eth0 + ip address 192.168.1.2/24 +! +interface r2-eth1 + ip address 192.168.2.1/24 +! +interface r2-eth2 + ip address 192.168.3.1/24 +! diff --git a/tests/topotests/pim-basic-topo2/r3/bfdd.conf b/tests/topotests/pim-basic-topo2/r3/bfdd.conf new file mode 100644 index 0000000000..ca61e467dc --- /dev/null +++ b/tests/topotests/pim-basic-topo2/r3/bfdd.conf @@ -0,0 +1,2 @@ +bfd +! diff --git a/tests/topotests/pim-basic-topo2/r3/pimd.conf b/tests/topotests/pim-basic-topo2/r3/pimd.conf new file mode 100644 index 0000000000..691a28ea27 --- /dev/null +++ b/tests/topotests/pim-basic-topo2/r3/pimd.conf @@ -0,0 +1,4 @@ +interface r3-eth0 + ip pim + ip pim bfd +! diff --git a/tests/topotests/pim-basic-topo2/r3/zebra.conf b/tests/topotests/pim-basic-topo2/r3/zebra.conf new file mode 100644 index 0000000000..3df218ee16 --- /dev/null +++ b/tests/topotests/pim-basic-topo2/r3/zebra.conf @@ -0,0 +1,3 @@ +interface r3-eth0 + ip address 192.168.2.3/24 +! diff --git a/tests/topotests/pim-basic-topo2/r4/bfdd.conf b/tests/topotests/pim-basic-topo2/r4/bfdd.conf new file mode 100644 index 0000000000..ca61e467dc --- /dev/null +++ b/tests/topotests/pim-basic-topo2/r4/bfdd.conf @@ -0,0 +1,2 @@ +bfd +! diff --git a/tests/topotests/pim-basic-topo2/r4/pimd.conf b/tests/topotests/pim-basic-topo2/r4/pimd.conf new file mode 100644 index 0000000000..2277b3e1f1 --- /dev/null +++ b/tests/topotests/pim-basic-topo2/r4/pimd.conf @@ -0,0 +1,4 @@ +interface r4-eth0 + ip pim + ip pim bfd +! diff --git a/tests/topotests/pim-basic-topo2/r4/zebra.conf b/tests/topotests/pim-basic-topo2/r4/zebra.conf new file mode 100644 index 0000000000..6ac5c78fc3 --- /dev/null +++ b/tests/topotests/pim-basic-topo2/r4/zebra.conf @@ -0,0 +1,3 @@ +interface r4-eth0 + ip address 192.168.3.4/24 +! diff --git a/tests/topotests/pim-basic-topo2/test_pim_basic_topo2.dot b/tests/topotests/pim-basic-topo2/test_pim_basic_topo2.dot new file mode 100644 index 0000000000..22fce27e22 --- /dev/null +++ b/tests/topotests/pim-basic-topo2/test_pim_basic_topo2.dot @@ -0,0 +1,73 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo3"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n192.168.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n192.168.2.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n192.168.3.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0\n.1"]; + r2 -- sw1 [label="eth0\n.2"]; + + r2 -- sw2 [label="eth1\n.1"]; + r3 -- sw2 [label="eth0\n.3"]; + + r2 -- sw3 [label="eth1\n.1"]; + r4 -- sw3 [label="eth2\n.4"]; +} diff --git a/tests/topotests/pim-basic-topo2/test_pim_basic_topo2.png b/tests/topotests/pim-basic-topo2/test_pim_basic_topo2.png new file mode 100644 index 0000000000000000000000000000000000000000..39139a35b10ec69010aa23852b45c4a75bc9e837 GIT binary patch literal 33496 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 NE?>KpA$7;~e*tDP%S-?O literal 0 HcmV?d00001 diff --git a/tests/topotests/pim-basic-topo2/test_pim_basic_topo2.py b/tests/topotests/pim-basic-topo2/test_pim_basic_topo2.py new file mode 100644 index 0000000000..a03747fa09 --- /dev/null +++ b/tests/topotests/pim-basic-topo2/test_pim_basic_topo2.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python + +# +# test_pim_basic_topo2.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_pim_basic_topo2.py: Test the FRR PIM protocol convergence. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd, pytest.mark.isisd, pytest.mark.ospfd] + + +class PimBasicTopo2(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 4 routers + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(PimBasicTopo2, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + daemon_file = "{}/{}/bfdd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BFD, daemon_file) + + daemon_file = "{}/{}/pimd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_PIM, daemon_file) + + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_ZEBRA, daemon_file) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def expect_neighbor(router, interface, peer): + "Wait until peer is present on interface." + logger.info("waiting peer {} in {}".format(peer, interface)) + tgen = get_topogen() + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ip pim neighbor json", + {interface: {peer: {}}} + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" PIM convergence failure'.format(router) + assert result is None, assertmsg + + +def test_wait_pim_convergence(): + "Wait for PIM to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for PIM to converge") + + expect_neighbor('r1', 'r1-eth0', '192.168.1.2') + expect_neighbor('r2', 'r2-eth0', '192.168.1.1') + + expect_neighbor('r2', 'r2-eth1', '192.168.2.3') + expect_neighbor('r2', 'r2-eth2', '192.168.3.4') + + expect_neighbor('r3', 'r3-eth0', '192.168.2.1') + expect_neighbor('r4', 'r4-eth0', '192.168.3.1') + + +def test_bfd_peers(): + "Wait for BFD peers to show up." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for BFD to converge") + + def expect_bfd_peer(router, peer): + "Wait until peer is present on interface." + logger.info("waiting BFD peer {} in {}".format(peer, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show bfd peers json", + [{"peer": peer, "status": "up"}] + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertmsg = '"{}" BFD convergence failure'.format(router) + assert result is None, assertmsg + + expect_bfd_peer("r1", "192.168.1.2") + expect_bfd_peer("r2", "192.168.1.1") + expect_bfd_peer("r2", "192.168.2.3") + expect_bfd_peer("r2", "192.168.3.4") + expect_bfd_peer("r3", "192.168.2.1") + expect_bfd_peer("r4", "192.168.3.1") + + +def test_pim_reconvergence(): + "Disconnect a peer and expect it to disconnect." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for disconnect convergence") + tgen.gears["r4"].link_enable("r4-eth0", enabled=False) + + def expect_neighbor_down(router, interface, peer): + "Wait until peer is present on interface." + logger.info("waiting peer {} in {} to disappear".format(peer, interface)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ip pim neighbor json", + {interface: {peer: None}} + ) + _, result = topotest.run_and_expect(test_func, None, count=4, wait=1) + assertmsg = '"{}" PIM convergence failure'.format(router) + assert result is None, assertmsg + + expect_neighbor_down("r2", "r2-eth2", "192.168.3.4") + + logger.info("waiting for reconvergence") + tgen.gears["r4"].link_enable("r4-eth0", enabled=True) + expect_neighbor("r2", "r2-eth2", "192.168.3.4") + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) From 3544d257b7b4c8be630d01cb0925cc5790b8283d Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Tue, 13 Apr 2021 10:43:19 -0300 Subject: [PATCH 4/7] yang: add PIM BFD profile support Allow users to configure BFD profile for the peers in a interface. Signed-off-by: Rafael Zalamena --- yang/frr-pim.yang | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang index 2070649ec2..52d8641613 100644 --- a/yang/frr-pim.yang +++ b/yang/frr-pim.yang @@ -331,6 +331,12 @@ module frr-pim { description "Detect Multiplier"; } + + leaf profile { + type string; + description + "Use a preconfigure BFD profile."; + } } leaf bsm { From 745b8d4a68c112cbcf371d73f81b7ce7c3921b4d Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Tue, 13 Apr 2021 11:07:01 -0300 Subject: [PATCH 5/7] pimd: support BFD profiles configuration Allow users to pre configure BFD sessions with a profile. Signed-off-by: Rafael Zalamena --- pimd/pim_bfd.c | 5 +++++ pimd/pim_cmd.c | 25 ++++++++++++++++++++--- pimd/pim_iface.h | 1 + pimd/pim_nb.c | 7 +++++++ pimd/pim_nb.h | 2 ++ pimd/pim_nb_config.c | 47 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 84 insertions(+), 3 deletions(-) diff --git a/pimd/pim_bfd.c b/pimd/pim_bfd.c index 2990fcb023..bc518391a5 100644 --- a/pimd/pim_bfd.c +++ b/pimd/pim_bfd.c @@ -56,6 +56,10 @@ void pim_bfd_write_config(struct vty *vty, struct interface *ifp) else #endif /* ! HAVE_BFDD */ vty_out(vty, " ip pim bfd\n"); + + if (pim_ifp->bfd_config.profile) + vty_out(vty, " ip pim bfd profile %s\n", + pim_ifp->bfd_config.profile); } static void pim_neighbor_bfd_cb(struct bfd_session_params *bsp, @@ -92,6 +96,7 @@ void pim_bfd_info_nbr_create(struct pim_interface *pim_ifp, pim_ifp->bfd_config.min_rx, pim_ifp->bfd_config.min_tx); bfd_sess_set_ipv4_addrs(neigh->bfd_session, NULL, &neigh->source_addr); bfd_sess_set_interface(neigh->bfd_session, neigh->interface->name); + bfd_sess_set_profile(neigh->bfd_session, pim_ifp->bfd_config.profile); bfd_sess_install(neigh->bfd_session); } diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index b25b755e1e..c01cfec88e 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -9676,12 +9676,14 @@ DEFUN (interface_no_pim_use_source, "frr-routing:ipv4"); } -DEFUN (ip_pim_bfd, +DEFPY (ip_pim_bfd, ip_pim_bfd_cmd, - "ip pim bfd", + "ip pim bfd [profile BFDPROF$prof]", IP_STR PIM_STR - "Enables BFD support\n") + "Enables BFD support\n" + "Use BFD profile\n" + "Use BFD profile name\n") { const struct lyd_node *igmp_enable_dnode; @@ -9698,6 +9700,22 @@ DEFUN (ip_pim_bfd, } nb_cli_enqueue_change(vty, "./bfd", NB_OP_CREATE, NULL); + if (prof) + nb_cli_enqueue_change(vty, "./bfd/profile", NB_OP_MODIFY, prof); + + return nb_cli_apply_changes(vty, "./frr-pim:pim"); +} + +DEFPY(no_ip_pim_bfd_profile, no_ip_pim_bfd_profile_cmd, + "no ip pim bfd profile [BFDPROF]", + NO_STR + IP_STR + PIM_STR + "Enables BFD support\n" + "Disable BFD profile\n" + "BFD Profile name\n") +{ + nb_cli_enqueue_change(vty, "./bfd/profile", NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, "./frr-pim:pim"); } @@ -11694,6 +11712,7 @@ void pim_cmd_init(void) /* Install BFD command */ install_element(INTERFACE_NODE, &ip_pim_bfd_cmd); install_element(INTERFACE_NODE, &ip_pim_bfd_param_cmd); + install_element(INTERFACE_NODE, &no_ip_pim_bfd_profile_cmd); install_element(INTERFACE_NODE, &no_ip_pim_bfd_cmd); #if HAVE_BFDD == 0 install_element(INTERFACE_NODE, &no_ip_pim_bfd_param_cmd); diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index b1365cab7f..92784103fe 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -166,6 +166,7 @@ struct pim_interface { uint32_t min_rx; uint32_t min_tx; uint8_t detection_multiplier; + char *profile; } bfd_config; }; diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c index 8ca0e0780b..37c539883d 100644 --- a/pimd/pim_nb.c +++ b/pimd/pim_nb.c @@ -258,6 +258,13 @@ const struct frr_yang_module_info frr_pim_info = { .modify = lib_interface_pim_bfd_detect_mult_modify, } }, + { + .xpath = "/frr-interface:lib/interface/frr-pim:pim/bfd/profile", + .cbs = { + .modify = lib_interface_pim_bfd_profile_modify, + .destroy = lib_interface_pim_bfd_profile_destroy, + } + }, { .xpath = "/frr-interface:lib/interface/frr-pim:pim/bsm", .cbs = { diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h index 78eb680103..440384e45c 100644 --- a/pimd/pim_nb.h +++ b/pimd/pim_nb.h @@ -121,6 +121,8 @@ int lib_interface_pim_bfd_min_rx_interval_modify(struct nb_cb_modify_args *args) int lib_interface_pim_bfd_min_tx_interval_modify( struct nb_cb_modify_args *args); int lib_interface_pim_bfd_detect_mult_modify(struct nb_cb_modify_args *args); +int lib_interface_pim_bfd_profile_modify(struct nb_cb_modify_args *args); +int lib_interface_pim_bfd_profile_destroy(struct nb_cb_destroy_args *args); int lib_interface_pim_bsm_modify(struct nb_cb_modify_args *args); int lib_interface_pim_unicast_bsm_modify(struct nb_cb_modify_args *args); int lib_interface_pim_active_active_modify(struct nb_cb_modify_args *args); diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index ce270ea6bc..8e6f2ec42b 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -2004,6 +2004,53 @@ int lib_interface_pim_bfd_detect_mult_modify(struct nb_cb_modify_args *args) return NB_OK; } +/* + * XPath: /frr-interface:lib/interface/frr-pim:pim/bfd/profile + */ +int lib_interface_pim_bfd_profile_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + pim_ifp = ifp->info; + XFREE(MTYPE_TMP, pim_ifp->bfd_config.profile); + pim_ifp->bfd_config.profile = XSTRDUP( + MTYPE_TMP, yang_dnode_get_string(args->dnode, NULL)); + break; + } + + return NB_OK; +} + +int lib_interface_pim_bfd_profile_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + pim_ifp = ifp->info; + XFREE(MTYPE_TMP, pim_ifp->bfd_config.profile); + break; + } + + return NB_OK; +} + /* * XPath: /frr-interface:lib/interface/frr-pim:pim/bsm */ From 46e3d008038371556c97d14e36e16ca9bfd40f53 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Tue, 13 Apr 2021 11:53:50 -0300 Subject: [PATCH 6/7] doc: document new PIM BFD command variant Tell user he can configure BFD profile for sessions created by PIM. Signed-off-by: Rafael Zalamena --- doc/user/bfd.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst index 6f797f7cc1..f73c91e026 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -340,12 +340,15 @@ PIM BFD Configuration The following commands are available inside the interface configuration node. -.. clicmd:: ip pim bfd +.. clicmd:: ip pim bfd [profile BFDPROF] Listen for BFD events on peers created on the interface. Every time a new neighbor is found a BFD peer is created to monitor the link status for fast convergence. + Optionally uses the BFD profile ``BFDPROF`` in the created sessions under + that interface. + .. _bfd-configuration: From d5f9847a9e6fd055bbf390990e0c20131e3e9bc8 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Tue, 13 Apr 2021 11:33:39 -0300 Subject: [PATCH 7/7] topotests: test PIM BFD profile integration Test that PIM BFD profile integration works. Signed-off-by: Rafael Zalamena --- tests/topotests/pim-basic-topo2/r1/bfdd.conf | 4 +++ tests/topotests/pim-basic-topo2/r1/pimd.conf | 2 +- .../pim-basic-topo2/test_pim_basic_topo2.py | 34 ++++++++++++++++++- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/tests/topotests/pim-basic-topo2/r1/bfdd.conf b/tests/topotests/pim-basic-topo2/r1/bfdd.conf index ca61e467dc..76c6f82190 100644 --- a/tests/topotests/pim-basic-topo2/r1/bfdd.conf +++ b/tests/topotests/pim-basic-topo2/r1/bfdd.conf @@ -1,2 +1,6 @@ bfd + profile fast-tx + receive-interval 250 + transmit-interval 250 + ! ! diff --git a/tests/topotests/pim-basic-topo2/r1/pimd.conf b/tests/topotests/pim-basic-topo2/r1/pimd.conf index ff28648dfa..b895d7d573 100644 --- a/tests/topotests/pim-basic-topo2/r1/pimd.conf +++ b/tests/topotests/pim-basic-topo2/r1/pimd.conf @@ -1,4 +1,4 @@ interface r1-eth0 ip pim - ip pim bfd + ip pim bfd profile fast-tx ! diff --git a/tests/topotests/pim-basic-topo2/test_pim_basic_topo2.py b/tests/topotests/pim-basic-topo2/test_pim_basic_topo2.py index a03747fa09..883125cfc7 100644 --- a/tests/topotests/pim-basic-topo2/test_pim_basic_topo2.py +++ b/tests/topotests/pim-basic-topo2/test_pim_basic_topo2.py @@ -45,7 +45,7 @@ from lib.topolog import logger # Required to instantiate the topology builder class. from mininet.topo import Topo -pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd, pytest.mark.isisd, pytest.mark.ospfd] +pytestmark = [pytest.mark.bfdd, pytest.mark.pimd] class PimBasicTopo2(Topo): @@ -192,6 +192,38 @@ def test_pim_reconvergence(): expect_neighbor("r2", "r2-eth2", "192.168.3.4") +def test_pim_bfd_profile(): + "Test that the BFD profile is properly applied in BFD." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def expect_bfd_peer_settings(router, settings): + "Expect the following BFD configuration" + logger.info("Verifying BFD peer {} in {}".format(settings["peer"], router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show bfd peers json", + [settings] + ) + _, result = topotest.run_and_expect(test_func, None, count=4, wait=1) + assertmsg = '"{}" BFD convergence failure'.format(router) + assert result is None, assertmsg + + expect_bfd_peer_settings("r1", { + "peer": "192.168.1.2", + "receive-interval": 250, + "transmit-interval": 250, + }) + + expect_bfd_peer_settings("r2", { + "peer": "192.168.1.1", + "remote-receive-interval": 250, + "remote-transmit-interval": 250, + }) + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen()