]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bfdd: re-route PTM-BFD daemon messages
authorRafael Zalamena <rzalamena@opensourcerouting.org>
Wed, 27 Jun 2018 16:40:50 +0000 (13:40 -0300)
committerRafael Zalamena <rzalamena@opensourcerouting.org>
Wed, 8 Aug 2018 21:25:04 +0000 (18:25 -0300)
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>
bfdd/bfd.h
bfdd/bfdd.c
bfdd/control.c
bfdd/ptm_adapter.c [new file with mode: 0644]
bfdd/subdir.am
lib/route_types.txt
lib/zclient.h
zebra/zapi_msg.c
zebra/zebra_ptm.c
zebra/zebra_ptm.h

index f47679f863ba93f8f8e5ad566f2e16c5159c9cc0..dc33d1c46fe54ccf7c49cec649a3ea8ab9abcc5c 100644 (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.
  */
index fb3a0fcfb8c96d089c0204a0c38f835113fbf705..515142243e32e625531a25ae21fc925b70c01c94 100644 (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]);
index 39c7871678785332a16da21e447c33333da6fd0d..7c6b60db5f335f1ce46a67d8bcdd7d699c6e1a3d 100644 (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.
diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c
new file mode 100644 (file)
index 0000000..5e79be9
--- /dev/null
@@ -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);
+}
index 2ac09139628b2bad7da915634cc8da8d06ee0d77..86923f5cec50dfef6d884e8e8e148a7d980d8931 100644 (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)
index cfa55e468cd328b4fdb55bc36de93aa2a7419866..72f59a1b78608d84e436baa6e6835e2bf20b1a7e 100644 (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)"
index 49419b3df33921eb225d375c1205789e9f520333..962b1707c948c9ebc6ff79533c4239dffca73d3d 100644 (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,
index ad574d7e8b91fcff8625cda13a56293fe7ba7544..22c0dd623ca02597f05edf98e5a0f581f7f27df7 100644 (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,
index 5975c4058b99b2454b5b624fbb1486f211de5edf..5a69568fe8096f2474942b1ab5f43906f9001244 100644 (file)
 #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 */
index 0e55574a02b0d684532c290037645a962ebd2a58..ada4f7b4f7dad4ca62eab4dd0d4ca6a64caf5ed0 100644 (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);