
dejanmm at fastmail
Sep 24, 2008, 7:30 AM
Post #2 of 5
(1001 views)
Permalink
|
|
Re: [Patch] IPv6 ping module for heartbeat
[In reply to]
|
|
Hi, On Tue, Sep 23, 2008 at 01:43:34PM -0700, Vince Busam wrote: > This patch adds a 'ping6' plugin that does the same as 'ping', only via > IPv6. Great! If you did this on company time, we'd need a confirmation from your boss that they are OK with the contribution. Then I'll review the code. Oh, can you please send the patch as an attachment. Cheers, Dejan > 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 <alanr [at] unix> > + * > + * 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: Linux-HA-Dev [at] lists > http://lists.linux-ha.org/mailman/listinfo/linux-ha-dev > Home Page: http://linux-ha.org/ _______________________________________________________ Linux-HA-Dev: Linux-HA-Dev [at] lists http://lists.linux-ha.org/mailman/listinfo/linux-ha-dev Home Page: http://linux-ha.org/
|