summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bfdd/bfd.h8
-rw-r--r--bfdd/bfdd.c3
-rw-r--r--bfdd/bfdd_vty.c843
-rw-r--r--bfdd/subdir.am4
-rw-r--r--lib/command.c11
-rw-r--r--lib/command.h2
-rw-r--r--lib/vty.c4
-rw-r--r--vtysh/Makefile.am4
-rw-r--r--vtysh/vtysh.c73
-rw-r--r--vtysh/vtysh.h35
-rw-r--r--vtysh/vtysh_config.c2
11 files changed, 972 insertions, 17 deletions
diff --git a/bfdd/bfd.h b/bfdd/bfd.h
index e4907ecce..f47679f86 100644
--- a/bfdd/bfd.h
+++ b/bfdd/bfd.h
@@ -583,6 +583,14 @@ int bfd_echo_xmt_cb(struct thread *t);
/*
+ * bfdd_vty.c
+ *
+ * BFD daemon vty shell commands.
+ */
+void bfdd_vty_init(void);
+
+
+/*
* OS compatibility functions.
*/
struct udp_psuedo_header {
diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c
index 3313c8137..fb3a0fcfb 100644
--- a/bfdd/bfdd.c
+++ b/bfdd/bfdd.c
@@ -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();
diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c
new file mode 100644
index 000000000..73ce06761
--- /dev/null
+++ b/bfdd/bfdd_vty.c
@@ -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);
+}
diff --git a/bfdd/subdir.am b/bfdd/subdir.am
index fe3daaf50..2ac091396 100644
--- a/bfdd/subdir.am
+++ b/bfdd/subdir.am
@@ -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 \
diff --git a/lib/command.c b/lib/command.c
index 0bf856f24..884e47729 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -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;
diff --git a/lib/command.h b/lib/command.h
index a001a90e2..e78de2d6c 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -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 */
};
diff --git a/lib/vty.c b/lib/vty.c
index 073092dfb..937876c14 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -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;
diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am
index 6d0b4a8fd..f241f3c5a 100644
--- a/vtysh/Makefile.am
+++ b/vtysh/Makefile.am
@@ -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 \
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index e25a57692..d0b654d2c 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -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);
diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
index e6ed5659c..c8e4c025e 100644
--- a/vtysh/vtysh.h
+++ b/vtysh/vtysh.h
@@ -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
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index 52ab28dfd..42f08342c 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -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"))