This patch adds a 'ping6' plugin that does the same as 'ping', only via
IPv6.

Vince

--- heartbeat-2.1.3/lib/plugins/HBcomm/Makefile.am      2007-12-21 
07:32:27.000000000 -0800
+++ heartbeat-2.1.3.new/lib/plugins/HBcomm/Makefile.am  2008-08-19 
11:11:10.256244000 -0700
@@ -46,7 +46,7 @@ AM_CFLAGS             = @CFLAGS@
 halibdir               = $(libdir)/@HB_PKG@
 plugindir              = $(halibdir)/plugins/HBcomm
 plugin_LTLIBRARIES     = bcast.la mcast.la ping.la serial.la ucast.la \
-                         ping_group.la  $(HBAPING) $(OPENAIS) $(TIPC)
+                         ping_group.la ping6.la $(HBAPING) $(OPENAIS) $(TIPC)
 
 bcast_la_SOURCES       = bcast.c
 bcast_la_LDFLAGS       = -export-dynamic -module -avoid-version
@@ -65,6 +65,10 @@ ping_la_SOURCES              = ping.c
 ping_la_LDFLAGS                = -export-dynamic -module -avoid-version
 ping_la_LIBADD         = $(top_builddir)/replace/libreplace.la
 
+ping6_la_SOURCES       = ping6.c
+ping6_la_LDFLAGS       = -export-dynamic -module -avoid-version
+ping6_la_LIBADD                = $(top_builddir)/replace/libreplace.la
+
 ping_group_la_SOURCES  = ping_group.c
 ping_group_la_LDFLAGS  = -export-dynamic -module -avoid-version
 ping_group_la_LIBADD   = $(top_builddir)/replace/libreplace.la
--- /dev/null   2008-04-14 20:24:50.000000000 -0700
+++ heartbeat-2.1.3.new/lib/plugins/HBcomm/ping6.c      2008-09-12 
15:05:09.073087000 -0700
@@ -0,0 +1,571 @@
+/*
+ * ping.c: ICMP-echo-based heartbeat code for heartbeat.
+ *
+ * Copyright (C) 2000 Alan Robertson <[EMAIL PROTECTED]>
+ *
+ * SECURITY NOTE:  It would be very easy for someone to masquerade as the
+ * device that you're pinging.  If they don't know the password, all they can
+ * do is echo back the packets that you're sending out, or send out old ones.
+ * This does mean that if you're using such an approach, that someone could
+ * make you think you have quorum when you don't during a cluster partition.
+ * The danger in that seems small, but you never know ;-)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library 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
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#      include <netinet/in_systm.h>
+#endif /* HAVE_NETINET_IN_SYSTM_H */
+
+#ifdef HAVE_NETINET_IP_VAR_H
+#      include <netinet/ip_var.h>
+#endif /* HAVE_NETINET_IP_VAR_H */
+
+#ifdef HAVE_NETINET_IP_FW_H
+#      include <netinet/ip_fw.h>
+#endif /* HAVE_NETINET_IP_FW_H */
+
+#ifdef HAVE_NETINET_IP_H
+#      include <netinet/ip.h>
+#endif /* HAVE_NETINET_IP_H */
+
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#ifdef HAVE_NETINET_IP_COMPAT_H
+#      include <netinet/ip_compat.h>
+#endif /* HAVE_NETINET_IP_COMPAT_H */
+
+#include <net/if.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <clplumbing/uids.h>
+#include <heartbeat.h>
+#include <HBcomm.h>
+
+#ifdef linux
+#      define  ICMP6_HDR_SZ    sizeof(struct icmp6_hdr)        /* 8 */
+#else
+#      define  ICMP6_HDR_SZ    8
+#endif
+
+#define PIL_PLUGINTYPE          HB_COMM_TYPE
+#define PIL_PLUGINTYPE_S        HB_COMM_TYPE_S
+#define PIL_PLUGIN              ping6
+#define PIL_PLUGIN_S            "ping6"
+#define PIL_PLUGINLICENSE      LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL   URL_LGPL
+#include <pils/plugin.h>
+
+
+struct ping_private {
+        struct sockaddr_storage addr;          /* ping addr */
+        int                            sock;           /* ping socket */
+       int                     ident;          /* heartbeat pid */
+       int                     iseq;           /* sequence number */
+};
+
+
+static struct hb_media*        ping_new (const char* interface);
+static int             ping_open (struct hb_media* mp);
+static int             ping_close (struct hb_media* mp);
+static void*           ping_read (struct hb_media* mp, int* lenp);
+static int             ping_write (struct hb_media* mp, void* p, int len);
+
+static struct ping_private *
+                       new_ping_interface(const char * host);
+
+static int             ping_mtype(char **buffer);
+static int             ping_descr(char **buffer);
+static int             ping_isping(void);
+
+
+#define                ISPINGOBJECT(mp)        ((mp) && ((mp)->vf == 
(void*)&pingOps))
+#define                PINGASSERT(mp)  g_assert(ISPINGOBJECT(mp))
+
+static struct hb_media_fns pingOps ={
+       ping_new,       /* Create single object function */
+       NULL,           /* whole-line parse function */
+       ping_open,
+       ping_close,
+       ping_read,
+       ping_write,
+       ping_mtype,
+       ping_descr,
+       ping_isping,
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports*  PluginImports;
+static PILPlugin*               OurPlugin;
+static PILInterface*           OurInterface;
+static struct hb_media_imports*        OurImports;
+static void*                   interfprivate;
+
+#define LOG    PluginImports->log
+#define MALLOC PluginImports->alloc
+#define STRDUP  PluginImports->mstrdup
+#define FREE   PluginImports->mfree
+
+static const char *inet_satop(void *sa) {
+       static char buf[INET6_ADDRSTRLEN];
+       struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+       switch (sin->sin_family) {
+               case AF_INET6:
+                       return inet_ntop(sin6->sin6_family,
+                               &(sin6->sin6_addr), buf, INET6_ADDRSTRLEN);
+               case AF_INET:
+                       return inet_ntop(sin->sin_family,
+                               &(sin->sin_addr), buf, INET6_ADDRSTRLEN);
+       }
+       errno = EAFNOSUPPORT;
+       return NULL;
+}
+
+
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+       /* Force the compiler to do a little type checking */
+       (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+       PluginImports = imports;
+       OurPlugin = us;
+
+       /* Register ourself as a plugin */
+       imports->register_plugin(us, &OurPIExports);  
+
+       /*  Register our interface implementation */
+       return imports->register_interface(us, PIL_PLUGINTYPE_S
+       ,       PIL_PLUGIN_S
+       ,       &pingOps
+       ,       NULL            /*close */
+       ,       &OurInterface
+       ,       (void*)&OurImports
+       ,       interfprivate); 
+}
+
+static int
+ping_mtype(char **buffer) { 
+       *buffer = STRDUP(PIL_PLUGIN_S);
+       if (!*buffer) {
+               return 0;
+       }
+
+       return strlen(*buffer);
+}
+
+static int
+ping_descr(char **buffer) { 
+       *buffer = STRDUP("ping6 membership");
+       if (!*buffer) {
+               return 0;
+       }
+
+       return strlen(*buffer);
+}
+
+/* Yes, a ping device */
+
+static int
+ping_isping(void) {
+       return 1;
+}
+
+
+static struct ping_private *
+new_ping_interface(const char * host)
+{
+       struct ping_private*    ppi;
+       struct sockaddr_in6 *to6;
+
+       if ((ppi = (struct ping_private*)MALLOC(sizeof(struct ping_private)))
+       == NULL) {
+               return NULL;
+       }
+       memset(ppi, 0, sizeof (*ppi));
+       to6 = (struct sockaddr_in6 *)&ppi->addr;
+       ppi->ident = getpid() & 0xFFFF;
+
+       if (inet_pton(AF_INET6, host, (void *)&to6->sin6_addr) > 0) {
+#ifdef HAVE_SOCKADDR_IN_SIN_LEN
+               to6->sin6_len = sizeof(struct sockaddr_in6);
+#endif
+               to6->sin6_family = AF_INET6;
+               return(ppi);
+       }
+
+       FREE(ppi);
+       return NULL;
+}
+
+/*
+ *     Create new ping heartbeat object 
+ *     Name of host is passed as a parameter
+ */
+static struct hb_media *
+ping_new(const char * host)
+{
+       struct ping_private*    ipi;
+       struct hb_media *       ret;
+       char *                  name;
+
+       ipi = new_ping_interface(host);
+       if (ipi == NULL) {
+               return(NULL);
+       }
+
+       ret = (struct hb_media *) MALLOC(sizeof(struct hb_media));
+       if (ret == NULL) {
+               FREE(ipi); ipi = NULL;
+               return(NULL);
+       }
+       memset(ret, 0, sizeof(*ret));
+
+       ret->pd = (void*)ipi;
+       name = STRDUP(host);
+       if(name == NULL) {
+               FREE(ipi); ipi = NULL;
+               FREE(ret); ret = NULL;
+               return(NULL);
+       }
+       ret->name = name;
+       add_node(host, PINGNODE_I);
+
+       return(ret);
+}
+
+/*
+ *     Close ICMP ping heartbeat interface
+ */
+
+static int
+ping_close(struct hb_media* mp)
+{
+       struct ping_private * ei;
+       int     rc = HA_OK;
+
+       PINGASSERT(mp);
+       ei = (struct ping_private *) mp->pd;
+
+       if (ei->sock >= 0) {
+               if (close(ei->sock) < 0) {
+                       rc = HA_FAIL;
+               }
+               ei->sock = -1;
+       }
+       return(rc);
+}
+
+
+
+/*
+ * Receive a heartbeat ping reply packet.
+ * NOTE: This code only needs to run once for ALL ping nodes.
+ * FIXME!!
+ */
+
+static char ping_pkt[MAXLINE];
+static void *
+ping_read(struct hb_media* mp, int *lenp)
+{
+       struct ping_private *   ei;
+       union {
+               char            cbuf[MAXLINE+ICMP6_HDR_SZ];
+       }buf;
+       const char *            bufmax = ((char *)&buf)+sizeof(buf);
+       char *                  msgstart;
+       socklen_t               addr_len = sizeof(struct sockaddr_in6);
+       struct sockaddr_in6     their_addr; /* connector's addr information */
+       struct icmp6_hdr        icp;
+       int                     numbytes;
+       struct ha_msg *         msg;
+       const char              *comment;
+       int                     pktlen;
+       
+       PINGASSERT(mp);
+       ei = (struct ping_private *) mp->pd;
+
+ReRead:        /* We recv lots of packets that aren't ours */
+       
+       if ((numbytes=recvfrom(ei->sock, (void *) &buf.cbuf
+       ,       sizeof(buf.cbuf)-1, 0,  &their_addr
+       ,       &addr_len)) < 0) {
+               if (errno != EINTR) {
+                       PILCallLog(LOG, PIL_CRIT, "Error receiving from socket: 
%s"
+                       ,       strerror(errno));
+               }
+               return NULL;
+       }
+       /* Avoid potential buffer overruns */
+       buf.cbuf[numbytes] = EOS;
+
+       if (numbytes < ICMP6_HDR_SZ) {
+               PILCallLog(LOG, PIL_WARN, "ping packet too short (%d bytes) 
from %s"
+               ,       numbytes
+               ,       inet_satop(&their_addr));
+               return NULL;
+       }
+       
+       /* Now the ICMP part */ /* (there may be a better way...) */
+       memcpy(&icp, (buf.cbuf), sizeof(icp));
+       
+       if (icp.icmp6_type != ICMP6_ECHO_REPLY || icp.icmp6_id != ei->ident) {
+               goto ReRead;    /* Not one of ours */
+       }
+
+       if (DEBUGPKT) {
+               PILCallLog(LOG, PIL_DEBUG, "got %d byte packet from %s"
+               ,       numbytes, inet_satop(&their_addr));
+       }
+       msgstart = (buf.cbuf + ICMP6_HDR_SZ);
+
+       if (DEBUGPKTCONT && numbytes > 0) {
+               PILCallLog(LOG, PIL_DEBUG, "%s", msgstart);
+       }
+       
+       pktlen = numbytes - ICMP6_HDR_SZ;
+
+       memcpy(ping_pkt, buf.cbuf + ICMP6_HDR_SZ, pktlen);
+       ping_pkt[pktlen] = 0;
+       *lenp = pktlen + 1;
+       
+       msg = wirefmt2msg(msgstart, bufmax - msgstart, MSG_NEEDAUTH);
+       if (msg == NULL) {
+               errno = EINVAL;
+               return(NULL);
+       }
+       comment = ha_msg_value(msg, F_COMMENT);
+       if (comment == NULL || strcmp(comment, PIL_PLUGIN_S) != 0) {
+               ha_msg_del(msg);
+               errno = EINVAL;
+               return(NULL);
+       }
+       
+       ha_msg_del(msg);
+       return (ping_pkt);
+}
+
+/*
+ * Send a heartbeat packet over ICMP ping channel
+ *
+ * The peculiar thing here is that we don't send the packet we're given at all
+ *
+ * Instead, we send out the packet we want to hear back from them, just
+ * as though we were they ;-)  That's what comes of having such a dumb
+ * device as a "member" of our cluster...
+ *
+ * We ignore packets we're given to write that aren't "status" packets.
+ *
+ */
+
+static int
+ping_write(struct hb_media* mp, void *p, int len)
+{
+       struct ping_private *   ei;
+       int                     rc;
+       char*                   pkt;
+       union{
+               char*                   buf;
+               struct icmp6_hdr        ipkt;
+       }*icmp_pkt;
+       size_t                  size;
+       struct icmp6_hdr *      icp;
+       size_t                  pktsize;
+       const char *            type;
+       const char *            ts;
+       struct ha_msg *         nmsg;
+       struct ha_msg *         msg;
+       static gboolean         needroot = FALSE;
+       
+       
+       msg = wirefmt2msg(p, len, MSG_NEEDAUTH);
+       if( !msg){
+               PILCallLog(LOG, PIL_CRIT, "ping_write(): cannot convert wirefmt 
to msg");
+               return(HA_FAIL);
+       }
+       
+       PINGASSERT(mp);
+       ei = (struct ping_private *) mp->pd;
+       type = ha_msg_value(msg, F_TYPE);
+       
+       if (type == NULL || strcmp(type, T_STATUS) != 0 
+       || ((ts = ha_msg_value(msg, F_TIME)) == NULL)) {
+               ha_msg_del(msg);
+               return HA_OK;
+       }
+
+       /*
+        * We populate the following fields in the packet we create:
+        *
+        * F_TYPE:      T_NS_STATUS
+        * F_STATUS:    ping
+        * F_COMMENT:   ping
+        * F_ORIG:      destination name
+        * F_TIME:      local timestamp (from "msg")
+        * F_AUTH:      added by add_msg_auth()
+        */
+       if ((nmsg = ha_msg_new(5)) == NULL) {
+               PILCallLog(LOG, PIL_CRIT, "cannot create new message");
+               ha_msg_del(msg);
+               return(HA_FAIL);
+       }
+
+       if (ha_msg_add(nmsg, F_TYPE, T_NS_STATUS) != HA_OK
+       ||      ha_msg_add(nmsg, F_STATUS, PINGSTATUS) != HA_OK
+       ||      ha_msg_add(nmsg, F_COMMENT, PIL_PLUGIN_S) != HA_OK
+       ||      ha_msg_add(nmsg, F_ORIG, mp->name) != HA_OK
+       ||      ha_msg_add(nmsg, F_TIME, ts) != HA_OK) {
+               ha_msg_del(nmsg); nmsg = NULL;
+               PILCallLog(LOG, PIL_CRIT, "cannot add fields to message");
+               ha_msg_del(msg);
+               return HA_FAIL;
+       }
+
+       if (add_msg_auth(nmsg) != HA_OK) {
+               PILCallLog(LOG, PIL_CRIT, "cannot add auth field to message");
+               ha_msg_del(nmsg); nmsg = NULL;
+               ha_msg_del(msg);
+               return HA_FAIL;
+       }
+       
+       if ((pkt = msg2wirefmt(nmsg, &size)) == NULL)  {
+               PILCallLog(LOG, PIL_CRIT, "cannot convert message to string");
+               ha_msg_del(msg);
+               return HA_FAIL;
+       }
+       ha_msg_del(nmsg); nmsg = NULL;
+
+
+       pktsize = size + ICMP6_HDR_SZ;
+
+       if ((icmp_pkt = MALLOC(pktsize)) == NULL) {
+               PILCallLog(LOG, PIL_CRIT, "out of memory");
+               cl_free(pkt);
+               ha_msg_del(msg);
+               return HA_FAIL;
+       }
+       
+       icp = &(icmp_pkt->ipkt);
+       icp->icmp6_type = ICMP6_ECHO_REQUEST;
+       icp->icmp6_code = 0;
+       icp->icmp6_cksum = 0;
+       icp->icmp6_seq = htons(ei->iseq);
+       icp->icmp6_id = ei->ident;      /* Only used by us */
+       ++ei->iseq;
+
+       memcpy((char *)icmp_pkt + ICMP6_HDR_SZ, pkt, size);
+       cl_free(pkt); pkt = NULL;
+
+retry:
+       if (needroot) {
+               return_to_orig_privs();
+       }
+
+       if ((rc=sendto(ei->sock, (void *) icmp_pkt, pktsize, MSG_DONTWAIT
+       ,       (struct sockaddr *)&ei->addr
+       ,       sizeof(struct sockaddr_in6))) != (ssize_t)pktsize) {
+               if (errno == EPERM && !needroot) {
+                       needroot=TRUE;
+                       goto retry;
+               }
+               if (!mp->suppresserrs) {
+                       PILCallLog(LOG, PIL_CRIT, "Error sending packet: %s", 
strerror(errno));
+                       PILCallLog(LOG, PIL_INFO, "euid=%lu egid=%lu"
+                       ,       (unsigned long) geteuid()
+                       ,       (unsigned long) getegid());
+               }
+               FREE(icmp_pkt);
+               ha_msg_del(msg);
+               return(HA_FAIL);
+       }
+       if (needroot) {
+               return_to_dropped_privs();
+       }
+
+       if (DEBUGPKT) {
+               PILCallLog(LOG, PIL_DEBUG, "sent %d bytes to %s"
+                          ,    rc, inet_satop(&ei->addr));
+       }
+       if (DEBUGPKTCONT) {
+               PILCallLog(LOG, PIL_DEBUG, "ping pkt: %s"
+               ,       (char *)icmp_pkt + ICMP6_HDR_SZ);
+       }
+       FREE(icmp_pkt);
+       ha_msg_del(msg);
+       return HA_OK;
+
+
+  
+}
+
+/*
+ *     Open ping socket.
+ */
+static int
+ping_open(struct hb_media* mp)
+{
+       struct ping_private * ei;
+       int sockfd;
+       struct protoent *proto;
+
+       PINGASSERT(mp);
+       ei = (struct ping_private *) mp->pd;
+
+
+       if ((proto = getprotobyname("ipv6-icmp")) == NULL) {
+               PILCallLog(LOG, PIL_CRIT, "protocol IPv6-ICMP is unknown: %s", 
strerror(errno));
+               return HA_FAIL;
+       }
+       if ((sockfd = socket(AF_INET6, SOCK_RAW, proto->p_proto)) < 0) {
+               PILCallLog(LOG, PIL_CRIT, "Can't open RAW socket.: %s", 
strerror(errno));
+               return HA_FAIL;
+       }
+
+       if (fcntl(sockfd, F_SETFD, FD_CLOEXEC)) {
+               PILCallLog(LOG, PIL_CRIT, "Error setting the close-on-exec 
flag: %s"
+               ,       strerror(errno));
+       }
+       ei->sock = sockfd;
+
+       PILCallLog(LOG, PIL_INFO, "ping heartbeat started.");
+       return HA_OK;
+}
+
_______________________________________________________
Linux-HA-Dev: [email protected]
http://lists.linux-ha.org/mailman/listinfo/linux-ha-dev
Home Page: http://linux-ha.org/

Reply via email to