From: Rafael Zalamena Date: Wed, 27 Jun 2018 16:26:06 +0000 (-0300) Subject: bfdd: add vty shell commands X-Git-Tag: frr-6.1-dev~69^2~15 X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;h=c2f29cf3d703e941d86c403682f9b76332bfdd6d;p=mirror_frr.git bfdd: add vty shell commands Implement vty shell integration and allow `bfdd` to be configured through FRR's vtysh. Signed-off-by: Rafael Zalamena --- diff --git a/bfdd/bfd.h b/bfdd/bfd.h index e4907ecce..f47679f86 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -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. */ 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 + +#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 [{multihop|local-address |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 $peer [{multihop|local-address $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 $peer [{multihop|local-address $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 [{multihop|local-address |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"))