]> git.proxmox.com Git - mirror_frr.git/commitdiff
bfdd: add vty shell commands
authorRafael Zalamena <rzalamena@opensourcerouting.org>
Wed, 27 Jun 2018 16:26:06 +0000 (13:26 -0300)
committerRafael Zalamena <rzalamena@opensourcerouting.org>
Wed, 8 Aug 2018 21:25:04 +0000 (18:25 -0300)
Implement vty shell integration and allow `bfdd` to be configured
through FRR's vtysh.

Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
bfdd/bfd.h
bfdd/bfdd.c
bfdd/bfdd_vty.c [new file with mode: 0644]
bfdd/subdir.am
lib/command.c
lib/command.h
lib/vty.c
vtysh/Makefile.am
vtysh/vtysh.c
vtysh/vtysh.h
vtysh/vtysh_config.c

index e4907eccebf198b03463b485bcdec8e7bb0f60a8..f47679f863ba93f8f8e5ad566f2e16c5159c9cc0 100644 (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.
  */
index 3313c8137ccd34a8aca7301e322079ca5a19e29f..fb3a0fcfb8c96d089c0204a0c38f835113fbf705 100644 (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();
 
diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c
new file mode 100644 (file)
index 0000000..73ce067
--- /dev/null
@@ -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);
+}
index fe3daaf5071717f167cc91cb21a65cad715357d9..2ac09139628b2bad7da915634cc8da8d06ee0d77 100644 (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 \
index 0bf856f2484b4ac07d74466629e11f6f92982723..884e477299fe554e842de35272ac528fd3cace51 100644 (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;
index a001a90e2e4641976cea82f31fa3413c27d82710..e78de2d6cb0cdcfd8a48b7f1a5e45e74044def4a 100644 (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 */
 };
 
index 073092dfb6470d762fbb44ec2583787c700bbac0..937876c14f729ccbb6515e26374229f7a97212ec 100644 (file)
--- 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;
index 6d0b4a8fdb85719d1ac61fc6adc0e8e01162e5cf..f241f3c5a012093bd8e006cc539b7cd9ca372932 100644 (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 \
index e25a576926072fe574dd22cd0e6a284f33e32d31..d0b654d2ce594f577d17b21ade6da1ad2cb96f46 100644 (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);
 
index e6ed5659cffb8210d632e26a89515a846f1a1e66..c8e4c025e035b77bec851b517a6dd88fed7d7050 100644 (file)
 #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
index 52ab28dfda70ceecf3b55e843d994d3ca9a6232b..42f08342c0b299017a9fb396e08706c6adf08586 100644 (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"))