mirror of
https://git.proxmox.com/git/mirror_frr
synced 2026-02-01 15:35:00 +00:00
bfdd: re-route PTM-BFD daemon messages
When `bfdd` is enabled - which it is by default - re-route the PTM-BFD messages to the FRR's internal BFD daemon instead of the external PTM daemon. This will help the migration of BFD implementations and avoid duplicating code. Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
This commit is contained in:
parent
c2f29cf3d7
commit
d3af6147a1
@ -590,6 +590,15 @@ int bfd_echo_xmt_cb(struct thread *t);
|
||||
void bfdd_vty_init(void);
|
||||
|
||||
|
||||
/*
|
||||
* ptm_adapter.c
|
||||
*/
|
||||
void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv);
|
||||
void bfdd_zclient_stop(void);
|
||||
|
||||
int ptm_bfd_notify(struct bfd_session *bs);
|
||||
|
||||
|
||||
/*
|
||||
* OS compatibility functions.
|
||||
*/
|
||||
|
||||
@ -82,6 +82,9 @@ static void sigterm_handler(void)
|
||||
/* Signalize shutdown. */
|
||||
frr_early_fini();
|
||||
|
||||
/* Stop receiving message from zebra. */
|
||||
bfdd_zclient_stop();
|
||||
|
||||
/* Shutdown controller to avoid receiving anymore commands. */
|
||||
control_shutdown();
|
||||
|
||||
@ -206,6 +209,9 @@ int main(int argc, char *argv[])
|
||||
/* Initialize BFD data structures. */
|
||||
bfd_initialize();
|
||||
|
||||
/* Initialize zebra connection. */
|
||||
bfdd_zclient_init(&bfdd_privs);
|
||||
|
||||
/* Add descriptors to the event loop. */
|
||||
thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop,
|
||||
&bglobal.bg_ev[0]);
|
||||
|
||||
@ -808,6 +808,9 @@ int control_notify(struct bfd_session *bs)
|
||||
struct bfd_control_socket *bcs;
|
||||
struct bfd_notify_peer *bnp;
|
||||
|
||||
/* Notify zebra listeners as well. */
|
||||
ptm_bfd_notify(bs);
|
||||
|
||||
/*
|
||||
* PERFORMANCE: reuse the bfd_control_msg allocated data for
|
||||
* all control sockets to avoid wasting memory.
|
||||
|
||||
631
bfdd/ptm_adapter.c
Normal file
631
bfdd/ptm_adapter.c
Normal file
@ -0,0 +1,631 @@
|
||||
/*
|
||||
* BFD PTM adapter 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/libfrr.h"
|
||||
#include "lib/queue.h"
|
||||
#include "lib/stream.h"
|
||||
#include "lib/zclient.h"
|
||||
|
||||
#include "lib/bfd.h"
|
||||
|
||||
#include "bfd.h"
|
||||
|
||||
/*
|
||||
* Data structures
|
||||
*/
|
||||
struct ptm_client_notification {
|
||||
struct bfd_session *pcn_bs;
|
||||
struct ptm_client *pcn_pc;
|
||||
|
||||
TAILQ_ENTRY(ptm_client_notification) pcn_entry;
|
||||
};
|
||||
TAILQ_HEAD(pcnqueue, ptm_client_notification);
|
||||
|
||||
struct ptm_client {
|
||||
uint32_t pc_pid;
|
||||
struct pcnqueue pc_pcnqueue;
|
||||
|
||||
TAILQ_ENTRY(ptm_client) pc_entry;
|
||||
};
|
||||
TAILQ_HEAD(pcqueue, ptm_client);
|
||||
|
||||
static struct pcqueue pcqueue;
|
||||
static struct zclient *zclient;
|
||||
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
static int _ptm_msg_address(struct stream *msg, struct sockaddr_any *sa);
|
||||
|
||||
static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa);
|
||||
static int _ptm_msg_read(struct stream *msg, int command,
|
||||
struct bfd_peer_cfg *bpc, struct ptm_client **pc);
|
||||
|
||||
static struct ptm_client *pc_lookup(uint32_t pid);
|
||||
static struct ptm_client *pc_new(uint32_t pid);
|
||||
static struct ptm_client_notification *pcn_new(struct ptm_client *pc,
|
||||
struct bfd_session *bs);
|
||||
static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc,
|
||||
struct bfd_session *bs);
|
||||
static void pcn_free(struct ptm_client_notification *pcn);
|
||||
|
||||
|
||||
static void bfdd_dest_register(struct stream *msg);
|
||||
static void bfdd_dest_deregister(struct stream *msg);
|
||||
static void bfdd_client_register(struct stream *msg);
|
||||
|
||||
/*
|
||||
* Functions
|
||||
*/
|
||||
#ifdef BFD_DEBUG
|
||||
static void debug_printbpc(const char *func, unsigned int line,
|
||||
struct bfd_peer_cfg *bpc);
|
||||
|
||||
static void debug_printbpc(const char *func, unsigned int line,
|
||||
struct bfd_peer_cfg *bpc)
|
||||
{
|
||||
char addr[3][128];
|
||||
char timers[3][128];
|
||||
|
||||
addr[0][0] = addr[1][0] = addr[2][0] = timers[0][0] = timers[1][0] =
|
||||
timers[2][0] = 0;
|
||||
|
||||
snprintf(addr[0], sizeof(addr[0]), "peer:%s", satostr(&bpc->bpc_peer));
|
||||
if (bpc->bpc_local.sa_sin.sin_family)
|
||||
snprintf(addr[1], sizeof(addr[1]), " local:%s",
|
||||
satostr(&bpc->bpc_local));
|
||||
|
||||
if (bpc->bpc_has_localif)
|
||||
snprintf(addr[2], sizeof(addr[2]), " ifname:%s",
|
||||
bpc->bpc_localif);
|
||||
|
||||
if (bpc->bpc_has_vrfname)
|
||||
snprintf(addr[2], sizeof(addr[2]), " vrf:%s", bpc->bpc_vrfname);
|
||||
|
||||
if (bpc->bpc_has_recvinterval)
|
||||
snprintf(timers[0], sizeof(timers[0]), " rx:%lu",
|
||||
bpc->bpc_recvinterval);
|
||||
|
||||
if (bpc->bpc_has_txinterval)
|
||||
snprintf(timers[1], sizeof(timers[1]), " tx:%lu",
|
||||
bpc->bpc_recvinterval);
|
||||
|
||||
if (bpc->bpc_has_detectmultiplier)
|
||||
snprintf(timers[2], sizeof(timers[2]), " detect-multiplier:%d",
|
||||
bpc->bpc_detectmultiplier);
|
||||
|
||||
log_debug("%s:%d: %s %s%s%s%s%s%s", func, line,
|
||||
bpc->bpc_mhop ? "multi-hop" : "single-hop", addr[0], addr[1],
|
||||
addr[2], timers[0], timers[1], timers[2]);
|
||||
}
|
||||
|
||||
#define DEBUG_PRINTBPC(bpc) debug_printbpc(__FILE__, __LINE__, (bpc))
|
||||
#else
|
||||
#define DEBUG_PRINTBPC(bpc)
|
||||
#endif /* BFD_DEBUG */
|
||||
|
||||
static int _ptm_msg_address(struct stream *msg, struct sockaddr_any *sa)
|
||||
{
|
||||
switch (sa->sa_sin.sin_family) {
|
||||
case AF_INET:
|
||||
stream_putc(msg, sa->sa_sin.sin_family);
|
||||
stream_put_in_addr(msg, &sa->sa_sin.sin_addr);
|
||||
stream_putc(msg, 32);
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
stream_putc(msg, sa->sa_sin6.sin6_family);
|
||||
stream_put(msg, &sa->sa_sin6.sin6_addr,
|
||||
sizeof(sa->sa_sin6.sin6_addr));
|
||||
stream_putc(msg, 128);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ptm_bfd_notify(struct bfd_session *bs)
|
||||
{
|
||||
struct stream *msg;
|
||||
struct sockaddr_any sac;
|
||||
|
||||
/*
|
||||
* Message format:
|
||||
* - header: command, vrf
|
||||
* - l: interface index
|
||||
* - c: family
|
||||
* - AF_INET:
|
||||
* - 4 bytes: ipv4
|
||||
* - AF_INET6:
|
||||
* - 16 bytes: ipv6
|
||||
* - c: prefix length
|
||||
* - l: bfd status
|
||||
* - c: family
|
||||
* - AF_INET:
|
||||
* - 4 bytes: ipv4
|
||||
* - AF_INET6:
|
||||
* - 16 bytes: ipv6
|
||||
* - c: prefix length
|
||||
*
|
||||
* Commands: ZEBRA_BFD_DEST_REPLAY
|
||||
*
|
||||
* q(64), l(32), w(16), c(8)
|
||||
*/
|
||||
msg = zclient->obuf;
|
||||
stream_reset(msg);
|
||||
|
||||
/* TODO: VRF handling */
|
||||
zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT);
|
||||
|
||||
/* NOTE: Interface is a shortcut to avoid comparing source address. */
|
||||
stream_putl(msg, bs->ifindex);
|
||||
|
||||
/* BFD destination prefix information. */
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
|
||||
_ptm_msg_address(msg, &bs->mhop.peer);
|
||||
else
|
||||
_ptm_msg_address(msg, &bs->shop.peer);
|
||||
|
||||
/* BFD status */
|
||||
switch (bs->ses_state) {
|
||||
case PTM_BFD_UP:
|
||||
stream_putl(msg, BFD_STATUS_UP);
|
||||
break;
|
||||
|
||||
case PTM_BFD_ADM_DOWN:
|
||||
case PTM_BFD_DOWN:
|
||||
case PTM_BFD_INIT:
|
||||
stream_putl(msg, BFD_STATUS_DOWN);
|
||||
break;
|
||||
|
||||
default:
|
||||
stream_putl(msg, BFD_STATUS_UNKNOWN);
|
||||
break;
|
||||
}
|
||||
|
||||
/* BFD source prefix information. */
|
||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
|
||||
_ptm_msg_address(msg, &bs->mhop.local);
|
||||
} else {
|
||||
if (bs->local_address.sa_sin.sin_family)
|
||||
_ptm_msg_address(msg, &bs->local_address);
|
||||
else if (bs->local_address.sa_sin.sin_family)
|
||||
_ptm_msg_address(msg, &bs->local_ip);
|
||||
else {
|
||||
sac = bs->shop.peer;
|
||||
switch (sac.sa_sin.sin_family) {
|
||||
case AF_INET:
|
||||
memset(&sac.sa_sin.sin_addr, 0,
|
||||
sizeof(sac.sa_sin.sin_family));
|
||||
break;
|
||||
case AF_INET6:
|
||||
memset(&sac.sa_sin6.sin6_addr, 0,
|
||||
sizeof(sac.sa_sin6.sin6_family));
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
/* No local address found yet, so send zeroes. */
|
||||
_ptm_msg_address(msg, &sac);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write packet size. */
|
||||
stream_putw_at(msg, 0, stream_get_endp(msg));
|
||||
|
||||
return zclient_send_message(zclient);
|
||||
}
|
||||
|
||||
static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa)
|
||||
{
|
||||
uint16_t family;
|
||||
|
||||
STREAM_GETW(msg, family);
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
sa->sa_sin.sin_family = family;
|
||||
STREAM_GET(&sa->sa_sin.sin_addr, msg,
|
||||
sizeof(sa->sa_sin.sin_addr));
|
||||
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
||||
sa->sa_sin.sin_len = sizeof(sa->sa_sin);
|
||||
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
||||
return;
|
||||
|
||||
case AF_INET6:
|
||||
sa->sa_sin6.sin6_family = family;
|
||||
STREAM_GET(&sa->sa_sin6.sin6_addr, msg,
|
||||
sizeof(sa->sa_sin6.sin6_addr));
|
||||
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
||||
sa->sa_sin6.sin6_len = sizeof(sa->sa_sin6);
|
||||
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
||||
return;
|
||||
|
||||
default:
|
||||
log_warning("%s: invalid family: %d", __func__, family);
|
||||
break;
|
||||
}
|
||||
|
||||
stream_failure:
|
||||
memset(sa, 0, sizeof(*sa));
|
||||
}
|
||||
|
||||
static int _ptm_msg_read(struct stream *msg, int command,
|
||||
struct bfd_peer_cfg *bpc, struct ptm_client **pc)
|
||||
{
|
||||
uint32_t pid;
|
||||
uint8_t ttl;
|
||||
uint8_t ifnamelen;
|
||||
|
||||
/*
|
||||
* Register/Deregister/Update Message format:
|
||||
* - header: Command, VRF
|
||||
* - l: pid
|
||||
* - w: family
|
||||
* - AF_INET:
|
||||
* - l: destination ipv4
|
||||
* - AF_INET6:
|
||||
* - 16 bytes: destination IPv6
|
||||
* - command != ZEBRA_BFD_DEST_DEREGISTER
|
||||
* - l: min_rx
|
||||
* - l: min_tx
|
||||
* - c: detect multiplier
|
||||
* - c: is_multihop?
|
||||
* - multihop:
|
||||
* - w: family
|
||||
* - AF_INET:
|
||||
* - l: destination ipv4
|
||||
* - AF_INET6:
|
||||
* - 16 bytes: destination IPv6
|
||||
* - c: ttl
|
||||
* - no multihop
|
||||
* - AF_INET6:
|
||||
* - w: family
|
||||
* - 16 bytes: ipv6 address
|
||||
* - c: ifname length
|
||||
* - X bytes: interface name
|
||||
*
|
||||
* q(64), l(32), w(16), c(8)
|
||||
*/
|
||||
|
||||
/* Initialize parameters return values. */
|
||||
memset(bpc, 0, sizeof(*bpc));
|
||||
*pc = NULL;
|
||||
|
||||
/* Find or allocate process context data. */
|
||||
STREAM_GETL(msg, pid);
|
||||
|
||||
*pc = pc_new(pid);
|
||||
if (*pc == NULL) {
|
||||
log_debug("%s: failed to allocate memory", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Register/update peer information. */
|
||||
_ptm_msg_read_address(msg, &bpc->bpc_peer);
|
||||
|
||||
/* Determine IP type from peer destination. */
|
||||
bpc->bpc_ipv4 = (bpc->bpc_peer.sa_sin.sin_family == AF_INET);
|
||||
|
||||
/* Get peer configuration. */
|
||||
if (command != ZEBRA_BFD_DEST_DEREGISTER) {
|
||||
STREAM_GETL(msg, bpc->bpc_recvinterval);
|
||||
bpc->bpc_has_recvinterval =
|
||||
(bpc->bpc_recvinterval != BPC_DEF_RECEIVEINTERVAL);
|
||||
|
||||
STREAM_GETL(msg, bpc->bpc_txinterval);
|
||||
bpc->bpc_has_txinterval =
|
||||
(bpc->bpc_txinterval != BPC_DEF_TRANSMITINTERVAL);
|
||||
|
||||
STREAM_GETC(msg, bpc->bpc_detectmultiplier);
|
||||
bpc->bpc_has_detectmultiplier =
|
||||
(bpc->bpc_detectmultiplier != BPC_DEF_DETECTMULTIPLIER);
|
||||
}
|
||||
|
||||
/* Read (single|multi)hop and its options. */
|
||||
STREAM_GETC(msg, bpc->bpc_mhop);
|
||||
if (bpc->bpc_mhop) {
|
||||
/* Read multihop source address and TTL. */
|
||||
_ptm_msg_read_address(msg, &bpc->bpc_local);
|
||||
STREAM_GETC(msg, ttl);
|
||||
|
||||
/*
|
||||
* TODO: use TTL for something. The line below removes
|
||||
* an unused variable compiler warning.
|
||||
*/
|
||||
ttl = ttl;
|
||||
} else {
|
||||
/* If target is IPv6, then we must obtain local address. */
|
||||
if (bpc->bpc_ipv4 == false)
|
||||
_ptm_msg_read_address(msg, &bpc->bpc_local);
|
||||
|
||||
/*
|
||||
* Read interface name and make sure it fits our data
|
||||
* structure, otherwise fail.
|
||||
*/
|
||||
STREAM_GETC(msg, ifnamelen);
|
||||
if (ifnamelen > sizeof(bpc->bpc_localif)) {
|
||||
log_error("%s: interface name is too big", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bpc->bpc_has_localif = ifnamelen > 0;
|
||||
if (bpc->bpc_has_localif) {
|
||||
STREAM_GET(bpc->bpc_localif, msg, ifnamelen);
|
||||
bpc->bpc_localif[ifnamelen] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sanity check: peer and local address must match IP types. */
|
||||
if (bpc->bpc_local.sa_sin.sin_family != 0
|
||||
&& (bpc->bpc_local.sa_sin.sin_family
|
||||
!= bpc->bpc_peer.sa_sin.sin_family)) {
|
||||
log_warning("%s: peer family doesn't match local type",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
stream_failure:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void bfdd_dest_register(struct stream *msg)
|
||||
{
|
||||
struct ptm_client *pc;
|
||||
struct ptm_client_notification *pcn;
|
||||
struct bfd_session *bs;
|
||||
struct bfd_peer_cfg bpc;
|
||||
|
||||
/* Read the client context and peer data. */
|
||||
if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_REGISTER, &bpc, &pc) == -1)
|
||||
return;
|
||||
|
||||
DEBUG_PRINTBPC(&bpc);
|
||||
|
||||
/* Find or start new BFD session. */
|
||||
bs = bs_peer_find(&bpc);
|
||||
if (bs == NULL) {
|
||||
bs = ptm_bfd_sess_new(&bpc);
|
||||
if (bs == NULL) {
|
||||
log_debug("%s: failed to create BFD session", __func__);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* Don't try to change echo/shutdown state. */
|
||||
bpc.bpc_echo = BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
|
||||
bpc.bpc_shutdown =
|
||||
BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
|
||||
}
|
||||
|
||||
/* Create client peer notification register. */
|
||||
pcn = pcn_new(pc, bs);
|
||||
if (pcn == NULL) {
|
||||
log_error("%s: failed to registrate notifications", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
ptm_bfd_notify(bs);
|
||||
}
|
||||
|
||||
static void bfdd_dest_deregister(struct stream *msg)
|
||||
{
|
||||
struct ptm_client *pc;
|
||||
struct ptm_client_notification *pcn;
|
||||
struct bfd_session *bs;
|
||||
struct bfd_peer_cfg bpc;
|
||||
|
||||
/* Read the client context and peer data. */
|
||||
if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_DEREGISTER, &bpc, &pc) == -1)
|
||||
return;
|
||||
|
||||
DEBUG_PRINTBPC(&bpc);
|
||||
|
||||
/* Find or start new BFD session. */
|
||||
bs = bs_peer_find(&bpc);
|
||||
if (bs == NULL) {
|
||||
log_debug("%s: failed to create BFD session", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Unregister client peer notification. */
|
||||
pcn = pcn_lookup(pc, bs);
|
||||
pcn_free(pcn);
|
||||
}
|
||||
|
||||
/*
|
||||
* header: command, VRF
|
||||
* l: pid
|
||||
*/
|
||||
static void bfdd_client_register(struct stream *msg)
|
||||
{
|
||||
struct ptm_client *pc;
|
||||
uint32_t pid;
|
||||
|
||||
/* Find or allocate process context data. */
|
||||
STREAM_GETL(msg, pid);
|
||||
|
||||
pc = pc_new(pid);
|
||||
if (pc == NULL) {
|
||||
log_error("%s: failed to register client: %u", __func__, pid);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
stream_failure:
|
||||
log_error("%s: failed to register client", __func__);
|
||||
}
|
||||
|
||||
static int bfdd_replay(int cmd, struct zclient *zc, uint16_t len, vrf_id_t vid)
|
||||
{
|
||||
struct stream *msg = zc->ibuf;
|
||||
uint32_t rcmd;
|
||||
|
||||
STREAM_GETL(msg, rcmd);
|
||||
|
||||
switch (rcmd) {
|
||||
case ZEBRA_BFD_DEST_REGISTER:
|
||||
case ZEBRA_BFD_DEST_UPDATE:
|
||||
bfdd_dest_register(msg);
|
||||
break;
|
||||
case ZEBRA_BFD_DEST_DEREGISTER:
|
||||
bfdd_dest_deregister(msg);
|
||||
break;
|
||||
case ZEBRA_BFD_CLIENT_REGISTER:
|
||||
bfdd_client_register(msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_debug("%s: invalid message type %u", __func__, rcmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
stream_failure:
|
||||
log_error("%s: failed to find command", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv)
|
||||
{
|
||||
zclient = zclient_new_notify(master, &zclient_options_default);
|
||||
assert(zclient != NULL);
|
||||
zclient_init(zclient, ZEBRA_ROUTE_BFD, 0, bfdd_priv);
|
||||
|
||||
/*
|
||||
* We'll receive all messages through replay, however it will
|
||||
* contain a special field with the real command inside so we
|
||||
* avoid having to create too many handlers.
|
||||
*/
|
||||
zclient->bfd_dest_replay = bfdd_replay;
|
||||
}
|
||||
|
||||
void bfdd_zclient_stop(void)
|
||||
{
|
||||
zclient_stop(zclient);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Client handling.
|
||||
*/
|
||||
static struct ptm_client *pc_lookup(uint32_t pid)
|
||||
{
|
||||
struct ptm_client *pc;
|
||||
|
||||
TAILQ_FOREACH (pc, &pcqueue, pc_entry) {
|
||||
if (pc->pc_pid != pid)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
||||
static struct ptm_client *pc_new(uint32_t pid)
|
||||
{
|
||||
struct ptm_client *pc;
|
||||
|
||||
/* Look up first, if not found create the client. */
|
||||
pc = pc_lookup(pid);
|
||||
if (pc != NULL)
|
||||
return pc;
|
||||
|
||||
/* Allocate the client data and save it. */
|
||||
pc = XCALLOC(MTYPE_BFDD_CONTROL, sizeof(*pc));
|
||||
if (pc == NULL)
|
||||
return NULL;
|
||||
|
||||
pc->pc_pid = pid;
|
||||
TAILQ_INSERT_HEAD(&pcqueue, pc, pc_entry);
|
||||
return pc;
|
||||
}
|
||||
|
||||
static struct ptm_client_notification *pcn_new(struct ptm_client *pc,
|
||||
struct bfd_session *bs)
|
||||
{
|
||||
struct ptm_client_notification *pcn;
|
||||
|
||||
/* Try to find an existing pcn fist. */
|
||||
pcn = pcn_lookup(pc, bs);
|
||||
if (pcn != NULL)
|
||||
return pcn;
|
||||
|
||||
/* Save the client notification data. */
|
||||
pcn = XCALLOC(MTYPE_BFDD_NOTIFICATION, sizeof(*pcn));
|
||||
if (pcn == NULL)
|
||||
return NULL;
|
||||
|
||||
TAILQ_INSERT_HEAD(&pc->pc_pcnqueue, pcn, pcn_entry);
|
||||
pcn->pcn_pc = pc;
|
||||
pcn->pcn_bs = bs;
|
||||
bs->refcount++;
|
||||
|
||||
return pcn;
|
||||
}
|
||||
|
||||
static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc,
|
||||
struct bfd_session *bs)
|
||||
{
|
||||
struct ptm_client_notification *pcn;
|
||||
|
||||
TAILQ_FOREACH (pcn, &pc->pc_pcnqueue, pcn_entry) {
|
||||
if (pcn->pcn_bs != bs)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return pcn;
|
||||
}
|
||||
|
||||
static void pcn_free(struct ptm_client_notification *pcn)
|
||||
{
|
||||
struct ptm_client *pc;
|
||||
struct bfd_session *bs;
|
||||
|
||||
if (pcn == NULL)
|
||||
return;
|
||||
|
||||
/* Handle session de-registration. */
|
||||
bs = pcn->pcn_bs;
|
||||
pcn->pcn_bs = NULL;
|
||||
bs->refcount--;
|
||||
|
||||
/* Handle ptm_client deregistration. */
|
||||
pc = pcn->pcn_pc;
|
||||
pcn->pcn_pc = NULL;
|
||||
TAILQ_REMOVE(&pc->pc_pcnqueue, pcn, pcn_entry);
|
||||
|
||||
XFREE(MTYPE_BFDD_NOTIFICATION, pcn);
|
||||
}
|
||||
@ -18,6 +18,7 @@ bfdd_libbfd_a_SOURCES = \
|
||||
bfdd/event.c \
|
||||
bfdd/linux.c \
|
||||
bfdd/log.c \
|
||||
bfdd/ptm_adapter.c \
|
||||
# end
|
||||
|
||||
bfdd/bfdd_vty_clippy.c: $(CLIPPY_DEPS)
|
||||
|
||||
@ -81,6 +81,7 @@ ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, 0, "BGP2V
|
||||
ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, 1, "Babel"
|
||||
ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, 1, "SHARP"
|
||||
ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, 0, "PBR"
|
||||
ZEBRA_ROUTE_BFD, bfd, bfdd, '-', 0, 0, 0, "BFD"
|
||||
ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-"
|
||||
|
||||
|
||||
@ -107,3 +108,4 @@ ZEBRA_ROUTE_VNC_DIRECT, "VNC direct (not via zebra) routes"
|
||||
ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)"
|
||||
ZEBRA_ROUTE_SHARP, "Super Happy Advanced Routing Protocol (sharpd)"
|
||||
ZEBRA_ROUTE_PBR, "Policy Based Routing (PBR)"
|
||||
ZEBRA_ROUTE_BFD, "Bidirectional Fowarding Detection (BFD)"
|
||||
|
||||
@ -108,6 +108,7 @@ typedef enum {
|
||||
ZEBRA_VRF_LABEL,
|
||||
ZEBRA_INTERFACE_VRF_UPDATE,
|
||||
ZEBRA_BFD_CLIENT_REGISTER,
|
||||
ZEBRA_BFD_CLIENT_DEREGISTER,
|
||||
ZEBRA_INTERFACE_ENABLE_RADV,
|
||||
ZEBRA_INTERFACE_DISABLE_RADV,
|
||||
ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB,
|
||||
|
||||
@ -3015,6 +3015,9 @@ void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
|
||||
[ZEBRA_BFD_DEST_UPDATE] = zebra_ptm_bfd_dst_register,
|
||||
[ZEBRA_BFD_DEST_REGISTER] = zebra_ptm_bfd_dst_register,
|
||||
[ZEBRA_BFD_DEST_DEREGISTER] = zebra_ptm_bfd_dst_deregister,
|
||||
#if HAVE_BFDD > 0
|
||||
[ZEBRA_BFD_DEST_REPLAY] = zebra_ptm_bfd_dst_replay,
|
||||
#endif /* HAVE_BFDD */
|
||||
[ZEBRA_VRF_UNREGISTER] = zread_vrf_unregister,
|
||||
[ZEBRA_VRF_LABEL] = zread_vrf_label,
|
||||
[ZEBRA_BFD_CLIENT_REGISTER] = zebra_ptm_bfd_client_register,
|
||||
|
||||
@ -39,6 +39,15 @@
|
||||
#include "zebra_vrf.h"
|
||||
#include "version.h"
|
||||
|
||||
/*
|
||||
* Choose the BFD implementation that we'll use.
|
||||
*
|
||||
* There are two implementations:
|
||||
* - PTM BFD: which uses an external daemon;
|
||||
* - bfdd: FRR's own BFD daemon;
|
||||
*/
|
||||
#if HAVE_BFDD == 0
|
||||
|
||||
#define ZEBRA_PTM_RECONNECT_TIME_INITIAL 1 /* initial reconnect is 1s */
|
||||
#define ZEBRA_PTM_RECONNECT_TIME_MAX 300
|
||||
|
||||
@ -1141,3 +1150,399 @@ void zebra_ptm_if_write(struct vty *vty, struct zebra_if *zebra_ifp)
|
||||
if (zebra_ifp->ptm_enable == ZEBRA_IF_PTM_ENABLE_OFF)
|
||||
vty_out(vty, " no ptm-enable\n");
|
||||
}
|
||||
|
||||
#else /* HAVE_BFDD */
|
||||
|
||||
#include "zebra/zebra_memory.h"
|
||||
|
||||
/*
|
||||
* Data structures.
|
||||
*/
|
||||
struct ptm_process {
|
||||
struct zserv *pp_zs;
|
||||
pid_t pp_pid;
|
||||
|
||||
TAILQ_ENTRY(ptm_process) pp_entry;
|
||||
};
|
||||
TAILQ_HEAD(ppqueue, ptm_process) ppqueue;
|
||||
|
||||
DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_PTM_BFD_PROCESS,
|
||||
"PTM BFD process registration table.");
|
||||
|
||||
/*
|
||||
* Prototypes.
|
||||
*/
|
||||
static struct ptm_process *pp_new(pid_t pid, struct zserv *zs);
|
||||
static struct ptm_process *pp_lookup_byzs(struct zserv *zs);
|
||||
static void pp_free(struct ptm_process *pp);
|
||||
|
||||
static void zebra_ptm_send_bfdd(struct stream *msg);
|
||||
static void zebra_ptm_send_clients(struct stream *msg);
|
||||
static int _zebra_ptm_bfd_client_deregister(struct zserv *zs);
|
||||
static void _zebra_ptm_reroute(struct zserv *zs, struct stream *msg,
|
||||
uint32_t command);
|
||||
|
||||
|
||||
/*
|
||||
* Process PID registration.
|
||||
*/
|
||||
static struct ptm_process *pp_new(pid_t pid, struct zserv *zs)
|
||||
{
|
||||
struct ptm_process *pp;
|
||||
|
||||
#ifdef PTM_DEBUG
|
||||
/* Sanity check: more than one client can't have the same PID. */
|
||||
TAILQ_FOREACH(pp, &ppqueue, pp_entry) {
|
||||
if (pp->pp_pid == pid && pp->pp_zs != zs)
|
||||
zlog_err("%s:%d pid and client pointer doesn't match",
|
||||
__FILE__, __LINE__);
|
||||
}
|
||||
#endif /* PTM_DEBUG */
|
||||
|
||||
/* Lookup for duplicates. */
|
||||
pp = pp_lookup_byzs(zs);
|
||||
if (pp != NULL)
|
||||
return pp;
|
||||
|
||||
/* Allocate and register new process. */
|
||||
pp = XCALLOC(MTYPE_ZEBRA_PTM_BFD_PROCESS, sizeof(*pp));
|
||||
if (pp == NULL)
|
||||
return NULL;
|
||||
|
||||
pp->pp_pid = pid;
|
||||
pp->pp_zs = zs;
|
||||
TAILQ_INSERT_HEAD(&ppqueue, pp, pp_entry);
|
||||
|
||||
return pp;
|
||||
}
|
||||
|
||||
static struct ptm_process *pp_lookup_byzs(struct zserv *zs)
|
||||
{
|
||||
struct ptm_process *pp;
|
||||
|
||||
TAILQ_FOREACH(pp, &ppqueue, pp_entry) {
|
||||
if (pp->pp_zs != zs)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return pp;
|
||||
}
|
||||
|
||||
static void pp_free(struct ptm_process *pp)
|
||||
{
|
||||
if (pp == NULL)
|
||||
return;
|
||||
|
||||
TAILQ_REMOVE(&ppqueue, pp, pp_entry);
|
||||
XFREE(MTYPE_ZEBRA_PTM_BFD_PROCESS, pp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Use the FRR's internal daemon implementation.
|
||||
*/
|
||||
static void zebra_ptm_send_bfdd(struct stream *msg)
|
||||
{
|
||||
struct listnode *node;
|
||||
struct zserv *client;
|
||||
struct stream *msgc;
|
||||
|
||||
/* Create copy for replication. */
|
||||
msgc = stream_dup(msg);
|
||||
if (msgc == NULL) {
|
||||
zlog_warn("%s: not enough memory", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send message to all running BFDd daemons. */
|
||||
for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) {
|
||||
if (client->proto != ZEBRA_ROUTE_BFD)
|
||||
continue;
|
||||
|
||||
zserv_send_message(client, msg);
|
||||
|
||||
/* Allocate more messages. */
|
||||
msg = stream_dup(msgc);
|
||||
if (msg == NULL) {
|
||||
zlog_warn("%s: not enough memory", __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
stream_free(msgc);
|
||||
}
|
||||
|
||||
static void zebra_ptm_send_clients(struct stream *msg)
|
||||
{
|
||||
struct listnode *node;
|
||||
struct zserv *client;
|
||||
struct stream *msgc;
|
||||
|
||||
/* Create copy for replication. */
|
||||
msgc = stream_dup(msg);
|
||||
if (msgc == NULL) {
|
||||
zlog_warn("%s: not enough memory", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send message to all running client daemons. */
|
||||
for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) {
|
||||
switch (client->proto) {
|
||||
case ZEBRA_ROUTE_BGP:
|
||||
case ZEBRA_ROUTE_OSPF:
|
||||
case ZEBRA_ROUTE_OSPF6:
|
||||
case ZEBRA_ROUTE_PIM:
|
||||
break;
|
||||
|
||||
default:
|
||||
/* NOTHING: skip this daemon. */
|
||||
continue;
|
||||
}
|
||||
|
||||
zserv_send_message(client, msg);
|
||||
|
||||
/* Allocate more messages. */
|
||||
msg = stream_dup(msgc);
|
||||
if (msg == NULL) {
|
||||
zlog_warn("%s: not enough memory", __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
stream_free(msgc);
|
||||
}
|
||||
|
||||
static int _zebra_ptm_bfd_client_deregister(struct zserv *zs)
|
||||
{
|
||||
struct stream *msg;
|
||||
struct ptm_process *pp;
|
||||
|
||||
/* Filter daemons that must receive this treatment. */
|
||||
switch (zs->proto) {
|
||||
case ZEBRA_ROUTE_BGP:
|
||||
case ZEBRA_ROUTE_OSPF:
|
||||
case ZEBRA_ROUTE_OSPF6:
|
||||
case ZEBRA_ROUTE_PIM:
|
||||
break;
|
||||
|
||||
case ZEBRA_ROUTE_BFD:
|
||||
/* Don't try to send BFDd messages to itself. */
|
||||
return 0;
|
||||
|
||||
default:
|
||||
/* Unsupported daemon. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find daemon pid by zebra connection pointer. */
|
||||
pp = pp_lookup_byzs(zs);
|
||||
if (pp == NULL) {
|
||||
zlog_err("%s:%d failed to find process pid registration",
|
||||
__FILE__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Generate, send message and free() daemon related data. */
|
||||
msg = stream_new(ZEBRA_MAX_PACKET_SIZ);
|
||||
if (msg == NULL) {
|
||||
zlog_warn("%s: not enough memory", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The message type will be BFD_DEST_REPLY so we can use only
|
||||
* one callback at the `bfdd` side, however the real command
|
||||
* number will be included right after the zebra header.
|
||||
*/
|
||||
zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, 0);
|
||||
stream_putl(msg, ZEBRA_BFD_CLIENT_DEREGISTER);
|
||||
|
||||
/* Put process PID. */
|
||||
stream_putl(msg, pp->pp_pid);
|
||||
|
||||
/* Update the data pointers. */
|
||||
stream_putw_at(msg, 0, stream_get_endp(msg));
|
||||
|
||||
zebra_ptm_send_bfdd(msg);
|
||||
|
||||
pp_free(pp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void zebra_ptm_init(void)
|
||||
{
|
||||
/* Initialize the ptm process information list. */
|
||||
TAILQ_INIT(&ppqueue);
|
||||
|
||||
/*
|
||||
* Send deregistration messages to BFD daemon when some other
|
||||
* daemon closes. This will help avoid sending daemons
|
||||
* unnecessary notification messages.
|
||||
*/
|
||||
hook_register(zserv_client_close, _zebra_ptm_bfd_client_deregister);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Message handling.
|
||||
*/
|
||||
static void _zebra_ptm_reroute(struct zserv *zs, struct stream *msg,
|
||||
uint32_t command)
|
||||
{
|
||||
struct stream *msgc;
|
||||
size_t zmsglen, zhdrlen;
|
||||
pid_t ppid;
|
||||
|
||||
/*
|
||||
* Don't modify message in the zebra API. In order to do that we
|
||||
* need to allocate a new message stream and copy the message
|
||||
* provided by zebra.
|
||||
*/
|
||||
msgc = stream_new(ZEBRA_MAX_PACKET_SIZ);
|
||||
if (msgc == NULL) {
|
||||
zlog_warn("%s: not enough memory", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Calculate our header size plus the message contents. */
|
||||
zhdrlen = ZEBRA_HEADER_SIZE + sizeof(uint32_t);
|
||||
zmsglen = msg->endp - msg->getp;
|
||||
memcpy(msgc->data + zhdrlen, msg->data + msg->getp, zmsglen);
|
||||
|
||||
/*
|
||||
* The message type will be BFD_DEST_REPLY so we can use only
|
||||
* one callback at the `bfdd` side, however the real command
|
||||
* number will be included right after the zebra header.
|
||||
*/
|
||||
zclient_create_header(msgc, ZEBRA_BFD_DEST_REPLAY, 0);
|
||||
stream_putl(msgc, command);
|
||||
|
||||
/* Update the data pointers. */
|
||||
msgc->getp = 0;
|
||||
msgc->endp = zhdrlen + zmsglen;
|
||||
stream_putw_at(msgc, 0, stream_get_endp(msgc));
|
||||
|
||||
zebra_ptm_send_bfdd(msgc);
|
||||
|
||||
/* Registrate process PID for shutdown hook. */
|
||||
STREAM_GETL(msg, ppid);
|
||||
pp_new(ppid, zs);
|
||||
|
||||
return;
|
||||
|
||||
stream_failure:
|
||||
zlog_err("%s:%d failed to registrate client pid", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS)
|
||||
{
|
||||
if (IS_ZEBRA_DEBUG_EVENT)
|
||||
zlog_debug("bfd_dst_register msg from client %s: length=%d",
|
||||
zebra_route_string(client->proto), hdr->length);
|
||||
|
||||
_zebra_ptm_reroute(client, msg, ZEBRA_BFD_DEST_REGISTER);
|
||||
}
|
||||
|
||||
void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS)
|
||||
{
|
||||
if (IS_ZEBRA_DEBUG_EVENT)
|
||||
zlog_debug("bfd_dst_deregister msg from client %s: length=%d",
|
||||
zebra_route_string(client->proto), hdr->length);
|
||||
|
||||
_zebra_ptm_reroute(client, msg, ZEBRA_BFD_DEST_DEREGISTER);
|
||||
}
|
||||
|
||||
void zebra_ptm_bfd_client_register(ZAPI_HANDLER_ARGS)
|
||||
{
|
||||
if (IS_ZEBRA_DEBUG_EVENT)
|
||||
zlog_debug("bfd_client_register msg from client %s: length=%d",
|
||||
zebra_route_string(client->proto), hdr->length);
|
||||
|
||||
_zebra_ptm_reroute(client, msg, ZEBRA_BFD_CLIENT_REGISTER);
|
||||
}
|
||||
|
||||
void zebra_ptm_bfd_dst_replay(ZAPI_HANDLER_ARGS)
|
||||
{
|
||||
struct stream *msgc;
|
||||
size_t zmsglen, zhdrlen;
|
||||
|
||||
/*
|
||||
* NOTE:
|
||||
* Replay messages with HAVE_BFDD are meant to be replayed to
|
||||
* the client daemons. These messages are composed and
|
||||
* originated from the `bfdd` daemon.
|
||||
*/
|
||||
if (IS_ZEBRA_DEBUG_EVENT)
|
||||
zlog_debug("bfd_dst_update msg from client %s: length=%d",
|
||||
zebra_route_string(client->proto), hdr->length);
|
||||
|
||||
/*
|
||||
* Don't modify message in the zebra API. In order to do that we
|
||||
* need to allocate a new message stream and copy the message
|
||||
* provided by zebra.
|
||||
*/
|
||||
msgc = stream_new(ZEBRA_MAX_PACKET_SIZ);
|
||||
if (msgc == NULL) {
|
||||
zlog_warn("%s: not enough memory", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Calculate our header size plus the message contents. */
|
||||
zhdrlen = ZEBRA_HEADER_SIZE;
|
||||
zmsglen = msg->endp - msg->getp;
|
||||
memcpy(msgc->data + zhdrlen, msg->data + msg->getp, zmsglen);
|
||||
|
||||
zclient_create_header(msgc, ZEBRA_INTERFACE_BFD_DEST_UPDATE,
|
||||
zvrf_id(zvrf));
|
||||
|
||||
/* Update the data pointers. */
|
||||
msgc->getp = 0;
|
||||
msgc->endp = zhdrlen + zmsglen;
|
||||
stream_putw_at(msgc, 0, stream_get_endp(msgc));
|
||||
|
||||
zebra_ptm_send_clients(msgc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unused functions.
|
||||
*/
|
||||
void zebra_ptm_finish(void)
|
||||
{
|
||||
/* NOTHING */
|
||||
}
|
||||
void zebra_ptm_if_init(struct zebra_if *zifp __attribute__((__unused__)))
|
||||
{
|
||||
/* NOTHING */
|
||||
}
|
||||
|
||||
int zebra_ptm_get_enable_state(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void zebra_ptm_show_status(struct vty *vty __attribute__((__unused__)),
|
||||
struct interface *ifp __attribute__((__unused__)))
|
||||
{
|
||||
/* NOTHING */
|
||||
}
|
||||
|
||||
void zebra_ptm_write(struct vty *vty __attribute__((__unused__)))
|
||||
{
|
||||
/* NOTHING */
|
||||
}
|
||||
|
||||
void zebra_ptm_if_write(struct vty *vty __attribute__((__unused__)),
|
||||
struct zebra_if *zifp __attribute__((__unused__)))
|
||||
{
|
||||
/* NOTHING */
|
||||
}
|
||||
void zebra_ptm_if_set_ptm_state(struct interface *i __attribute__((__unused__)),
|
||||
struct zebra_if *zi __attribute__((__unused__)))
|
||||
{
|
||||
/* NOTHING */
|
||||
}
|
||||
|
||||
#endif /* HAVE_BFDD */
|
||||
|
||||
@ -69,6 +69,9 @@ int zebra_ptm_get_enable_state(void);
|
||||
void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS);
|
||||
void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS);
|
||||
void zebra_ptm_bfd_client_register(ZAPI_HANDLER_ARGS);
|
||||
#if HAVE_BFDD > 0
|
||||
void zebra_ptm_bfd_dst_replay(ZAPI_HANDLER_ARGS);
|
||||
#endif /* HAVE_BFDD */
|
||||
|
||||
void zebra_ptm_show_status(struct vty *vty, struct interface *ifp);
|
||||
void zebra_ptm_if_init(struct zebra_if *zebra_ifp);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user