mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-30 01:16:39 +00:00

Ticket: CM-7615, CM-7773 Reviewed By: CCR-3610, CCR-3708 Testing Done: Unit, BGP Smoke and OSPF Smoke Changes (70790261926b17200c8c9377c4576cd3b486fcef) ported from 2.5 Issue (related to CM-7615): 1. CM-7615: There is mismatch in the client name between ptm display of client BFD sessions and the zebra logs. For example, if bgpd added BFD session, zebra logs will show the client as “bgp” but the ptm display will show it as “quagga” 2. Bigger problem is when 2 clients (for example OSPF and BGP) from Quagga register for same BFD session and only one client de-registers the BFD session. This results in BFD session deletion from PTM even though other client still has the BFD registration. Root Cause: Even though BGP, OSPF and OSPF6 are 3 different clients from Quagga that are trying to register/deregister BFD sessions with PTM, all 3 are represented as one client “quagga” from zebra. This makes it hard for PTM/BFD to distinguish between all three when BFD peer registration/deregistration happens from the clients. Fix: Send the actual client name bgp, ospf or ospf6 from zebra with BFD reg/dereg messages instead of one unified client name “quagga” CM-7773: BFD sessions are not getting cleaned from PTM even though no BGP peering exists in Quagga. Root Cause: PTM cleans up stale BFD sessions from a client when it finds a change in seq id advertised by the client. But, if PTM never detects a change in the seq id then the stale BFD sessions never get cleaned up. The test restarts the quagga without saving the configuration, which results in no BGP peering. No BGP peers are registered with PTM after restart and PTM does not detect a client seq id change resulting in stale BFD sessions. Fix: New client registration message was added in PTM. Every client that is interested in BFD monitoring will register with PTM with the client seq id. Client will register with a different seq id (typically pid) every time it restarts. This will help in detecting the change in seq id and cleanup of stale BFD sessions for a client. Code Changes: To support the new client registration message following changes have been made - Added support for client registration messaging in zebra for sending messages to PTM. - Added support for client registration messaging between zebra and clients (BGP, OSPF and OSPF6) in BFD library. - Expanded the reg/de reg peer messaging between zebra and clients to support client specific seq id to distinguish between multiple clients registering for BFD peer rather than one “quagga” client. - Changes in bgpd, ospfd and ospf6d to send client registrations at the time of daemon initialization and on receiving BFD peer replay message.
479 lines
11 KiB
C
479 lines
11 KiB
C
/**
|
|
* bfd.c: BFD handling routines
|
|
*
|
|
* @copyright Copyright (C) 2015 Cumulus Networks, Inc.
|
|
*
|
|
* This file is part of GNU Zebra.
|
|
*
|
|
* GNU Zebra 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.
|
|
*
|
|
* GNU Zebra 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 GNU Zebra; 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 "command.h"
|
|
#include "memory.h"
|
|
#include "prefix.h"
|
|
#include "thread.h"
|
|
#include "stream.h"
|
|
#include "zclient.h"
|
|
#include "table.h"
|
|
#include "vty.h"
|
|
#include "bfd.h"
|
|
|
|
/*
|
|
* bfd_info_create - Allocate the BFD information
|
|
*/
|
|
struct bfd_info *
|
|
bfd_info_create(void)
|
|
{
|
|
struct bfd_info *bfd_info;
|
|
|
|
bfd_info = XCALLOC (MTYPE_BFD_INFO, sizeof (struct bfd_info));
|
|
assert(bfd_info);
|
|
|
|
bfd_info->status = BFD_STATUS_UNKNOWN;
|
|
bfd_info->last_update = 0;
|
|
return bfd_info;
|
|
}
|
|
|
|
/*
|
|
* bfd_info_free - Free the BFD information.
|
|
*/
|
|
void
|
|
bfd_info_free(struct bfd_info **bfd_info)
|
|
{
|
|
if (*bfd_info)
|
|
{
|
|
XFREE (MTYPE_BFD_INFO, *bfd_info);
|
|
*bfd_info = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* bfd_validate_param - Validate the BFD paramter information.
|
|
*/
|
|
int
|
|
bfd_validate_param(struct vty *vty, const char *dm_str, const char *rx_str,
|
|
const char *tx_str, u_int8_t *dm_val, u_int32_t *rx_val,
|
|
u_int32_t *tx_val)
|
|
{
|
|
VTY_GET_INTEGER_RANGE ("detect-mul", *dm_val, dm_str,
|
|
BFD_MIN_DETECT_MULT, BFD_MAX_DETECT_MULT);
|
|
VTY_GET_INTEGER_RANGE ("min-rx", *rx_val, rx_str,
|
|
BFD_MIN_MIN_RX, BFD_MAX_MIN_RX);
|
|
VTY_GET_INTEGER_RANGE ("min-tx", *tx_val, tx_str,
|
|
BFD_MIN_MIN_TX, BFD_MAX_MIN_TX);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* bfd_set_param - Set the configured BFD paramter values
|
|
*/
|
|
void
|
|
bfd_set_param (struct bfd_info **bfd_info, u_int32_t min_rx, u_int32_t min_tx,
|
|
u_int8_t detect_mult, int defaults, int *command)
|
|
{
|
|
if (!*bfd_info)
|
|
{
|
|
*bfd_info = bfd_info_create();
|
|
*command = ZEBRA_BFD_DEST_REGISTER;
|
|
}
|
|
else
|
|
{
|
|
if (((*bfd_info)->required_min_rx != min_rx) ||
|
|
((*bfd_info)->desired_min_tx != min_tx) ||
|
|
((*bfd_info)->detect_mult != detect_mult))
|
|
*command = ZEBRA_BFD_DEST_UPDATE;
|
|
}
|
|
|
|
if (*command)
|
|
{
|
|
(*bfd_info)->required_min_rx = min_rx;
|
|
(*bfd_info)->desired_min_tx = min_tx;
|
|
(*bfd_info)->detect_mult = detect_mult;
|
|
}
|
|
|
|
if (!defaults)
|
|
SET_FLAG ((*bfd_info)->flags, BFD_FLAG_PARAM_CFG);
|
|
else
|
|
UNSET_FLAG ((*bfd_info)->flags, BFD_FLAG_PARAM_CFG);
|
|
}
|
|
|
|
/*
|
|
* bfd_peer_sendmsg - Format and send a peer register/Unregister
|
|
* command to Zebra to be forwarded to BFD
|
|
*/
|
|
void
|
|
bfd_peer_sendmsg (struct zclient *zclient, struct bfd_info *bfd_info,
|
|
int family, void *dst_ip, void *src_ip, char *if_name,
|
|
int ttl, int multihop, int command, int set_flag,
|
|
vrf_id_t vrf_id)
|
|
{
|
|
struct stream *s;
|
|
int ret;
|
|
int len;
|
|
|
|
/* Check socket. */
|
|
if (!zclient || zclient->sock < 0)
|
|
{
|
|
zlog_debug("%s: Can't send BFD peer register, Zebra client not "
|
|
"established", __FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
s = zclient->obuf;
|
|
stream_reset (s);
|
|
zclient_create_header (s, command, vrf_id);
|
|
|
|
stream_putl(s, getpid());
|
|
|
|
stream_putw(s, family);
|
|
switch (family)
|
|
{
|
|
case AF_INET:
|
|
stream_put_in_addr (s, (struct in_addr *)dst_ip);
|
|
break;
|
|
#ifdef HAVE_IPV6
|
|
case AF_INET6:
|
|
stream_put(s, dst_ip, 16);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (command != ZEBRA_BFD_DEST_DEREGISTER)
|
|
{
|
|
stream_putl(s, bfd_info->required_min_rx);
|
|
stream_putl(s, bfd_info->desired_min_tx);
|
|
stream_putc(s, bfd_info->detect_mult);
|
|
}
|
|
|
|
if (multihop)
|
|
{
|
|
stream_putc(s, 1);
|
|
/* Multi-hop destination send the source IP address to BFD */
|
|
if (src_ip)
|
|
{
|
|
stream_putw(s, family);
|
|
switch (family)
|
|
{
|
|
case AF_INET:
|
|
stream_put_in_addr (s, (struct in_addr *) src_ip);
|
|
break;
|
|
#ifdef HAVE_IPV6
|
|
case AF_INET6:
|
|
stream_put(s, src_ip, 16);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
stream_putc(s, ttl);
|
|
}
|
|
else
|
|
{
|
|
stream_putc(s, 0);
|
|
#ifdef HAVE_IPV6
|
|
if ((family == AF_INET6) && (src_ip))
|
|
{
|
|
stream_putw(s, family);
|
|
stream_put(s, src_ip, 16);
|
|
}
|
|
#endif
|
|
if (if_name)
|
|
{
|
|
len = strlen(if_name);
|
|
stream_putc(s, len);
|
|
stream_put(s, if_name, len);
|
|
}
|
|
else
|
|
{
|
|
stream_putc(s, 0);
|
|
}
|
|
}
|
|
|
|
stream_putw_at (s, 0, stream_get_endp (s));
|
|
|
|
ret = zclient_send_message(zclient);
|
|
|
|
if (ret < 0)
|
|
{
|
|
zlog_warn("bfd_peer_sendmsg: zclient_send_message() failed");
|
|
return;
|
|
}
|
|
|
|
if (set_flag)
|
|
{
|
|
if (command == ZEBRA_BFD_DEST_REGISTER)
|
|
SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG);
|
|
else if (command == ZEBRA_BFD_DEST_DEREGISTER)
|
|
UNSET_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* bfd_get_command_dbg_str - Convert command to a debug string.
|
|
*/
|
|
const char *
|
|
bfd_get_command_dbg_str(int command)
|
|
{
|
|
switch (command)
|
|
{
|
|
case ZEBRA_BFD_DEST_REGISTER:
|
|
return "Register";
|
|
case ZEBRA_BFD_DEST_DEREGISTER:
|
|
return "Deregister";
|
|
case ZEBRA_BFD_DEST_UPDATE:
|
|
return "Update";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
/*
|
|
* bfd_get_peer_info - Extract the Peer information for which the BFD session
|
|
* went down from the message sent from Zebra to clients.
|
|
*/
|
|
struct interface *
|
|
bfd_get_peer_info (struct stream *s, struct prefix *dp, struct prefix *sp,
|
|
int *status)
|
|
{
|
|
unsigned int ifindex;
|
|
struct interface *ifp = NULL;
|
|
int plen;
|
|
|
|
/* Get interface index. */
|
|
ifindex = stream_getl (s);
|
|
|
|
/* Lookup index. */
|
|
if (ifindex != 0)
|
|
{
|
|
ifp = if_lookup_by_index (ifindex);
|
|
if (ifp == NULL)
|
|
{
|
|
zlog_warn ("zebra_interface_bfd_read: "
|
|
"Can't find interface by ifindex: %d ", ifindex);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Fetch destination address. */
|
|
dp->family = stream_getc (s);
|
|
|
|
plen = prefix_blen (dp);
|
|
stream_get (&dp->u.prefix, s, plen);
|
|
dp->prefixlen = stream_getc (s);
|
|
|
|
/* Get BFD status. */
|
|
*status = stream_getl (s);
|
|
|
|
if (sp)
|
|
{
|
|
sp->family = stream_getc (s);
|
|
|
|
plen = prefix_blen (sp);
|
|
stream_get (&sp->u.prefix, s, plen);
|
|
sp->prefixlen = stream_getc (s);
|
|
}
|
|
return ifp;
|
|
}
|
|
|
|
/*
|
|
* bfd_get_status_str - Convert BFD status to a display string.
|
|
*/
|
|
const char *
|
|
bfd_get_status_str(int status)
|
|
{
|
|
switch (status)
|
|
{
|
|
case BFD_STATUS_DOWN:
|
|
return "Down";
|
|
case BFD_STATUS_UP:
|
|
return "Up";
|
|
case BFD_STATUS_UNKNOWN:
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
/*
|
|
* bfd_last_update - Calculate the last BFD update time and convert it
|
|
* into a dd:hh:mm:ss display format.
|
|
*/
|
|
static void
|
|
bfd_last_update (time_t last_update, char *buf, size_t len)
|
|
{
|
|
time_t curr;
|
|
time_t diff;
|
|
struct tm *tm;
|
|
struct timeval tv;
|
|
|
|
/* If no BFD satatus update has ever been received, print `never'. */
|
|
if (last_update == 0)
|
|
{
|
|
snprintf (buf, len, "never");
|
|
return;
|
|
}
|
|
|
|
/* Get current time. */
|
|
quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv);
|
|
curr = tv.tv_sec;
|
|
diff = curr - last_update;
|
|
tm = gmtime (&diff);
|
|
|
|
snprintf (buf, len, "%d:%02d:%02d:%02d",
|
|
tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec);
|
|
}
|
|
|
|
/*
|
|
* bfd_show_param - Show the BFD parameter information.
|
|
*/
|
|
void
|
|
bfd_show_param(struct vty *vty, struct bfd_info *bfd_info, int bfd_tag,
|
|
int extra_space, u_char use_json, json_object *json_obj)
|
|
{
|
|
json_object *json_bfd = NULL;
|
|
|
|
if (!bfd_info)
|
|
return;
|
|
|
|
if (use_json)
|
|
{
|
|
if (bfd_tag)
|
|
json_bfd = json_object_new_object();
|
|
else
|
|
json_bfd = json_obj;
|
|
|
|
json_object_int_add(json_bfd, "detectMultiplier", bfd_info->detect_mult);
|
|
json_object_int_add(json_bfd, "rxMinInterval", bfd_info->required_min_rx);
|
|
json_object_int_add(json_bfd, "txMinInterval", bfd_info->desired_min_tx);
|
|
if (bfd_tag)
|
|
json_object_object_add(json_obj, "peerBfdInfo", json_bfd);
|
|
}
|
|
else
|
|
{
|
|
vty_out (vty, " %s%sDetect Mul: %d, Min Rx interval: %d,"
|
|
" Min Tx interval: %d%s",
|
|
(extra_space) ? " ": "", (bfd_tag) ? "BFD: " : " ",
|
|
bfd_info->detect_mult, bfd_info->required_min_rx,
|
|
bfd_info->desired_min_tx, VTY_NEWLINE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* bfd_show_status - Show the BFD status information.
|
|
*/
|
|
static void
|
|
bfd_show_status(struct vty *vty, struct bfd_info *bfd_info, int bfd_tag,
|
|
int extra_space, u_char use_json, json_object *json_bfd)
|
|
{
|
|
char time_buf[32];
|
|
|
|
if (!bfd_info)
|
|
return;
|
|
|
|
bfd_last_update(bfd_info->last_update, time_buf, 32);
|
|
if (use_json)
|
|
{
|
|
json_object_string_add(json_bfd, "status",
|
|
bfd_get_status_str(bfd_info->status));
|
|
json_object_string_add(json_bfd, "lastUpdate", time_buf);
|
|
}
|
|
else
|
|
{
|
|
vty_out (vty, " %s%sStatus: %s, Last update: %s%s",
|
|
(extra_space) ? " ": "", (bfd_tag) ? "BFD: " : " ",
|
|
bfd_get_status_str(bfd_info->status), time_buf, VTY_NEWLINE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* bfd_show_info - Show the BFD information.
|
|
*/
|
|
void
|
|
bfd_show_info(struct vty *vty, struct bfd_info *bfd_info, int multihop,
|
|
int extra_space, u_char use_json, json_object *json_obj)
|
|
{
|
|
json_object *json_bfd = NULL;
|
|
|
|
if (!bfd_info)
|
|
return;
|
|
|
|
if (use_json)
|
|
{
|
|
json_bfd = json_object_new_object();
|
|
if (multihop)
|
|
json_object_string_add(json_bfd, "type", "multi hop");
|
|
else
|
|
json_object_string_add(json_bfd, "type", "single hop");
|
|
}
|
|
else
|
|
{
|
|
vty_out (vty, " %sBFD: Type: %s%s", (extra_space) ? " " : "",
|
|
(multihop) ? "multi hop" : "single hop", VTY_NEWLINE);
|
|
}
|
|
|
|
bfd_show_param(vty, bfd_info, 0, extra_space, use_json, json_bfd);
|
|
bfd_show_status(vty, bfd_info, 0, extra_space, use_json, json_bfd);
|
|
|
|
if (use_json)
|
|
json_object_object_add(json_obj, "peerBfdInfo", json_bfd);
|
|
else
|
|
vty_out (vty, "%s", VTY_NEWLINE);
|
|
}
|
|
|
|
/*
|
|
* bfd_client_sendmsg - Format and send a client register
|
|
* command to Zebra to be forwarded to BFD
|
|
*/
|
|
void
|
|
bfd_client_sendmsg (struct zclient *zclient, int command)
|
|
{
|
|
struct stream *s;
|
|
int ret;
|
|
|
|
/* Check socket. */
|
|
if (!zclient || zclient->sock < 0)
|
|
{
|
|
zlog_debug("%s: Can't send BFD client register, Zebra client not "
|
|
"established", __FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
s = zclient->obuf;
|
|
stream_reset (s);
|
|
zclient_create_header (s, command, VRF_DEFAULT);
|
|
|
|
stream_putl(s, getpid());
|
|
|
|
stream_putw_at (s, 0, stream_get_endp (s));
|
|
|
|
ret = zclient_send_message(zclient);
|
|
|
|
if (ret < 0)
|
|
{
|
|
zlog_warn("bfd_client_sendmsg %d: zclient_send_message() failed",
|
|
getpid());
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|