bfdd: add vty shell commands

Implement vty shell integration and allow `bfdd` to be configured
through FRR's vtysh.

Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
This commit is contained in:
Rafael Zalamena 2018-06-27 13:26:06 -03:00
parent 7134904b1f
commit c2f29cf3d7
11 changed files with 972 additions and 17 deletions

View File

@ -582,6 +582,14 @@ int bfd_xmt_cb(struct thread *t);
int bfd_echo_xmt_cb(struct thread *t);
/*
* bfdd_vty.c
*
* BFD daemon vty shell commands.
*/
void bfdd_vty_init(void);
/*
* OS compatibility functions.
*/

View File

@ -224,6 +224,9 @@ int main(int argc, char *argv[])
thread_add_read(master, control_accept, NULL, bglobal.bg_csock,
&bglobal.bg_csockev);
/* Install commands. */
bfdd_vty_init();
/* read configuration file and daemonize */
frr_config_fork();

843
bfdd/bfdd_vty.c Normal file
View File

@ -0,0 +1,843 @@
/*
* BFD daemon code
* Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF")
*
* FRR 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, or (at your option) any
* later version.
*
* FRR 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 FRR; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <zebra.h>
#include "lib/command.h"
#include "lib/json.h"
#include "lib/log.h"
#include "lib/vty.h"
#include "bfd.h"
#ifndef VTYSH_EXTRACT_PL
#include "bfdd/bfdd_vty_clippy.c"
#endif
/*
* Commands help string definitions.
*/
#define PEER_STR "Configure peer\n"
#define INTERFACE_NAME_STR "Configure interface name to use\n"
#define PEER_IPV4_STR "IPv4 peer address\n"
#define PEER_IPV6_STR "IPv6 peer address\n"
#define MHOP_STR "Configure multihop\n"
#define LOCAL_STR "Configure local address\n"
#define LOCAL_IPV4_STR "IPv4 local address\n"
#define LOCAL_IPV6_STR "IPv6 local address\n"
#define LOCAL_INTF_STR "Configure local interface name to use\n"
#define VRF_STR "Configure VRF\n"
#define VRF_NAME_STR "Configure VRF name\n"
/*
* Prototypes
*/
static int bfdd_write_config(struct vty *vty);
static int bfdd_peer_write_config(struct vty *vty);
static void _bfdd_peer_write_config(struct hash_backet *hb, void *arg);
static int bfd_configure_peer(struct bfd_peer_cfg *bpc, bool mhop,
const struct sockaddr_any *peer,
const struct sockaddr_any *local,
const char *ifname, const char *vrfname,
char *ebuf, size_t ebuflen);
static struct json_object *__display_peer_json(struct bfd_session *bs);
static void _display_peer_json(struct vty *vty, struct bfd_session *bs);
static void _display_peer(struct vty *vty, struct bfd_session *bs);
static void _display_all_peers(struct vty *vty, bool use_json);
static void _display_peer_iter(struct hash_backet *hb, void *arg);
static void _display_peer_json_iter(struct hash_backet *hb, void *arg);
/*
* Commands definition.
*/
DEFUN_NOSH(bfd_enter, bfd_enter_cmd, "bfd", "Configure BFD peers\n")
{
vty->node = BFD_NODE;
return CMD_SUCCESS;
}
DEFUN_NOSH(
bfd_peer_enter, bfd_peer_enter_cmd,
"peer <A.B.C.D|X:X::X:X> [{multihop|local-address <A.B.C.D|X:X::X:X>|interface IFNAME|vrf NAME}]",
PEER_STR PEER_IPV4_STR PEER_IPV6_STR
MHOP_STR
LOCAL_STR LOCAL_IPV4_STR LOCAL_IPV6_STR
INTERFACE_STR
LOCAL_INTF_STR
VRF_STR VRF_NAME_STR)
{
bool mhop;
int idx;
struct bfd_session *bs;
const char *peer, *ifname, *local, *vrfname;
struct bfd_peer_cfg bpc;
struct sockaddr_any psa, lsa, *lsap;
char errormsg[128];
vrfname = peer = ifname = local = NULL;
/* Gather all provided information. */
peer = argv[1]->arg;
idx = 0;
mhop = argv_find(argv, argc, "multihop", &idx);
idx = 0;
if (argv_find(argv, argc, "interface", &idx))
ifname = argv[idx + 1]->arg;
idx = 0;
if (argv_find(argv, argc, "local-address", &idx))
local = argv[idx + 1]->arg;
idx = 0;
if (argv_find(argv, argc, "vrf", &idx))
vrfname = argv[idx + 1]->arg;
if (vrfname && ifname) {
vty_out(vty, "%% VRF is not mixable with interface\n");
return CMD_WARNING_CONFIG_FAILED;
}
strtosa(peer, &psa);
if (local) {
strtosa(local, &lsa);
lsap = &lsa;
} else
lsap = NULL;
if (bfd_configure_peer(&bpc, mhop, &psa, lsap, ifname, vrfname,
errormsg, sizeof(errormsg))
!= 0) {
vty_out(vty, "%% Invalid peer configuration: %s\n", errormsg);
return CMD_WARNING_CONFIG_FAILED;
}
bs = bs_peer_find(&bpc);
if (bs == NULL) {
bs = ptm_bfd_sess_new(&bpc);
if (bs == NULL) {
vty_out(vty, "%% Failed to add peer.\n");
return CMD_WARNING_CONFIG_FAILED;
}
}
VTY_PUSH_CONTEXT(BFD_PEER_NODE, bs);
return CMD_SUCCESS;
}
DEFPY(bfd_peer_detectmultiplier, bfd_peer_detectmultiplier_cmd,
"detect-multiplier (2-255)$multiplier",
"Configure peer detection multiplier\n"
"Configure peer detection multiplier value\n")
{
struct bfd_session *bs;
bs = VTY_GET_CONTEXT(bfd_session);
bs->detect_mult = multiplier;
return CMD_SUCCESS;
}
DEFPY(bfd_peer_recvinterval, bfd_peer_recvinterval_cmd,
"receive-interval (10-60000)$interval",
"Configure peer receive interval\n"
"Configure peer receive interval value in milliseconds\n")
{
struct bfd_session *bs;
bs = VTY_GET_CONTEXT(bfd_session);
bs->timers.required_min_rx = interval * 1000;
return CMD_SUCCESS;
}
DEFPY(bfd_peer_txinterval, bfd_peer_txinterval_cmd,
"transmit-interval (10-60000)$interval",
"Configure peer transmit interval\n"
"Configure peer transmit interval value in milliseconds\n")
{
struct bfd_session *bs;
bs = VTY_GET_CONTEXT(bfd_session);
bs->up_min_tx = interval * 1000;
return CMD_SUCCESS;
}
DEFPY(bfd_peer_echointerval, bfd_peer_echointerval_cmd,
"echo-interval (10-60000)$interval",
"Configure peer echo interval\n"
"Configure peer echo interval value in milliseconds\n")
{
struct bfd_session *bs;
bs = VTY_GET_CONTEXT(bfd_session);
bs->timers.required_min_echo = interval * 1000;
return CMD_SUCCESS;
}
DEFPY(bfd_peer_shutdown, bfd_peer_shutdown_cmd, "[no] shutdown",
NO_STR "Disable BFD peer")
{
struct bfd_session *bs;
bs = VTY_GET_CONTEXT(bfd_session);
if (no) {
if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
return CMD_SUCCESS;
BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
/* Change and notify state change. */
bs->ses_state = PTM_BFD_DOWN;
control_notify(bs);
/* Enable all timers. */
bfd_recvtimer_update(bs);
bfd_xmttimer_update(bs, bs->xmt_TO);
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) {
bfd_echo_recvtimer_update(bs);
bfd_echo_xmttimer_update(bs, bs->echo_xmt_TO);
}
} else {
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
return CMD_SUCCESS;
BFD_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);
ptm_bfd_snd(bs, 0);
}
return CMD_SUCCESS;
}
DEFPY(bfd_peer_echo, bfd_peer_echo_cmd, "[no] echo-mode",
NO_STR "Configure echo mode\n")
{
struct bfd_session *bs;
bs = VTY_GET_CONTEXT(bfd_session);
if (no) {
if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
return CMD_SUCCESS;
BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
ptm_bfd_echo_stop(bs, 0);
} else {
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
return CMD_SUCCESS;
BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
/* Apply setting immediately. */
if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) {
ptm_bfd_echo_start(bs);
bfd_echo_recvtimer_update(bs);
}
}
return CMD_SUCCESS;
}
DEFPY(bfd_peer_label, bfd_peer_label_cmd, "label WORD$label",
"Register peer label\n"
"Register peer label identification\n")
{
struct bfd_session *bs;
/* Validate label length. */
if (strlen(label) >= MAXNAMELEN) {
vty_out(vty, "%% Label name is too long\n");
return CMD_WARNING_CONFIG_FAILED;
}
bs = VTY_GET_CONTEXT(bfd_session);
if (bfd_session_update_label(bs, label) == -1) {
vty_out(vty, "%% Failed to update peer label.\n");
return CMD_WARNING_CONFIG_FAILED;
}
return CMD_SUCCESS;
}
DEFPY(bfd_no_peer, bfd_no_peer_cmd,
"no peer <A.B.C.D|X:X::X:X>$peer [{multihop|local-address <A.B.C.D|X:X::X:X>$local|interface IFNAME$ifname|vrf NAME$vrfname}]",
NO_STR
PEER_STR PEER_IPV4_STR PEER_IPV6_STR
MHOP_STR
LOCAL_STR LOCAL_IPV4_STR LOCAL_IPV6_STR
INTERFACE_STR
LOCAL_INTF_STR
VRF_STR VRF_NAME_STR)
{
int idx;
bool mhop;
struct bfd_peer_cfg bpc;
struct sockaddr_any psa, lsa, *lsap;
char errormsg[128];
strtosa(peer_str, &psa);
if (local) {
strtosa(local_str, &lsa);
lsap = &lsa;
} else {
lsap = NULL;
}
idx = 0;
mhop = argv_find(argv, argc, "multihop", &idx);
if (bfd_configure_peer(&bpc, mhop, &psa, lsap, ifname, vrfname,
errormsg, sizeof(errormsg))
!= 0) {
vty_out(vty, "%% Invalid peer configuration: %s\n", errormsg);
return CMD_WARNING_CONFIG_FAILED;
}
if (ptm_bfd_ses_del(&bpc) != 0) {
vty_out(vty, "%% Failed to remove peer.\n");
return CMD_WARNING_CONFIG_FAILED;
}
return CMD_SUCCESS;
}
/*
* Show commands helper functions
*/
static void _display_peer(struct vty *vty, struct bfd_session *bs)
{
char buf[256];
time_t now;
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
vty_out(vty, "\tpeer %s", satostr(&bs->mhop.peer));
vty_out(vty, " multihop");
vty_out(vty, " local-address %s", satostr(&bs->mhop.local));
if (bs->mhop.vrf_name[0])
vty_out(vty, " vrf %s", bs->mhop.vrf_name);
vty_out(vty, "\n");
} else {
vty_out(vty, "\tpeer %s", satostr(&bs->mhop.peer));
if (bs->shop.port_name[0])
vty_out(vty, " interface %s", bs->shop.port_name);
vty_out(vty, "\n");
}
if (bs->pl)
vty_out(vty, "\t\tlabel: %s\n", bs->pl->pl_label);
vty_out(vty, "\t\tID: %u\n", bs->discrs.my_discr);
vty_out(vty, "\t\tRemote ID: %u\n", bs->discrs.remote_discr);
vty_out(vty, "\t\tStatus: ");
switch (bs->ses_state) {
case PTM_BFD_ADM_DOWN:
vty_out(vty, "shutdown\n");
break;
case PTM_BFD_DOWN:
vty_out(vty, "down\n");
now = monotime(NULL);
integer2timestr(now - bs->uptime.tv_sec, buf, sizeof(buf));
vty_out(vty, "\t\tDowntime: %s\n", buf);
break;
case PTM_BFD_INIT:
vty_out(vty, "init\n");
break;
case PTM_BFD_UP:
vty_out(vty, "up\n");
now = monotime(NULL);
integer2timestr(now - bs->uptime.tv_sec, buf, sizeof(buf));
vty_out(vty, "\t\tUptime: %s\n", buf);
break;
default:
vty_out(vty, "unknown\n");
break;
}
vty_out(vty, "\t\tDiagnostics: %s\n", diag2str(bs->local_diag));
vty_out(vty, "\t\tRemote diagnostics: %s\n", diag2str(bs->remote_diag));
vty_out(vty, "\t\tLocal timers:\n");
vty_out(vty, "\t\t\tReceive interval: %" PRIu32 "ms\n",
bs->timers.required_min_rx / 1000);
vty_out(vty, "\t\t\tTransmission interval: %" PRIu32 "ms\n",
bs->timers.desired_min_tx / 1000);
vty_out(vty, "\t\t\tEcho transmission interval: ");
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
vty_out(vty, "%" PRIu32 "ms\n",
bs->timers.required_min_echo / 1000);
else
vty_out(vty, "disabled\n");
vty_out(vty, "\t\tRemote timers:\n");
vty_out(vty, "\t\t\tReceive interval: %" PRIu32 "ms\n",
bs->remote_timers.required_min_rx / 1000);
vty_out(vty, "\t\t\tTransmission interval: %" PRIu32 "ms\n",
bs->remote_timers.desired_min_tx / 1000);
vty_out(vty, "\t\t\tEcho transmission interval: %" PRIu32 "ms\n",
bs->remote_timers.required_min_echo / 1000);
vty_out(vty, "\n");
}
static struct json_object *__display_peer_json(struct bfd_session *bs)
{
struct json_object *jo = json_object_new_object();
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
json_object_boolean_true_add(jo, "multihop");
json_object_string_add(jo, "peer", satostr(&bs->mhop.peer));
json_object_string_add(jo, "local", satostr(&bs->mhop.local));
if (bs->mhop.vrf_name[0])
json_object_string_add(jo, "vrf", bs->mhop.vrf_name);
} else {
json_object_boolean_false_add(jo, "multihop");
json_object_string_add(jo, "peer", satostr(&bs->shop.peer));
if (bs->shop.port_name[0])
json_object_string_add(jo, "interface",
bs->shop.port_name);
}
if (bs->pl)
json_object_string_add(jo, "label", bs->pl->pl_label);
json_object_int_add(jo, "id", bs->discrs.my_discr);
json_object_int_add(jo, "remote-id", bs->discrs.remote_discr);
switch (bs->ses_state) {
case PTM_BFD_ADM_DOWN:
json_object_string_add(jo, "status", "shutdown");
break;
case PTM_BFD_DOWN:
json_object_string_add(jo, "status", "down");
json_object_int_add(jo, "downtime",
monotime(NULL) - bs->uptime.tv_sec);
break;
case PTM_BFD_INIT:
json_object_string_add(jo, "status", "init");
break;
case PTM_BFD_UP:
json_object_string_add(jo, "status", "up");
json_object_int_add(jo, "uptime",
monotime(NULL) - bs->uptime.tv_sec);
break;
default:
json_object_string_add(jo, "status", "unknown");
break;
}
json_object_string_add(jo, "diagnostic", diag2str(bs->local_diag));
json_object_string_add(jo, "remote-diagnostic",
diag2str(bs->remote_diag));
json_object_int_add(jo, "receive-interval",
bs->timers.required_min_rx / 1000);
json_object_int_add(jo, "transmit-interval",
bs->timers.desired_min_tx / 1000);
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
json_object_int_add(jo, "echo-interval",
bs->timers.required_min_echo / 1000);
else
json_object_int_add(jo, "echo-interval", 0);
json_object_int_add(jo, "remote-receive-interval",
bs->remote_timers.required_min_rx / 1000);
json_object_int_add(jo, "remote-transmit-interval",
bs->remote_timers.desired_min_tx / 1000);
json_object_int_add(jo, "remote-echo-interval",
bs->remote_timers.required_min_echo / 1000);
return jo;
}
static void _display_peer_json(struct vty *vty, struct bfd_session *bs)
{
struct json_object *jo = __display_peer_json(bs);
vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0));
json_object_free(jo);
}
static void _display_peer_iter(struct hash_backet *hb, void *arg)
{
struct vty *vty = arg;
struct bfd_session *bs = hb->data;
_display_peer(vty, bs);
}
static void _display_peer_json_iter(struct hash_backet *hb, void *arg)
{
struct json_object *jo = arg, *jon = NULL;
struct bfd_session *bs = hb->data;
jon = __display_peer_json(bs);
if (jon == NULL) {
log_warning("%s: not enough memory", __func__);
return;
}
json_object_array_add(jo, jon);
}
static void _display_all_peers(struct vty *vty, bool use_json)
{
struct json_object *jo;
if (use_json == false) {
bfd_id_iterate(_display_peer_iter, vty);
return;
}
jo = json_object_new_array();
bfd_id_iterate(_display_peer_json_iter, jo);
vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0));
json_object_free(jo);
}
DEFPY(bfd_show_peers, bfd_show_peers_cmd, "show bfd peers [json]",
SHOW_STR
"Bidirection Forwarding Detection\n"
"BFD peers status\n"
JSON_STR)
{
bool json = use_json(argc, argv);
if (json) {
_display_all_peers(vty, true);
} else {
vty_out(vty, "BFD Peers:\n");
_display_all_peers(vty, false);
}
return CMD_SUCCESS;
}
DEFPY(bfd_show_peer, bfd_show_peer_cmd,
"show bfd peer <WORD$label|<A.B.C.D|X:X::X:X>$peer [{multihop|local-address <A.B.C.D|X:X::X:X>$local|interface IFNAME$ifname|vrf NAME$vrfname}]> [json]",
SHOW_STR
"Bidirection Forwarding Detection\n"
"BFD peers status\n"
"Peer label\n"
PEER_IPV4_STR PEER_IPV6_STR
MHOP_STR
LOCAL_STR LOCAL_IPV4_STR LOCAL_IPV6_STR
INTERFACE_STR
LOCAL_INTF_STR
VRF_STR VRF_NAME_STR
JSON_STR)
{
int idx;
bool mhop;
struct bfd_session *bs = NULL;
struct peer_label *pl;
struct bfd_peer_cfg bpc;
struct sockaddr_any psa, lsa, *lsap;
char errormsg[128];
/* Look up the BFD peer. */
if (label) {
pl = pl_find(label);
if (pl)
bs = pl->pl_bs;
} else {
strtosa(peer_str, &psa);
if (local) {
strtosa(local_str, &lsa);
lsap = &lsa;
} else
lsap = NULL;
idx = 0;
mhop = argv_find(argv, argc, "multihop", &idx);
if (bfd_configure_peer(&bpc, mhop, &psa, lsap, ifname, vrfname,
errormsg, sizeof(errormsg))
!= 0) {
vty_out(vty, "%% Invalid peer configuration: %s\n",
errormsg);
return CMD_WARNING_CONFIG_FAILED;
}
bs = bs_peer_find(&bpc);
}
/* Find peer data. */
if (bs == NULL) {
vty_out(vty, "%% Unable to find 'peer %s",
label ? label : peer_str);
if (ifname)
vty_out(vty, " interface %s", ifname);
if (local)
vty_out(vty, " local-address %s", local_str);
if (vrfname)
vty_out(vty, " vrf %s", vrfname);
vty_out(vty, "'\n");
return CMD_WARNING_CONFIG_FAILED;
}
if (use_json(argc, argv)) {
_display_peer_json(vty, bs);
} else {
vty_out(vty, "BFD Peer:\n");
_display_peer(vty, bs);
}
return CMD_SUCCESS;
}
/*
* Function definitions.
*/
/*
* Configuration rules:
*
* Single hop:
* peer + (optional vxlan or interface name)
*
* Multi hop:
* peer + local + (optional vrf)
*
* Anything else is misconfiguration.
*/
static int bfd_configure_peer(struct bfd_peer_cfg *bpc, bool mhop,
const struct sockaddr_any *peer,
const struct sockaddr_any *local,
const char *ifname, const char *vrfname,
char *ebuf, size_t ebuflen)
{
memset(bpc, 0, sizeof(*bpc));
/* Defaults */
bpc->bpc_shutdown = true;
bpc->bpc_detectmultiplier = BPC_DEF_DETECTMULTIPLIER;
bpc->bpc_recvinterval = BPC_DEF_RECEIVEINTERVAL;
bpc->bpc_txinterval = BPC_DEF_TRANSMITINTERVAL;
bpc->bpc_echointerval = BPC_DEF_ECHOINTERVAL;
bpc->bpc_lastevent = monotime(NULL);
/* Safety check: when no error buf is provided len must be zero. */
if (ebuf == NULL)
ebuflen = 0;
/* Peer is always mandatory. */
if (peer == NULL) {
snprintf(ebuf, ebuflen, "peer must not be empty");
return -1;
}
/* Validate address families. */
if (peer->sa_sin.sin_family == AF_INET) {
if (local && local->sa_sin.sin_family != AF_INET) {
snprintf(ebuf, ebuflen,
"local is IPv6, but peer is IPv4");
return -1;
}
bpc->bpc_ipv4 = true;
} else if (peer->sa_sin.sin_family == AF_INET6) {
if (local && local->sa_sin.sin_family != AF_INET6) {
snprintf(ebuf, ebuflen,
"local is IPv4, but peer is IPv6");
return -1;
}
bpc->bpc_ipv4 = false;
} else {
snprintf(ebuf, ebuflen, "invalid peer address family");
return -1;
}
/* Copy local and/or peer addresses. */
if (local)
bpc->bpc_local = *local;
if (peer) {
bpc->bpc_peer = *peer;
} else {
/* Peer configuration is mandatory. */
snprintf(ebuf, ebuflen, "no peer configured");
return -1;
}
bpc->bpc_mhop = mhop;
#if 0
/* Handle VxLAN configuration. */
if (vxlan >= 0) {
if (vxlan > ((1 << 24) - 1)) {
snprintf(ebuf, ebuflen, "invalid VxLAN %d", vxlan);
return -1;
}
if (bpc->bpc_mhop) {
snprintf(ebuf, ebuflen,
"multihop doesn't accept VxLAN");
return -1;
}
bpc->bpc_vxlan = vxlan;
}
#endif /* VxLAN */
/* Handle interface specification configuration. */
if (ifname) {
if (bpc->bpc_mhop) {
snprintf(ebuf, ebuflen,
"multihop doesn't accept interface names");
return -1;
}
bpc->bpc_has_localif = true;
if (strlcpy(bpc->bpc_localif, ifname, sizeof(bpc->bpc_localif))
> sizeof(bpc->bpc_localif)) {
snprintf(ebuf, ebuflen, "interface name too long");
return -1;
}
}
/* Handle VRF configuration. */
if (vrfname) {
bpc->bpc_has_vrfname = true;
if (strlcpy(bpc->bpc_vrfname, vrfname, sizeof(bpc->bpc_vrfname))
> sizeof(bpc->bpc_vrfname)) {
snprintf(ebuf, ebuflen, "vrf name too long");
return -1;
}
}
return 0;
}
static int bfdd_write_config(struct vty *vty)
{
vty_out(vty, "bfd\n");
vty_out(vty, "!\n");
return 0;
}
static void _bfdd_peer_write_config(struct hash_backet *hb, void *arg)
{
struct vty *vty = arg;
struct bfd_session *bs = hb->data;
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
vty_out(vty, " peer %s", satostr(&bs->mhop.peer));
vty_out(vty, " multihop");
vty_out(vty, " local-address %s", satostr(&bs->mhop.local));
if (bs->mhop.vrf_name[0])
vty_out(vty, " vrf %s", bs->mhop.vrf_name);
vty_out(vty, "\n");
} else {
vty_out(vty, " peer %s", satostr(&bs->shop.peer));
if (bs->local_address.sa_sin.sin_family != AF_UNSPEC)
vty_out(vty, " local-address %s",
satostr(&bs->local_address));
if (bs->shop.port_name[0])
vty_out(vty, " interface %s", bs->shop.port_name);
vty_out(vty, "\n");
}
if (bs->detect_mult != BPC_DEF_DETECTMULTIPLIER)
vty_out(vty, " detect-multiplier %d\n", bs->detect_mult);
if (bs->timers.required_min_rx != (BPC_DEF_RECEIVEINTERVAL * 1000))
vty_out(vty, " receive-interval %" PRIu32 "\n",
bs->timers.required_min_rx / 1000);
if (bs->timers.desired_min_tx != (BPC_DEF_TRANSMITINTERVAL * 1000))
vty_out(vty, " transmit-interval %" PRIu32 "\n",
bs->timers.desired_min_tx / 1000);
if (bs->timers.required_min_echo != (BPC_DEF_ECHOINTERVAL * 1000))
vty_out(vty, " echo-interval %" PRIu32 "\n",
bs->timers.required_min_echo / 1000);
if (bs->pl)
vty_out(vty, " label %s\n", bs->pl->pl_label);
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
vty_out(vty, " echo-mode\n");
vty_out(vty, " %sshutdown\n",
BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) ? "" : "no ");
vty_out(vty, " !\n");
}
static int bfdd_peer_write_config(struct vty *vty)
{
bfd_id_iterate(_bfdd_peer_write_config, vty);
return 1;
}
struct cmd_node bfd_node = {
BFD_NODE,
"%s(config-bfd)# ",
1,
};
struct cmd_node bfd_peer_node = {
BFD_PEER_NODE,
"%s(config-bfd-peer)# ",
1,
};
void bfdd_vty_init(void)
{
install_element(ENABLE_NODE, &bfd_show_peers_cmd);
install_element(ENABLE_NODE, &bfd_show_peer_cmd);
install_element(CONFIG_NODE, &bfd_enter_cmd);
/* Install BFD node and commands. */
install_node(&bfd_node, bfdd_write_config);
install_default(BFD_NODE);
install_element(BFD_NODE, &bfd_peer_enter_cmd);
install_element(BFD_NODE, &bfd_no_peer_cmd);
/* Install BFD peer node. */
install_node(&bfd_peer_node, bfdd_peer_write_config);
install_default(BFD_PEER_NODE);
install_element(BFD_PEER_NODE, &bfd_peer_detectmultiplier_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_recvinterval_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_txinterval_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_echointerval_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_shutdown_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_echo_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_label_cmd);
}

View File

@ -10,6 +10,7 @@ endif
bfdd_libbfd_a_SOURCES = \
bfdd/bfd.c \
bfdd/bfdd_vty.c \
bfdd/bfd_packet.c \
bfdd/bsd.c \
bfdd/config.c \
@ -19,6 +20,9 @@ bfdd_libbfd_a_SOURCES = \
bfdd/log.c \
# end
bfdd/bfdd_vty_clippy.c: $(CLIPPY_DEPS)
bfdd/bfdd_vty.$(OBJEXT): bfdd/bfdd_vty_clippy.c
noinst_HEADERS += \
bfdd/bfdctl.h \
bfdd/bfd.h \

View File

@ -143,6 +143,8 @@ const char *node_names[] = {
*/
"bgp ipv6 flowspec", /* BGP_FLOWSPECV6_NODE
*/
"bfd", /* BFD_NODE */
"bfd peer", /* BFD_PEER_NODE */
};
/* clang-format on */
@ -987,6 +989,9 @@ enum node_type node_parent(enum node_type node)
case LDP_PSEUDOWIRE_NODE:
ret = LDP_L2VPN_NODE;
break;
case BFD_PEER_NODE:
ret = BFD_NODE;
break;
default:
ret = CONFIG_NODE;
break;
@ -1433,6 +1438,7 @@ void cmd_exit(struct vty *vty)
case RMAP_NODE:
case PBRMAP_NODE:
case VTY_NODE:
case BFD_NODE:
vty->node = CONFIG_NODE;
break;
case BGP_IPV4_NODE:
@ -1474,6 +1480,9 @@ void cmd_exit(struct vty *vty)
case LINK_PARAMS_NODE:
vty->node = INTERFACE_NODE;
break;
case BFD_PEER_NODE:
vty->node = BFD_NODE;
break;
default:
break;
}
@ -1544,6 +1553,8 @@ DEFUN (config_end,
case KEYCHAIN_KEY_NODE:
case VTY_NODE:
case LINK_PARAMS_NODE:
case BFD_NODE:
case BFD_PEER_NODE:
vty_config_unlock(vty);
vty->node = ENABLE_NODE;
break;

View File

@ -139,6 +139,8 @@ enum node_type {
connections.*/
BGP_FLOWSPECV4_NODE, /* BGP IPv4 FLOWSPEC Address-Family */
BGP_FLOWSPECV6_NODE, /* BGP IPv6 FLOWSPEC Address-Family */
BFD_NODE, /* BFD protocol mode. */
BFD_PEER_NODE, /* BFD peer configuration mode. */
NODE_TYPE_MAX, /* maximum */
};

View File

@ -814,6 +814,8 @@ static void vty_end_config(struct vty *vty)
case KEYCHAIN_KEY_NODE:
case VTY_NODE:
case BGP_EVPN_VNI_NODE:
case BFD_NODE:
case BFD_PEER_NODE:
vty_config_unlock(vty);
vty->node = ENABLE_NODE;
break;
@ -1210,6 +1212,8 @@ static void vty_stop_input(struct vty *vty)
case KEYCHAIN_NODE:
case KEYCHAIN_KEY_NODE:
case VTY_NODE:
case BFD_NODE:
case BFD_PEER_NODE:
vty_config_unlock(vty);
vty->node = ENABLE_NODE;
break;

View File

@ -150,6 +150,10 @@ if STATICD
vtysh_scan += $(top_srcdir)/staticd/static_vty.c
endif
if BFDD
vtysh_scan += $(top_srcdir)/bfdd/bfdd_vty.c
endif
vtysh_cmd_FILES = $(vtysh_scan) \
$(top_srcdir)/lib/keychain.c $(top_srcdir)/lib/routemap.c \
$(top_srcdir)/lib/filter.c $(top_srcdir)/lib/plist.c \

View File

@ -134,6 +134,7 @@ struct vtysh_client vtysh_client[] = {
{.fd = -1, .name = "watchfrr", .flag = VTYSH_WATCHFRR, .next = NULL},
{.fd = -1, .name = "pbrd", .flag = VTYSH_PBRD, .next = NULL},
{.fd = -1, .name = "staticd", .flag = VTYSH_STATICD, .next = NULL},
{.fd = -1, .name = "bfdd", .flag = VTYSH_BFDD, .next = NULL},
};
enum vtysh_write_integrated vtysh_write_integrated =
@ -1254,6 +1255,18 @@ struct cmd_node link_params_node = {
static struct cmd_node rpki_node = {RPKI_NODE, "%s(config-rpki)# ", 1};
#endif
#if HAVE_BFDD > 0
static struct cmd_node bfd_node = {
BFD_NODE,
"%s(config-bfd)# ",
};
static struct cmd_node bfd_peer_node = {
BFD_PEER_NODE,
"%s(config-bfd-peer)# ",
};
#endif /* HAVE_BFDD */
/* Defined in lib/vty.c */
extern struct cmd_node vty_node;
@ -1680,6 +1693,32 @@ DEFUNSH(VTYSH_PBRD, vtysh_pbr_map, vtysh_pbr_map_cmd,
return CMD_SUCCESS;
}
#if HAVE_BFDD > 0
DEFUNSH(VTYSH_BFDD, bfd_enter, bfd_enter_cmd, "bfd", "Configure BFD peers\n")
{
vty->node = BFD_NODE;
return CMD_SUCCESS;
}
DEFUNSH(VTYSH_BFDD, bfd_peer_enter, bfd_peer_enter_cmd,
"peer <A.B.C.D|X:X::X:X> [{multihop|local-address <A.B.C.D|X:X::X:X>|interface IFNAME|vrf NAME}]",
"Configure peer\n"
"IPv4 peer address\n"
"IPv6 peer address\n"
"Configure multihop\n"
"Configure local address\n"
"IPv4 local address\n"
"IPv6 local address\n"
INTERFACE_STR
"Configure interface name to use\n"
"Configure VRF\n"
"Configure VRF name\n")
{
vty->node = BFD_PEER_NODE;
return CMD_SUCCESS;
}
#endif /* HAVE_BFDD */
DEFSH(VTYSH_PBRD, vtysh_no_pbr_map_cmd, "no pbr-map WORD [seq (1-700)]",
NO_STR
"Delete pbr-map\n"
@ -1749,6 +1788,7 @@ static int vtysh_exit(struct vty *vty)
case PBRMAP_NODE:
case VTY_NODE:
case KEYCHAIN_NODE:
case BFD_NODE:
vtysh_execute("end");
vtysh_execute("configure terminal");
vty->node = CONFIG_NODE;
@ -1792,6 +1832,9 @@ static int vtysh_exit(struct vty *vty)
case LINK_PARAMS_NODE:
vty->node = INTERFACE_NODE;
break;
case BFD_PEER_NODE:
vty->node = BFD_NODE;
break;
default:
break;
}
@ -1988,6 +2031,17 @@ DEFUNSH(VTYSH_ISISD, vtysh_quit_isisd, vtysh_quit_isisd_cmd, "quit",
return vtysh_exit_isisd(self, vty, argc, argv);
}
#if HAVE_BFDD > 0
DEFUNSH(VTYSH_BFDD, vtysh_exit_bfdd, vtysh_exit_bfdd_cmd, "exit",
"Exit current mode and down to previous mode\n")
{
return vtysh_exit(vty);
}
ALIAS(vtysh_exit_bfdd, vtysh_quit_bfdd_cmd, "quit",
"Exit current mode and down to previous mode\n")
#endif
DEFUNSH(VTYSH_ALL, vtysh_exit_line_vty, vtysh_exit_line_vty_cmd, "exit",
"Exit current mode and down to previous mode\n")
{
@ -3440,6 +3494,10 @@ void vtysh_init_vty(void)
#if defined(HAVE_RPKI)
install_node(&rpki_node, NULL);
#endif
#if HAVE_BFDD > 0
install_node(&bfd_node, NULL);
install_node(&bfd_peer_node, NULL);
#endif /* HAVE_BFDD */
struct cmd_node *node;
for (unsigned int i = 0; i < vector_active(cmdvec); i++) {
@ -3534,6 +3592,21 @@ void vtysh_init_vty(void)
install_element(RMAP_NODE, &vtysh_quit_rmap_cmd);
install_element(PBRMAP_NODE, &vtysh_exit_pbr_map_cmd);
install_element(PBRMAP_NODE, &vtysh_quit_pbr_map_cmd);
#if HAVE_BFDD > 0
/* Enter node. */
install_element(CONFIG_NODE, &bfd_enter_cmd);
install_element(BFD_NODE, &bfd_peer_enter_cmd);
/* Exit/quit node. */
install_element(BFD_NODE, &vtysh_exit_bfdd_cmd);
install_element(BFD_NODE, &vtysh_quit_bfdd_cmd);
install_element(BFD_PEER_NODE, &vtysh_exit_bfdd_cmd);
install_element(BFD_PEER_NODE, &vtysh_quit_bfdd_cmd);
/* End/exit all. */
install_element(BFD_NODE, &vtysh_end_all_cmd);
install_element(BFD_PEER_NODE, &vtysh_end_all_cmd);
#endif /* HAVE_BFDD */
install_element(VTY_NODE, &vtysh_exit_line_vty_cmd);
install_element(VTY_NODE, &vtysh_quit_line_vty_cmd);

View File

@ -24,22 +24,23 @@
#include "memory.h"
DECLARE_MGROUP(MVTYSH)
#define VTYSH_ZEBRA 0x0001
#define VTYSH_RIPD 0x0002
#define VTYSH_RIPNGD 0x0004
#define VTYSH_OSPFD 0x0008
#define VTYSH_OSPF6D 0x0010
#define VTYSH_BGPD 0x0020
#define VTYSH_ISISD 0x0040
#define VTYSH_PIMD 0x0080
#define VTYSH_LDPD 0x0100
#define VTYSH_WATCHFRR 0x0200
#define VTYSH_NHRPD 0x0400
#define VTYSH_EIGRPD 0x0800
#define VTYSH_BABELD 0x1000
#define VTYSH_SHARPD 0x2000
#define VTYSH_PBRD 0x4000
#define VTYSH_STATICD 0x8000
#define VTYSH_ZEBRA 0x00001
#define VTYSH_RIPD 0x00002
#define VTYSH_RIPNGD 0x00004
#define VTYSH_OSPFD 0x00008
#define VTYSH_OSPF6D 0x00010
#define VTYSH_BGPD 0x00020
#define VTYSH_ISISD 0x00040
#define VTYSH_PIMD 0x00080
#define VTYSH_LDPD 0x00100
#define VTYSH_WATCHFRR 0x00200
#define VTYSH_NHRPD 0x00400
#define VTYSH_EIGRPD 0x00800
#define VTYSH_BABELD 0x01000
#define VTYSH_SHARPD 0x02000
#define VTYSH_PBRD 0x04000
#define VTYSH_STATICD 0x08000
#define VTYSH_BFDD 0x10000
#define VTYSH_WAS_ACTIVE (-2)
@ -48,7 +49,7 @@ DECLARE_MGROUP(MVTYSH)
/* watchfrr is not in ALL since library CLI functions should not be
* run on it (logging & co. should stay in a fixed/frozen config, and
* things like prefix lists are not even initialised) */
#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD
#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD
#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_SHARPD
#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD
#define VTYSH_NS VTYSH_ZEBRA

View File

@ -318,6 +318,8 @@ void vtysh_config_parse_line(void *arg, const char *line)
config = config_get(PROTOCOL_NODE, line);
else if (strncmp(line, "mpls", strlen("mpls")) == 0)
config = config_get(MPLS_NODE, line);
else if (strncmp(line, "bfd", strlen("bfd")) == 0)
config = config_get(BFD_NODE, line);
else {
if (strncmp(line, "log", strlen("log")) == 0
|| strncmp(line, "hostname", strlen("hostname"))