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:
Rafael Zalamena 2018-06-27 13:40:50 -03:00
parent c2f29cf3d7
commit d3af6147a1
10 changed files with 1064 additions and 0 deletions

View File

@ -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.
*/

View File

@ -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]);

View File

@ -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
View 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);
}

View File

@ -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)

View File

@ -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)"

View File

@ -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,

View File

@ -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,

View File

@ -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 */

View File

@ -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);