]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/zebra_ptm.c
Merge pull request #3502 from donaldsharp/socket_to_me_baby
[mirror_frr.git] / zebra / zebra_ptm.c
index 8c23bf34cf1670c080003fafe9dc4ece86d73fb9..e4a4adba05b19f1e0b97f166d14a4b7104980f8a 100644 (file)
  */
 
 #include <zebra.h>
+
 #include <sys/un.h> /* for sockaddr_un */
 #include <net/if.h>
+
+#include "bfd.h"
+#include "buffer.h"
+#include "command.h"
+#include "if.h"
+#include "network.h"
+#include "ptm_lib.h"
+#include "rib.h"
+#include "stream.h"
+#include "version.h"
+#include "vrf.h"
 #include "vty.h"
-#include "zebra/zserv.h"
-#include "zebra/interface.h"
+#include "lib_errors.h"
+
 #include "zebra/debug.h"
+#include "zebra/interface.h"
+#include "zebra/zebra_errors.h"
 #include "zebra/zebra_ptm.h"
-#include "if.h"
-#include "command.h"
-#include "stream.h"
-#include "ptm_lib.h"
-#include "network.h"
-#include "buffer.h"
 #include "zebra/zebra_ptm_redistribute.h"
-#include "bfd.h"
-#include "vrf.h"
-#include "rib.h"
+#include "zebra/zserv.h"
 #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
@@ -103,13 +117,13 @@ void zebra_ptm_init(void)
 
        ptm_cb.out_data = calloc(1, ZEBRA_PTM_SEND_MAX_SOCKBUF);
        if (!ptm_cb.out_data) {
-               zlog_warn("%s: Allocation of send data failed", __func__);
+               zlog_debug("%s: Allocation of send data failed", __func__);
                return;
        }
 
        ptm_cb.in_data = calloc(1, ZEBRA_PTM_MAX_SOCKBUF);
        if (!ptm_cb.in_data) {
-               zlog_warn("%s: Allocation of recv data failed", __func__);
+               zlog_debug("%s: Allocation of recv data failed", __func__);
                free(ptm_cb.out_data);
                return;
        }
@@ -167,8 +181,8 @@ static int zebra_ptm_flush_messages(struct thread *thread)
 
        switch (buffer_flush_available(ptm_cb.wb, ptm_cb.ptm_sock)) {
        case BUFFER_ERROR:
-               zlog_warn("%s ptm socket error: %s", __func__,
-                         safe_strerror(errno));
+               flog_err_sys(EC_LIB_SOCKET, "%s ptm socket error: %s", __func__,
+                            safe_strerror(errno));
                close(ptm_cb.ptm_sock);
                ptm_cb.ptm_sock = -1;
                zebra_ptm_reset_status(0);
@@ -193,8 +207,8 @@ static int zebra_ptm_send_message(char *data, int size)
        errno = 0;
        switch (buffer_write(ptm_cb.wb, ptm_cb.ptm_sock, data, size)) {
        case BUFFER_ERROR:
-               zlog_warn("%s ptm socket error: %s", __func__,
-                         safe_strerror(errno));
+               flog_err_sys(EC_LIB_SOCKET, "%s ptm socket error: %s", __func__,
+                            safe_strerror(errno));
                close(ptm_cb.ptm_sock);
                ptm_cb.ptm_sock = -1;
                zebra_ptm_reset_status(0);
@@ -491,14 +505,16 @@ static int zebra_ptm_handle_bfd_msg(void *arg, void *in_ctxt,
                        dest_str, src_str);
 
        if (str2prefix(dest_str, &dest_prefix) == 0) {
-               zlog_err("%s: Peer addr %s not found", __func__, dest_str);
+               flog_err(EC_ZEBRA_PREFIX_PARSE_ERROR,
+                        "%s: Peer addr %s not found", __func__, dest_str);
                return -1;
        }
 
        memset(&src_prefix, 0, sizeof(struct prefix));
        if (strcmp(ZEBRA_PTM_INVALID_SRC_IP, src_str)) {
                if (str2prefix(src_str, &src_prefix) == 0) {
-                       zlog_err("%s: Local addr %s not found", __func__,
+                       flog_err(EC_ZEBRA_PREFIX_PARSE_ERROR,
+                                "%s: Local addr %s not found", __func__,
                                 src_str);
                        return -1;
                }
@@ -593,8 +609,9 @@ static int zebra_ptm_handle_msg_cb(void *arg, void *in_ctxt)
                ifp = if_lookup_by_name_all_vrf(port_str);
 
                if (!ifp) {
-                       zlog_err("%s: %s not found in interface list", __func__,
-                                port_str);
+                       flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE,
+                                 "%s: %s not found in interface list",
+                                 __func__, port_str);
                        return -1;
                }
        }
@@ -615,7 +632,7 @@ static int zebra_ptm_handle_msg_cb(void *arg, void *in_ctxt)
 
 int zebra_ptm_sock_read(struct thread *thread)
 {
-       int sock, done = 0;
+       int sock;
        int rc;
 
        errno = 0;
@@ -625,28 +642,25 @@ int zebra_ptm_sock_read(struct thread *thread)
                return -1;
 
        /* PTM communicates in CSV format */
-       while (!done) {
+       do {
                rc = ptm_lib_process_msg(ptm_hdl, sock, ptm_cb.in_data,
                                         ZEBRA_PTM_MAX_SOCKBUF, NULL);
-               if (rc <= 0)
-                       break;
-       }
+       } while (rc > 0);
 
-       if (rc <= 0) {
-               if (((rc == 0) && !errno)
-                   || (errno && (errno != EWOULDBLOCK) && (errno != EAGAIN))) {
-                       zlog_warn("%s routing socket error: %s(%d) bytes %d",
-                                 __func__, safe_strerror(errno), errno, rc);
-
-                       close(ptm_cb.ptm_sock);
-                       ptm_cb.ptm_sock = -1;
-                       zebra_ptm_reset_status(0);
-                       ptm_cb.t_timer = NULL;
-                       thread_add_timer(zebrad.master, zebra_ptm_connect, NULL,
-                                        ptm_cb.reconnect_time,
-                                        &ptm_cb.t_timer);
-                       return (-1);
-               }
+       if (((rc == 0) && !errno)
+           || (errno && (errno != EWOULDBLOCK) && (errno != EAGAIN))) {
+               flog_err_sys(EC_LIB_SOCKET,
+                            "%s routing socket error: %s(%d) bytes %d",
+                            __func__, safe_strerror(errno), errno, rc);
+
+               close(ptm_cb.ptm_sock);
+               ptm_cb.ptm_sock = -1;
+               zebra_ptm_reset_status(0);
+               ptm_cb.t_timer = NULL;
+               thread_add_timer(zebrad.master, zebra_ptm_connect, NULL,
+                                ptm_cb.reconnect_time,
+                                &ptm_cb.t_timer);
+               return (-1);
        }
 
        ptm_cb.t_read = NULL;
@@ -1016,13 +1030,12 @@ int zebra_ptm_bfd_client_deregister(struct zserv *client)
        char tmp_buf[64];
        int data_len = ZEBRA_PTM_SEND_MAX_SOCKBUF;
 
-       if (proto != ZEBRA_ROUTE_OSPF && proto != ZEBRA_ROUTE_BGP
-           && proto != ZEBRA_ROUTE_OSPF6 && proto != ZEBRA_ROUTE_PIM)
+       if (!IS_BFD_ENABLED_PROTOCOL(proto))
                return 0;
 
        if (IS_ZEBRA_DEBUG_EVENT)
-               zlog_err("bfd_client_deregister msg for client %s",
-                        zebra_route_string(proto));
+               zlog_debug("bfd_client_deregister msg for client %s",
+                          zebra_route_string(proto));
 
        if (ptm_cb.ptm_sock == -1) {
                ptm_cb.t_timer = NULL;
@@ -1145,3 +1158,411 @@ 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 pp_free_all(void);
+
+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);
+}
+
+static void pp_free_all(void)
+{
+       struct ptm_process *pp;
+
+       while (!TAILQ_EMPTY(&ppqueue)) {
+               pp = TAILQ_FIRST(&ppqueue);
+               pp_free(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_debug("%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_debug("%s: not enough memory", __func__);
+                       return;
+               }
+       }
+
+       stream_free(msgc);
+       stream_free(msg);
+}
+
+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_debug("%s: not enough memory", __func__);
+               return;
+       }
+
+       /* Send message to all running client daemons. */
+       for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) {
+               if (!IS_BFD_ENABLED_PROTOCOL(client->proto))
+                       continue;
+
+               zserv_send_message(client, msg);
+
+               /* Allocate more messages. */
+               msg = stream_dup(msgc);
+               if (msg == NULL) {
+                       zlog_debug("%s: not enough memory", __func__);
+                       return;
+               }
+       }
+
+       stream_free(msgc);
+       stream_free(msg);
+}
+
+static int _zebra_ptm_bfd_client_deregister(struct zserv *zs)
+{
+       struct stream *msg;
+       struct ptm_process *pp;
+
+       if (!IS_BFD_ENABLED_PROTOCOL(zs->proto))
+               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_debug("%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);
+}
+
+void zebra_ptm_finish(void)
+{
+       /* Remove the client disconnect hook and free all memory. */
+       hook_unregister(zserv_client_close, _zebra_ptm_bfd_client_deregister);
+       pp_free_all();
+}
+
+
+/*
+ * 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_debug("%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;
+       uint32_t cmd;
+
+       /*
+        * 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);
+
+       /*
+        * Client messages must be re-routed, otherwise do the `bfdd`
+        * special treatment.
+        */
+       if (client->proto != ZEBRA_ROUTE_BFD) {
+               _zebra_ptm_reroute(client, msg, ZEBRA_BFD_DEST_REPLAY);
+               return;
+       }
+
+       /* Figure out if this is an DEST_UPDATE or DEST_REPLAY. */
+       if (stream_getl2(msg, &cmd) == false) {
+               zlog_err("%s: expected at least 4 bytes (command)", __func__);
+               return;
+       }
+
+       /*
+        * 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_debug("%s: not enough memory", __func__);
+               return;
+       }
+
+       /* Calculate our header size plus the message contents. */
+       if (cmd != ZEBRA_BFD_DEST_REPLAY) {
+               zhdrlen = ZEBRA_HEADER_SIZE;
+               zmsglen = msg->endp - msg->getp;
+               memcpy(msgc->data + zhdrlen, msg->data + msg->getp, zmsglen);
+
+               zclient_create_header(msgc, cmd, zvrf_id(zvrf));
+
+               msgc->getp = 0;
+               msgc->endp = zhdrlen + zmsglen;
+       } else
+               zclient_create_header(msgc, cmd, zvrf_id(zvrf));
+
+       /* Update the data pointers. */
+       stream_putw_at(msgc, 0, stream_get_endp(msgc));
+
+       zebra_ptm_send_clients(msgc);
+}
+
+/*
+ * Unused functions.
+ */
+void zebra_ptm_if_init(struct zebra_if *zifp __attribute__((__unused__)))
+{
+       /* NOTHING */
+}
+
+int zebra_ptm_get_enable_state(void)
+{
+       return 0;
+}
+
+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 */