diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c index b9e7903613..5072c76aac 100644 --- a/bfdd/bfdd_cli.c +++ b/bfdd/bfdd_cli.c @@ -57,6 +57,12 @@ bfd_cli_is_single_hop(struct vty *vty) return strstr(VTY_CURR_XPATH, "/single-hop") != NULL; } +static bool +bfd_cli_is_profile(struct vty *vty) +{ + return strstr(VTY_CURR_XPATH, "/bfd/profile") != NULL; +} + /* * Functions. */ @@ -117,10 +123,14 @@ DEFPY_YANG_NOSH( char source_str[INET6_ADDRSTRLEN + 32]; char xpath[XPATH_MAXLEN], xpath_srcaddr[XPATH_MAXLEN + 32]; - if (multihop) + if (multihop) { + if (!local_address_str) { + vty_out(vty, "%% local-address is required when using multihop\n"); + return CMD_WARNING_CONFIG_FAILED; + } snprintf(source_str, sizeof(source_str), "[source-addr='%s']", local_address_str); - else + } else source_str[0] = 0; slen = snprintf(xpath, sizeof(xpath), @@ -418,7 +428,7 @@ DEFPY_YANG( NO_STR "Configure echo mode\n") { - if (!bfd_cli_is_single_hop(vty)) { + if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) { vty_out(vty, "%% Echo mode is only available for single hop sessions.\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -446,7 +456,7 @@ DEFPY_YANG( { char value[32]; - if (!bfd_cli_is_single_hop(vty)) { + if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) { vty_out(vty, "%% Echo mode is only available for single hop sessions.\n"); return CMD_WARNING_CONFIG_FAILED; } diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c index fe6a0b7905..c8dd5cc3f6 100644 --- a/bfdd/bfdd_nb_config.c +++ b/bfdd/bfdd_nb_config.c @@ -55,36 +55,87 @@ static void bfd_session_get_key(bool mhop, const struct lyd_node *dnode, gen_bfd_key(bk, &psa, &lsa, mhop, ifname, vrfname); } -static int bfd_session_create(enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource, bool mhop) +struct session_iter { + int count; + bool wildcard; +}; + +static int session_iter_cb(const struct lyd_node *dnode, void *arg) { - struct bfd_session *bs; + 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 *source; + const char *dest; + const char *ifname; + const char *vrfname; struct bfd_key bk; struct prefix p; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: /* * 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. */ - yang_dnode_get_prefix(&p, dnode, "./dest-addr"); + yang_dnode_get_prefix(&p, args->dnode, "./dest-addr"); - ifname = yang_dnode_get_string(dnode, "./interface"); + ifname = yang_dnode_get_string(args->dnode, "./interface"); if (p.family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6) && strcmp(ifname, "*") == 0) { - zlog_warn( - "%s: when using link-local you must specify an interface.", - __func__); + 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"); + + if (mhop) { + source = yang_dnode_get_string(args->dnode, "./source-addr"); + + yang_dnode_iterate(session_iter_cb, &iter, sess_dnode, + "./multi-hop[source-addr='%s'][dest-addr='%s'][vrf='%s']", + source, dest, vrfname); + } else { + 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, dnode, &bk); + bfd_session_get_key(mhop, args->dnode, &bk); bs = bfd_key_lookup(bk); /* This session was already configured by another daemon. */ @@ -93,14 +144,14 @@ static int bfd_session_create(enum nb_event event, const struct lyd_node *dnode, SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); bs->refcount++; - resource->ptr = bs; + args->resource->ptr = bs; break; } bs = bfd_session_new(); /* Fill the session key. */ - bfd_session_get_key(mhop, dnode, &bs->key); + bfd_session_get_key(mhop, args->dnode, &bs->key); /* Set configuration flags. */ bs->refcount = 1; @@ -110,23 +161,23 @@ static int bfd_session_create(enum nb_event event, const struct lyd_node *dnode, if (bs->key.family == AF_INET6) SET_FLAG(bs->flags, BFD_SESS_FLAG_IPV6); - resource->ptr = bs; + args->resource->ptr = bs; break; case NB_EV_APPLY: - bs = resource->ptr; + 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(dnode, bs); + nb_running_set_entry(args->dnode, bs); break; case NB_EV_ABORT: - bs = resource->ptr; + bs = args->resource->ptr; if (bs->refcount <= 1) - bfd_session_free(resource->ptr); + bfd_session_free(bs); break; } @@ -474,8 +525,7 @@ int bfdd_bfd_profile_desired_echo_transmission_interval_modify( */ int bfdd_bfd_sessions_single_hop_create(struct nb_cb_create_args *args) { - return bfd_session_create(args->event, args->dnode, args->resource, - false); + return bfd_session_create(args, false); } int bfdd_bfd_sessions_single_hop_destroy(struct nb_cb_destroy_args *args) @@ -759,8 +809,7 @@ int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify( */ int bfdd_bfd_sessions_multi_hop_create(struct nb_cb_create_args *args) { - return bfd_session_create(args->event, args->dnode, args->resource, - true); + return bfd_session_create(args, true); } int bfdd_bfd_sessions_multi_hop_destroy(struct nb_cb_destroy_args *args)