summaryrefslogtreecommitdiffstats
path: root/bfdd/bfdd_nb_config.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
commite2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch)
treef0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /bfdd/bfdd_nb_config.c
parentInitial commit. (diff)
downloadfrr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz
frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'bfdd/bfdd_nb_config.c')
-rw-r--r--bfdd/bfdd_nb_config.c847
1 files changed, 847 insertions, 0 deletions
diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c
new file mode 100644
index 0000000..8cf2f0a
--- /dev/null
+++ b/bfdd/bfdd_nb_config.c
@@ -0,0 +1,847 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BFD daemon northbound implementation.
+ *
+ * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
+ * Rafael Zalamena
+ */
+
+#include <zebra.h>
+
+#include "lib/log.h"
+#include "lib/northbound.h"
+
+#include "bfd.h"
+#include "bfdd_nb.h"
+
+/*
+ * Helpers.
+ */
+static void bfd_session_get_key(bool mhop, const struct lyd_node *dnode,
+ struct bfd_key *bk)
+{
+ const char *ifname = NULL, *vrfname = NULL;
+ struct sockaddr_any psa, lsa;
+
+ /* Required destination parameter. */
+ strtosa(yang_dnode_get_string(dnode, "./dest-addr"), &psa);
+
+ /* Get optional source address. */
+ memset(&lsa, 0, sizeof(lsa));
+ if (yang_dnode_exists(dnode, "./source-addr"))
+ strtosa(yang_dnode_get_string(dnode, "./source-addr"), &lsa);
+
+ vrfname = yang_dnode_get_string(dnode, "./vrf");
+
+ if (!mhop) {
+ ifname = yang_dnode_get_string(dnode, "./interface");
+ if (strcmp(ifname, "*") == 0)
+ ifname = NULL;
+ }
+
+ /* Generate the corresponding key. */
+ gen_bfd_key(bk, &psa, &lsa, mhop, ifname, vrfname);
+}
+
+struct session_iter {
+ int count;
+ bool wildcard;
+};
+
+static int session_iter_cb(const struct lyd_node *dnode, void *arg)
+{
+ struct session_iter *iter = arg;
+ const char *ifname;
+
+ ifname = yang_dnode_get_string(dnode, "./interface");
+
+ if (strmatch(ifname, "*"))
+ iter->wildcard = true;
+
+ iter->count++;
+
+ return YANG_ITER_CONTINUE;
+}
+
+static int bfd_session_create(struct nb_cb_create_args *args, bool mhop)
+{
+ const struct lyd_node *sess_dnode;
+ struct session_iter iter;
+ struct bfd_session *bs;
+ const char *dest;
+ const char *ifname;
+ const char *vrfname;
+ struct bfd_key bk;
+ struct prefix p;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ yang_dnode_get_prefix(&p, args->dnode, "./dest-addr");
+
+ if (mhop) {
+ /*
+ * Do not allow IPv6 link-local address for multihop.
+ */
+ if (p.family == AF_INET6
+ && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) {
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "Cannot use link-local address for multihop sessions");
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
+ /*
+ * When `dest-addr` is IPv6 and link-local we must
+ * require interface name, otherwise we can't figure
+ * which interface to use to send the packets.
+ */
+ ifname = yang_dnode_get_string(args->dnode, "./interface");
+
+ if (p.family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)
+ && strcmp(ifname, "*") == 0) {
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "When using link-local you must specify an interface");
+ return NB_ERR_VALIDATION;
+ }
+
+ iter.count = 0;
+ iter.wildcard = false;
+
+ sess_dnode = yang_dnode_get_parent(args->dnode, "sessions");
+
+ dest = yang_dnode_get_string(args->dnode, "./dest-addr");
+ vrfname = yang_dnode_get_string(args->dnode, "./vrf");
+
+ yang_dnode_iterate(session_iter_cb, &iter, sess_dnode,
+ "./single-hop[dest-addr='%s'][vrf='%s']",
+ dest, vrfname);
+
+ if (iter.wildcard && iter.count > 1) {
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "It is not allowed to configure the same peer with and without ifname");
+ return NB_ERR_VALIDATION;
+ }
+ break;
+
+ case NB_EV_PREPARE:
+ bfd_session_get_key(mhop, args->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. */
+ SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
+ bs->refcount++;
+
+ args->resource->ptr = bs;
+ break;
+ }
+
+ bs = bfd_session_new();
+
+ /* Fill the session key. */
+ bfd_session_get_key(mhop, args->dnode, &bs->key);
+
+ /* Set configuration flags. */
+ bs->refcount = 1;
+ SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
+ if (mhop)
+ SET_FLAG(bs->flags, BFD_SESS_FLAG_MH);
+ if (bs->key.family == AF_INET6)
+ SET_FLAG(bs->flags, BFD_SESS_FLAG_IPV6);
+
+ args->resource->ptr = bs;
+ break;
+
+ case NB_EV_APPLY:
+ bs = args->resource->ptr;
+
+ /* 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(args->dnode, bs);
+ break;
+
+ case NB_EV_ABORT:
+ bs = args->resource->ptr;
+ if (bs->refcount <= 1)
+ bfd_session_free(bs);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static 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) {
+ case NB_EV_VALIDATE:
+ bfd_session_get_key(mhop, dnode, &bk);
+ if (bfd_key_lookup(bk) == NULL)
+ return NB_ERR_INCONSISTENCY;
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bs = nb_running_unset_entry(dnode);
+ /* CLI is not using this session anymore. */
+ if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) == 0)
+ break;
+
+ 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:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd
+ */
+int bfdd_bfd_create(struct nb_cb_create_args *args)
+{
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ /*
+ * Set any non-NULL value to be able to call
+ * nb_running_unset_entry in bfdd_bfd_destroy.
+ */
+ nb_running_set_entry(args->dnode, (void *)0x1);
+
+ return NB_OK;
+}
+
+int bfdd_bfd_destroy(struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ /* NOTHING */
+ return NB_OK;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ return NB_OK;
+
+ case NB_EV_APPLY:
+ /*
+ * We need to call this to unset pointers from
+ * the child nodes - sessions and profiles.
+ */
+ nb_running_unset_entry(args->dnode);
+
+ bfd_sessions_remove_manual();
+ bfd_profiles_remove();
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ return NB_OK;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/profile
+ */
+int bfdd_bfd_profile_create(struct nb_cb_create_args *args)
+{
+ struct bfd_profile *bp;
+ const char *name;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ name = yang_dnode_get_string(args->dnode, "./name");
+ bp = bfd_profile_new(name);
+ nb_running_set_entry(args->dnode, bp);
+
+ return NB_OK;
+}
+
+int bfdd_bfd_profile_destroy(struct nb_cb_destroy_args *args)
+{
+ struct bfd_profile *bp;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ bp = nb_running_unset_entry(args->dnode);
+ bfd_profile_free(bp);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/profile/detection-multiplier
+ */
+int bfdd_bfd_profile_detection_multiplier_modify(struct nb_cb_modify_args *args)
+{
+ struct bfd_profile *bp;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ bp = nb_running_get_entry(args->dnode, NULL, true);
+ bp->detection_multiplier = yang_dnode_get_uint8(args->dnode, NULL);
+ bfd_profile_update(bp);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/profile/desired-transmission-interval
+ */
+int bfdd_bfd_profile_desired_transmission_interval_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct bfd_profile *bp;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bp = nb_running_get_entry(args->dnode, NULL, true);
+ bp->min_tx = yang_dnode_get_uint32(args->dnode, NULL);
+ bfd_profile_update(bp);
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/profile/required-receive-interval
+ */
+int bfdd_bfd_profile_required_receive_interval_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct bfd_profile *bp;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bp = nb_running_get_entry(args->dnode, NULL, true);
+ bp->min_rx = yang_dnode_get_uint32(args->dnode, NULL);
+ bfd_profile_update(bp);
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/profile/administrative-down
+ */
+int bfdd_bfd_profile_administrative_down_modify(struct nb_cb_modify_args *args)
+{
+ struct bfd_profile *bp;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ bp = nb_running_get_entry(args->dnode, NULL, true);
+ bp->admin_shutdown = yang_dnode_get_bool(args->dnode, NULL);
+ bfd_profile_update(bp);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/profile/passive-mode
+ */
+int bfdd_bfd_profile_passive_mode_modify(struct nb_cb_modify_args *args)
+{
+ struct bfd_profile *bp;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ bp = nb_running_get_entry(args->dnode, NULL, true);
+ bp->passive = yang_dnode_get_bool(args->dnode, NULL);
+ bfd_profile_update(bp);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/profile/minimum-ttl
+ */
+int bfdd_bfd_profile_minimum_ttl_modify(struct nb_cb_modify_args *args)
+{
+ struct bfd_profile *bp;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ bp = nb_running_get_entry(args->dnode, NULL, true);
+ bp->minimum_ttl = yang_dnode_get_uint8(args->dnode, NULL);
+ bfd_profile_update(bp);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/profile/echo-mode
+ */
+int bfdd_bfd_profile_echo_mode_modify(struct nb_cb_modify_args *args)
+{
+ struct bfd_profile *bp;
+ bool echo;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ echo = yang_dnode_get_bool(args->dnode, NULL);
+ bp = nb_running_get_entry(args->dnode, NULL, true);
+ if (bp->echo_mode == echo)
+ return NB_OK;
+
+ bp->echo_mode = echo;
+ bfd_profile_update(bp);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/profile/desired-echo-transmission-interval
+ */
+int bfdd_bfd_profile_desired_echo_transmission_interval_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct bfd_profile *bp;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bp = nb_running_get_entry(args->dnode, NULL, true);
+ bp->min_echo_tx = yang_dnode_get_uint32(args->dnode, NULL);
+ bfd_profile_update(bp);
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/profile/required-echo-receive-interval
+ */
+int bfdd_bfd_profile_required_echo_receive_interval_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct bfd_profile *bp;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bp = nb_running_get_entry(args->dnode, NULL, true);
+ bp->min_echo_rx = yang_dnode_get_uint32(args->dnode, NULL);
+ bfd_profile_update(bp);
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop
+ */
+int bfdd_bfd_sessions_single_hop_create(struct nb_cb_create_args *args)
+{
+ return bfd_session_create(args, false);
+}
+
+int bfdd_bfd_sessions_single_hop_destroy(struct nb_cb_destroy_args *args)
+{
+ return bfd_session_destroy(args->event, args->dnode, false);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/source-addr
+ */
+int bfdd_bfd_sessions_single_hop_source_addr_modify(
+ struct nb_cb_modify_args *args)
+{
+ return NB_OK;
+}
+
+int bfdd_bfd_sessions_single_hop_source_addr_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/profile
+ */
+int bfdd_bfd_sessions_single_hop_profile_modify(struct nb_cb_modify_args *args)
+{
+ struct bfd_session *bs;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ bfd_profile_apply(yang_dnode_get_string(args->dnode, NULL), bs);
+
+ return NB_OK;
+}
+
+int bfdd_bfd_sessions_single_hop_profile_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct bfd_session *bs;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ bfd_profile_remove(bs);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/detection-multiplier
+ */
+int bfdd_bfd_sessions_single_hop_detection_multiplier_modify(
+ struct nb_cb_modify_args *args)
+{
+ uint8_t detection_multiplier = yang_dnode_get_uint8(args->dnode, NULL);
+ struct bfd_session *bs;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ bs->peer_profile.detection_multiplier = detection_multiplier;
+ bfd_session_apply(bs);
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/desired-transmission-interval
+ */
+int bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct bfd_session *bs;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ bs->peer_profile.min_tx =
+ yang_dnode_get_uint32(args->dnode, NULL);
+ bfd_session_apply(bs);
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/required-receive-interval
+ */
+int bfdd_bfd_sessions_single_hop_required_receive_interval_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct bfd_session *bs;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ bs->peer_profile.min_rx =
+ yang_dnode_get_uint32(args->dnode, NULL);
+ bfd_session_apply(bs);
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/administrative-down
+ */
+int bfdd_bfd_sessions_single_hop_administrative_down_modify(
+ struct nb_cb_modify_args *args)
+{
+ bool shutdown = yang_dnode_get_bool(args->dnode, NULL);
+ struct bfd_session *bs;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ return NB_OK;
+
+ case NB_EV_APPLY:
+ break;
+
+ case NB_EV_ABORT:
+ return NB_OK;
+ }
+
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ bs->peer_profile.admin_shutdown = shutdown;
+ bfd_session_apply(bs);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/passive-mode
+ */
+int bfdd_bfd_sessions_single_hop_passive_mode_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct bfd_session *bs;
+ bool passive;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ return NB_OK;
+
+ case NB_EV_APPLY:
+ break;
+
+ case NB_EV_ABORT:
+ return NB_OK;
+ }
+
+ passive = yang_dnode_get_bool(args->dnode, NULL);
+
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ bs->peer_profile.passive = passive;
+ bfd_session_apply(bs);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/echo-mode
+ */
+int bfdd_bfd_sessions_single_hop_echo_mode_modify(
+ struct nb_cb_modify_args *args)
+{
+ bool echo = yang_dnode_get_bool(args->dnode, NULL);
+ struct bfd_session *bs;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ return NB_OK;
+
+ case NB_EV_APPLY:
+ break;
+
+ case NB_EV_ABORT:
+ return NB_OK;
+ }
+
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ bs->peer_profile.echo_mode = echo;
+ bfd_session_apply(bs);
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-bfdd:bfdd/bfd/sessions/single-hop/desired-echo-transmission-interval
+ */
+int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct bfd_session *bs;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ bs->peer_profile.min_echo_tx =
+ yang_dnode_get_uint32(args->dnode, NULL);
+ bfd_session_apply(bs);
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-bfdd:bfdd/bfd/sessions/single-hop/required-echo-receive-interval
+ */
+int bfdd_bfd_sessions_single_hop_required_echo_receive_interval_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct bfd_session *bs;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ bs->peer_profile.min_echo_rx =
+ yang_dnode_get_uint32(args->dnode, NULL);
+ bfd_session_apply(bs);
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/multi-hop
+ */
+int bfdd_bfd_sessions_multi_hop_create(struct nb_cb_create_args *args)
+{
+ return bfd_session_create(args, true);
+}
+
+int bfdd_bfd_sessions_multi_hop_destroy(struct nb_cb_destroy_args *args)
+{
+ return bfd_session_destroy(args->event, args->dnode, true);
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/multi-hop/minimum-ttl
+ */
+int bfdd_bfd_sessions_multi_hop_minimum_ttl_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct bfd_session *bs;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ return NB_OK;
+
+ case NB_EV_APPLY:
+ break;
+
+ case NB_EV_ABORT:
+ return NB_OK;
+ }
+
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ bs->peer_profile.minimum_ttl = yang_dnode_get_uint8(args->dnode, NULL);
+ bfd_session_apply(bs);
+
+ return NB_OK;
+}