From: Rafael Zalamena Date: Wed, 27 Jun 2018 16:40:50 +0000 (-0300) Subject: bfdd: re-route PTM-BFD daemon messages X-Git-Tag: frr-6.1-dev~69^2~14 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=d3af6147a1fce0a72e4a890b4526b122761e8555;p=matthieu%2Ffrr.git 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 --- diff --git a/bfdd/bfd.h b/bfdd/bfd.h index f47679f863..dc33d1c46f 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -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. */ diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c index fb3a0fcfb8..515142243e 100644 --- a/bfdd/bfdd.c +++ b/bfdd/bfdd.c @@ -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]); diff --git a/bfdd/control.c b/bfdd/control.c index 39c7871678..7c6b60db5f 100644 --- a/bfdd/control.c +++ b/bfdd/control.c @@ -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. diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c new file mode 100644 index 0000000000..5e79be91f8 --- /dev/null +++ b/bfdd/ptm_adapter.c @@ -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 + +#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); +} diff --git a/bfdd/subdir.am b/bfdd/subdir.am index 2ac0913962..86923f5cec 100644 --- a/bfdd/subdir.am +++ b/bfdd/subdir.am @@ -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) diff --git a/lib/route_types.txt b/lib/route_types.txt index cfa55e468c..72f59a1b78 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -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)" diff --git a/lib/zclient.h b/lib/zclient.h index 49419b3df3..962b1707c9 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -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, diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index ad574d7e8b..22c0dd623c 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -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, diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index 5975c4058b..5a69568fe8 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -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 */ diff --git a/zebra/zebra_ptm.h b/zebra/zebra_ptm.h index 0e55574a02..ada4f7b4f7 100644 --- a/zebra/zebra_ptm.h +++ b/zebra/zebra_ptm.h @@ -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);