diff --git a/bfdd/bfd.c b/bfdd/bfd.c index 828059eb4a..fc9d033437 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -1290,6 +1290,7 @@ static unsigned int bfd_key_hash_do(const void *p); static void _bfd_free(struct hash_bucket *hb, void *arg __attribute__((__unused__))); int _bfd_session_next(struct hash_bucket *hb, void *arg); +void _bfd_session_remove_manual(struct hash_bucket *hb, void *arg); /* BFD hash for our discriminator. */ static unsigned int bfd_id_hash_do(const void *p) @@ -1587,6 +1588,35 @@ const struct bfd_session *bfd_session_next(const struct bfd_session *bs, return bsi.bsi_bs; } +void _bfd_session_remove_manual(struct hash_bucket *hb, + void *arg __attribute__((__unused__))) +{ + struct bfd_session *bs = hb->data; + + /* Delete only manually configured sessions. */ + if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) == 0) + return; + + bs->refcount--; + BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + + /* Don't delete sessions still in use. */ + if (bs->refcount != 0) + return; + + bfd_session_free(bs); +} + +/* + * bfd_sessions_remove_manual: remove all manually configured sessions. + * + * NOTE: this function doesn't remove automatically created sessions. + */ +void bfd_sessions_remove_manual(void) +{ + hash_iterate(bfd_key_hash, _bfd_session_remove_manual, NULL); +} + /* * VRF related functions. */ diff --git a/bfdd/bfd.h b/bfdd/bfd.h index 78b21a9b6f..10aeb3e52c 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -550,6 +550,7 @@ struct bfd_session *bs_registrate(struct bfd_session *bs); void bfd_session_free(struct bfd_session *bs); const struct bfd_session *bfd_session_next(const struct bfd_session *bs, bool mhop); +void bfd_sessions_remove_manual(void); /* BFD hash data structures interface */ void bfd_initialize(void); diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c index dac1c2521d..64500cef7d 100644 --- a/bfdd/bfdd_cli.c +++ b/bfdd/bfdd_cli.c @@ -52,6 +52,16 @@ /* * Functions. */ +DEFUN( + bfd_config_reset, bfd_config_reset_cmd, + "no bfd", + NO_STR + "Configure BFD peers\n") +{ + nb_cli_enqueue_change(vty, "/frr-bfdd:bfdd/bfd", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + void bfd_cli_show_header(struct vty *vty, struct lyd_node *dnode __attribute__((__unused__)), bool show_defaults __attribute__((__unused__))) @@ -343,6 +353,8 @@ void bfd_cli_show_echo_interval(struct vty *vty, struct lyd_node *dnode, void bfdd_cli_init(void) { + install_element(CONFIG_NODE, &bfd_config_reset_cmd); + install_element(BFD_NODE, &bfd_peer_enter_cmd); install_element(BFD_NODE, &bfd_no_peer_cmd); diff --git a/bfdd/bfdd_northbound.c b/bfdd/bfdd_northbound.c index 4b21a21162..fd007b57b2 100644 --- a/bfdd/bfdd_northbound.c +++ b/bfdd/bfdd_northbound.c @@ -71,11 +71,27 @@ int bfd_session_create(enum nb_event event, const struct lyd_node *dnode, switch (event) { case NB_EV_VALIDATE: bfd_session_get_key(mhop, dnode, &bk); - if (bfd_key_lookup(bk)) + bs = bfd_key_lookup(bk); + + /* This session was already configured by CLI. */ + if (bs != NULL && BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG)) return NB_ERR_VALIDATION; break; case NB_EV_PREPARE: + bfd_session_get_key(mhop, dnode, &bk); + bs = bfd_key_lookup(bk); + + /* This session was already configured by another daemon. */ + if (bs != NULL) { + /* Now it is configured also by CLI. */ + BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + bs->refcount++; + + resource->ptr = bs; + break; + } + bs = bfd_session_new(); if (bs == NULL) return NB_ERR_RESOURCE; @@ -84,6 +100,7 @@ int bfd_session_create(enum nb_event event, const struct lyd_node *dnode, bfd_session_get_key(mhop, dnode, &bs->key); /* Set configuration flags. */ + bs->refcount = 1; BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); if (mhop) BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_MH); @@ -95,14 +112,18 @@ int bfd_session_create(enum nb_event event, const struct lyd_node *dnode, case NB_EV_APPLY: bs = resource->ptr; - if (bs_registrate(bs) == NULL) + + /* Only attempt to registrate if freshly allocated. */ + if (bs->discrs.my_discr == 0 && bs_registrate(bs) == NULL) return NB_ERR_RESOURCE; nb_running_set_entry(dnode, bs); break; case NB_EV_ABORT: - bfd_session_free(resource->ptr); + bs = resource->ptr; + if (bs->refcount <= 1) + bfd_session_free(resource->ptr); break; } @@ -112,6 +133,7 @@ int bfd_session_create(enum nb_event event, const struct lyd_node *dnode, int bfd_session_destroy(enum nb_event event, const struct lyd_node *dnode, bool mhop) { + struct bfd_session *bs; struct bfd_key bk; switch (event) { @@ -126,7 +148,18 @@ int bfd_session_destroy(enum nb_event event, const struct lyd_node *dnode, break; case NB_EV_APPLY: - bfd_session_free(nb_running_unset_entry(dnode)); + bs = nb_running_unset_entry(dnode); + /* CLI is not using this session anymore. */ + if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) == 0) + break; + + BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + bs->refcount--; + /* There are still daemons using it. */ + if (bs->refcount > 0) + break; + + bfd_session_free(bs); break; case NB_EV_ABORT: @@ -152,7 +185,24 @@ static int bfdd_bfd_create(enum nb_event event, static int bfdd_bfd_destroy(enum nb_event event, const struct lyd_node *dnode) { - /* NOTHING */ + switch (event) { + case NB_EV_VALIDATE: + /* NOTHING */ + return NB_OK; + + case NB_EV_PREPARE: + /* NOTHING */ + return NB_OK; + + case NB_EV_APPLY: + bfd_sessions_remove_manual(); + break; + + case NB_EV_ABORT: + /* NOTHING */ + return NB_OK; + } + return NB_OK; }