mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-03 06:59:21 +00:00

Having a fixed set of parameters for each northbound callback isn't a good idea since it makes it difficult to add new parameters whenever that becomes necessary, as several hundreds or thousands of existing callbacks need to be updated accordingly. To remediate this issue, this commit changes the signature of all northbound callbacks to have a single parameter: a pointer to a 'nb_cb_x_args' structure (where x is different for each type of callback). These structures encapsulate all real parameters (both input and output) the callbacks need to have access to. And adding a new parameter to a given callback is as simple as adding a new field to the corresponding 'nb_cb_x_args' structure, without needing to update any instance of that callback in any daemon. This commit includes a .cocci semantic patch that can be used to update old code to the new format automatically. Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
501 lines
11 KiB
C
501 lines
11 KiB
C
/*
|
|
* BFD daemon northbound implementation.
|
|
*
|
|
* Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
|
|
* Rafael Zalamena
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA.
|
|
*/
|
|
|
|
#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);
|
|
|
|
/* Get optional interface and vrf names. */
|
|
if (yang_dnode_exists(dnode, "./interface"))
|
|
ifname = yang_dnode_get_string(dnode, "./interface");
|
|
if (yang_dnode_exists(dnode, "./vrf"))
|
|
vrfname = yang_dnode_get_string(dnode, "./vrf");
|
|
|
|
/* Generate the corresponding key. */
|
|
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 bfd_session *bs;
|
|
const char *ifname;
|
|
struct bfd_key bk;
|
|
struct prefix p;
|
|
|
|
switch (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");
|
|
|
|
/*
|
|
* To support old FRR versions we must allow empty
|
|
* interface to be specified, however that should
|
|
* change in the future.
|
|
*/
|
|
if (yang_dnode_exists(dnode, "./interface"))
|
|
ifname = yang_dnode_get_string(dnode, "./interface");
|
|
else
|
|
ifname = "";
|
|
|
|
if (p.family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)
|
|
&& strlen(ifname) == 0) {
|
|
zlog_warn(
|
|
"%s: when using link-local you must specify "
|
|
"an interface.",
|
|
__func__);
|
|
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. */
|
|
SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
|
|
bs->refcount++;
|
|
|
|
resource->ptr = bs;
|
|
break;
|
|
}
|
|
|
|
bs = bfd_session_new();
|
|
|
|
/* Fill the session key. */
|
|
bfd_session_get_key(mhop, 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);
|
|
|
|
resource->ptr = bs;
|
|
break;
|
|
|
|
case NB_EV_APPLY:
|
|
bs = 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);
|
|
break;
|
|
|
|
case NB_EV_ABORT:
|
|
bs = resource->ptr;
|
|
if (bs->refcount <= 1)
|
|
bfd_session_free(resource->ptr);
|
|
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)
|
|
{
|
|
/* NOTHING */
|
|
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:
|
|
bfd_sessions_remove_manual();
|
|
break;
|
|
|
|
case NB_EV_ABORT:
|
|
/* NOTHING */
|
|
return NB_OK;
|
|
}
|
|
|
|
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->event, args->dnode, args->resource,
|
|
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/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->detect_mult = detection_multiplier;
|
|
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)
|
|
{
|
|
uint32_t tx_interval = yang_dnode_get_uint32(args->dnode, NULL);
|
|
struct bfd_session *bs;
|
|
|
|
switch (args->event) {
|
|
case NB_EV_VALIDATE:
|
|
if (tx_interval < 10000 || tx_interval > 60000000)
|
|
return NB_ERR_VALIDATION;
|
|
break;
|
|
|
|
case NB_EV_PREPARE:
|
|
/* NOTHING */
|
|
break;
|
|
|
|
case NB_EV_APPLY:
|
|
bs = nb_running_get_entry(args->dnode, NULL, true);
|
|
if (tx_interval == bs->timers.desired_min_tx)
|
|
return NB_OK;
|
|
|
|
bs->timers.desired_min_tx = tx_interval;
|
|
bfd_set_polling(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)
|
|
{
|
|
uint32_t rx_interval = yang_dnode_get_uint32(args->dnode, NULL);
|
|
struct bfd_session *bs;
|
|
|
|
switch (args->event) {
|
|
case NB_EV_VALIDATE:
|
|
if (rx_interval < 10000 || rx_interval > 60000000)
|
|
return NB_ERR_VALIDATION;
|
|
break;
|
|
|
|
case NB_EV_PREPARE:
|
|
/* NOTHING */
|
|
break;
|
|
|
|
case NB_EV_APPLY:
|
|
bs = nb_running_get_entry(args->dnode, NULL, true);
|
|
if (rx_interval == bs->timers.required_min_rx)
|
|
return NB_OK;
|
|
|
|
bs->timers.required_min_rx = rx_interval;
|
|
bfd_set_polling(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);
|
|
|
|
if (!shutdown) {
|
|
if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
|
|
return NB_OK;
|
|
|
|
UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
|
|
|
|
/* Change and notify state change. */
|
|
bs->ses_state = PTM_BFD_DOWN;
|
|
control_notify(bs, bs->ses_state);
|
|
|
|
/* Enable all timers. */
|
|
bfd_recvtimer_update(bs);
|
|
bfd_xmttimer_update(bs, bs->xmt_TO);
|
|
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) {
|
|
bfd_echo_recvtimer_update(bs);
|
|
bfd_echo_xmttimer_update(bs, bs->echo_xmt_TO);
|
|
}
|
|
} else {
|
|
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
|
|
return NB_OK;
|
|
|
|
SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
|
|
|
|
/* Disable all events. */
|
|
bfd_recvtimer_delete(bs);
|
|
bfd_echo_recvtimer_delete(bs);
|
|
bfd_xmttimer_delete(bs);
|
|
bfd_echo_xmttimer_delete(bs);
|
|
|
|
/* Change and notify state change. */
|
|
bs->ses_state = PTM_BFD_ADM_DOWN;
|
|
control_notify(bs, bs->ses_state);
|
|
|
|
ptm_bfd_snd(bs, 0);
|
|
}
|
|
|
|
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);
|
|
|
|
if (!echo) {
|
|
if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
|
|
return NB_OK;
|
|
|
|
UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
|
|
ptm_bfd_echo_stop(bs);
|
|
} else {
|
|
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
|
|
return NB_OK;
|
|
|
|
SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
|
|
/* Apply setting immediately. */
|
|
if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
|
|
bs_echo_timer_handler(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)
|
|
{
|
|
uint32_t echo_interval = yang_dnode_get_uint32(args->dnode, NULL);
|
|
struct bfd_session *bs;
|
|
|
|
switch (args->event) {
|
|
case NB_EV_VALIDATE:
|
|
if (echo_interval < 10000 || echo_interval > 60000000)
|
|
return NB_ERR_VALIDATION;
|
|
break;
|
|
|
|
case NB_EV_PREPARE:
|
|
/* NOTHING */
|
|
break;
|
|
|
|
case NB_EV_APPLY:
|
|
bs = nb_running_get_entry(args->dnode, NULL, true);
|
|
if (echo_interval == bs->timers.required_min_echo)
|
|
return NB_OK;
|
|
|
|
bs->timers.required_min_echo = echo_interval;
|
|
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->event, args->dnode, args->resource,
|
|
true);
|
|
}
|
|
|
|
int bfdd_bfd_sessions_multi_hop_destroy(struct nb_cb_destroy_args *args)
|
|
{
|
|
return bfd_session_destroy(args->event, args->dnode, true);
|
|
}
|