mirror of https://github.com/sipwise/heartbeat.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
796 lines
18 KiB
796 lines
18 KiB
/*
|
|
* ping_group.c: ICMP-echo-based heartbeat code for heartbeat.
|
|
*
|
|
* This allows a group of nodes to be pinged. The group is
|
|
* considered to be available if any of the nodes are available.
|
|
*
|
|
* Copyright (C) 2003 Horms <horms@verge.net.au>
|
|
*
|
|
* Based heavily on ping.c
|
|
* Copyright (C) 2000 Alan Robertson <alanr@unix.sh>
|
|
*
|
|
* The checksum code in this file code was borrowed from the ping program.
|
|
*
|
|
* 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 <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/param.h>
|
|
|
|
#ifdef HAVE_NETINET_IN_SYSTM_H
|
|
# include <netinet/in_systm.h>
|
|
#endif /* HAVE_NETINET_IN_SYSTM_H */
|
|
|
|
#ifdef HAVE_NETINET_IP_H
|
|
# include <netinet/ip.h>
|
|
#endif /* HAVE_NETINET_IP_H */
|
|
|
|
#include <netinet/ip_icmp.h>
|
|
|
|
#ifdef HAVE_NETINET_IP_H
|
|
# include <netinet/ip.h>
|
|
#endif /* HAVE_NETINET_IP_H */
|
|
|
|
#ifdef HAVE_NETINET_IP_VAR_H
|
|
# include <netinet/ip_var.h>
|
|
#endif /* HAVE_NETINET_IP_VAR_H */
|
|
|
|
#ifdef HAVE_NETINET_IP_COMPAT_H
|
|
# include <netinet/ip_compat.h>
|
|
#endif /* HAVE_NETINET_IP_COMPAT_H */
|
|
|
|
#ifdef HAVE_NETINET_IP_FW_H
|
|
# include <netinet/ip_fw.h>
|
|
#endif /* HAVE_NETINET_IP_FW_H */
|
|
|
|
#include <netdb.h>
|
|
#include <heartbeat.h>
|
|
#include <HBcomm.h>
|
|
#include <clplumbing/realtime.h>
|
|
#include <clplumbing/uids.h>
|
|
|
|
#ifdef linux
|
|
# define ICMP_HDR_SZ sizeof(struct icmphdr) /* 8 */
|
|
#else
|
|
# define ICMP_HDR_SZ 8
|
|
#endif
|
|
|
|
#define PIL_PLUGINTYPE HB_COMM_TYPE
|
|
#define PIL_PLUGINTYPE_S HB_COMM_TYPE_S
|
|
#define PIL_PLUGIN ping_group
|
|
#define PIL_PLUGIN_S "ping_group"
|
|
#define PIL_PLUGINLICENSE LICENSE_LGPL
|
|
#define PIL_PLUGINLICENSEURL URL_LGPL
|
|
#include <pils/plugin.h>
|
|
|
|
|
|
#define NSLOT 128 /* How old ping sequence
|
|
numbers can be to still
|
|
count */
|
|
|
|
typedef struct ping_group_node ping_group_node_t;
|
|
|
|
struct ping_group_node {
|
|
struct sockaddr_in addr; /* ping addr */
|
|
ping_group_node_t *next;
|
|
};
|
|
|
|
typedef struct {
|
|
int ident; /* heartbeat pid */
|
|
int sock; /* ping socket */
|
|
ping_group_node_t *node;
|
|
size_t nnode;
|
|
int slot[NSLOT];
|
|
int iseq; /* sequence number */
|
|
} ping_group_private_t;
|
|
|
|
|
|
static int ping_group_parse(const char *line);
|
|
static int ping_group_open (struct hb_media* mp);
|
|
static int ping_group_close (struct hb_media* mp);
|
|
static void* ping_group_read (struct hb_media* mp, int* lenp);
|
|
static int ping_group_write (struct hb_media* mp
|
|
,void* msg, int len);
|
|
|
|
static struct hb_media * ping_group_new(const char *name);
|
|
static int in_cksum (u_short * buf, size_t nbytes);
|
|
|
|
static int ping_group_mtype(char **buffer);
|
|
static int ping_group_descr(char **buffer);
|
|
static int ping_group_isping(void);
|
|
|
|
|
|
#define ISPINGGROUPOBJECT(mp) \
|
|
((mp) && ((mp)->vf == (void*)&ping_group_ops))
|
|
#define PINGGROUPASSERT(mp) g_assert(ISPINGGROUPOBJECT(mp))
|
|
|
|
static struct hb_media_fns ping_group_ops ={
|
|
NULL, /* Create single object function */
|
|
ping_group_parse, /* whole-line parse function */
|
|
ping_group_open,
|
|
ping_group_close,
|
|
ping_group_read,
|
|
ping_group_write,
|
|
ping_group_mtype,
|
|
ping_group_descr,
|
|
ping_group_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
|
|
|
|
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
|
|
, &ping_group_ops
|
|
, NULL /*close */
|
|
, &OurInterface
|
|
, (void*)&OurImports
|
|
, interfprivate);
|
|
}
|
|
static int
|
|
ping_group_mtype(char **buffer) {
|
|
|
|
*buffer = STRDUP(PIL_PLUGIN_S);
|
|
if (!*buffer) {
|
|
return 0;
|
|
}
|
|
|
|
return strlen(*buffer);
|
|
}
|
|
|
|
static int
|
|
ping_group_descr(char **buffer) {
|
|
*buffer = STRDUP("ping group membership");
|
|
if (!*buffer) {
|
|
return 0;
|
|
}
|
|
|
|
return strlen(*buffer);
|
|
}
|
|
|
|
/* Yes, a ping device */
|
|
|
|
static int
|
|
ping_group_isping(void) {
|
|
return 1;
|
|
}
|
|
|
|
|
|
static ping_group_node_t *
|
|
new_ping_group_node(const char *host)
|
|
{
|
|
ping_group_node_t* node;
|
|
|
|
node = (ping_group_node_t*)MALLOC(sizeof(ping_group_node_t));
|
|
if(!node) {
|
|
return(NULL);
|
|
}
|
|
memset(node, 0, sizeof(ping_group_node_t));
|
|
|
|
#ifdef HAVE_SOCKADDR_IN_SIN_LEN
|
|
node->addr.sin_len = sizeof(struct sockaddr_in);
|
|
#endif
|
|
node->addr.sin_family = AF_INET;
|
|
|
|
if (inet_pton(AF_INET, host, (void *)&node->addr.sin_addr) <= 0) {
|
|
struct hostent *hp;
|
|
hp = gethostbyname(host);
|
|
if (hp == NULL) {
|
|
PILCallLog(LOG, PIL_CRIT, "unknown host: %s: %s"
|
|
, host, strerror(errno));
|
|
FREE(node);
|
|
return NULL;
|
|
}
|
|
node->addr.sin_family = hp->h_addrtype;
|
|
memcpy(&node->addr.sin_addr, hp->h_addr, hp->h_length);
|
|
}
|
|
|
|
return(node);
|
|
}
|
|
|
|
static int
|
|
ping_group_add_node(struct hb_media* media, const char *host)
|
|
{
|
|
ping_group_private_t *priv;
|
|
ping_group_node_t *node;
|
|
|
|
PINGGROUPASSERT(media);
|
|
priv = (ping_group_private_t *)media->pd;
|
|
|
|
node = new_ping_group_node(host);
|
|
if(!node) {
|
|
return(HA_FAIL);
|
|
}
|
|
|
|
node->next = priv->node;
|
|
priv->node = node;
|
|
priv->nnode++;
|
|
|
|
return(HA_OK);
|
|
}
|
|
|
|
/*
|
|
* Create new ping heartbeat object
|
|
* Name of host is passed as a parameter
|
|
*/
|
|
static struct hb_media *
|
|
ping_group_new(const char *name)
|
|
{
|
|
ping_group_private_t* priv;
|
|
struct hb_media * media;
|
|
char * tmp;
|
|
|
|
priv = (ping_group_private_t*)MALLOC(sizeof(ping_group_private_t));
|
|
if(!priv) {
|
|
return(NULL);
|
|
}
|
|
memset(priv, 0, sizeof(ping_group_private_t));
|
|
|
|
priv->ident = getpid() & 0xFFFF;
|
|
|
|
media = (struct hb_media *) MALLOC(sizeof(struct hb_media));
|
|
if (!media) {
|
|
FREE(priv);
|
|
return(NULL);
|
|
}
|
|
memset(media, 0, sizeof(*media));
|
|
|
|
media->pd = (void*)priv;
|
|
tmp = STRDUP(name);
|
|
if(!tmp) {
|
|
FREE(priv);
|
|
FREE(media);
|
|
return(NULL);
|
|
}
|
|
|
|
media->name = tmp;
|
|
add_node(tmp, PINGNODE_I);
|
|
|
|
/* Fake it so that PINGGROUPASSERT() will work
|
|
* before the media is registered */
|
|
media->vf = (void*)&ping_group_ops;
|
|
|
|
return(media);
|
|
}
|
|
|
|
static void
|
|
ping_group_destroy_data(struct hb_media* media)
|
|
{
|
|
ping_group_private_t* priv;
|
|
ping_group_node_t * node;
|
|
|
|
PINGGROUPASSERT(media);
|
|
priv = (ping_group_private_t *)media->pd;
|
|
|
|
while(priv->node) {
|
|
node = priv->node;
|
|
priv->node = node->next;
|
|
FREE(node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ping_group_destroy(struct hb_media* media)
|
|
{
|
|
ping_group_private_t* priv;
|
|
|
|
PINGGROUPASSERT(media);
|
|
priv = (ping_group_private_t *)media->pd;
|
|
|
|
ping_group_destroy_data(media);
|
|
|
|
FREE(priv);
|
|
media->pd = NULL;
|
|
|
|
/* XXX: How can we free this? Should media->name really be const?
|
|
* And on the same topic, how are media unregistered / freed ? */
|
|
/*
|
|
tmp = (char *)media->name;
|
|
FREE(tmp);
|
|
media->name = NULL;
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* Close UDP/IP broadcast heartbeat interface
|
|
*/
|
|
|
|
static int
|
|
ping_group_close(struct hb_media* mp)
|
|
{
|
|
ping_group_private_t * ei;
|
|
int rc = HA_OK;
|
|
|
|
PINGGROUPASSERT(mp);
|
|
ei = (ping_group_private_t *) mp->pd;
|
|
|
|
if (ei->sock >= 0) {
|
|
if (close(ei->sock) < 0) {
|
|
rc = HA_FAIL;
|
|
}
|
|
ei->sock=-1;
|
|
}
|
|
|
|
ping_group_destroy_data(mp);
|
|
return(rc);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Receive a heartbeat ping reply packet.
|
|
*/
|
|
|
|
char pinggroup_pkt[MAXLINE];
|
|
static void *
|
|
ping_group_read(struct hb_media* mp, int *lenp)
|
|
{
|
|
|
|
ping_group_private_t * ei;
|
|
union {
|
|
char cbuf[MAXLINE+ICMP_HDR_SZ];
|
|
struct ip ip;
|
|
}buf;
|
|
const char * bufmax = ((char *)&buf)+sizeof(buf);
|
|
char * msgstart;
|
|
socklen_t addr_len = sizeof(struct sockaddr);
|
|
struct sockaddr_in their_addr; /* connector's addr information */
|
|
struct ip * ip;
|
|
struct icmp icp;
|
|
int numbytes;
|
|
int hlen;
|
|
int seq;
|
|
size_t slotn;
|
|
ping_group_node_t *node;
|
|
struct ha_msg *msg = NULL;
|
|
const char *comment;
|
|
int pktlen;
|
|
|
|
PINGGROUPASSERT(mp);
|
|
ei = (ping_group_private_t *) mp->pd;
|
|
|
|
ReRead: /* We recv lots of packets that aren't ours */
|
|
|
|
*lenp = 0;
|
|
if ((numbytes=recvfrom(ei->sock, (void *) &buf.cbuf
|
|
, sizeof(buf.cbuf)-1, 0, (struct sockaddr *)&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;
|
|
|
|
/* Check the IP header */
|
|
ip = &buf.ip;
|
|
hlen = ip->ip_hl * 4;
|
|
|
|
if (numbytes < hlen + ICMP_MINLEN) {
|
|
PILCallLog(LOG, PIL_WARN, "ping packet too short (%d bytes) from %s"
|
|
, numbytes
|
|
, inet_ntoa(*(struct in_addr *)
|
|
& their_addr.sin_addr.s_addr));
|
|
return(NULL);
|
|
}
|
|
|
|
/* Now the ICMP part */ /* (there may be a better way...) */
|
|
memcpy(&icp, (buf.cbuf + hlen), sizeof(icp));
|
|
|
|
if (icp.icmp_type != ICMP_ECHOREPLY || icp.icmp_id != ei->ident) {
|
|
goto ReRead; /* Not ours */
|
|
}
|
|
seq = ntohs(icp.icmp_seq);
|
|
|
|
if (DEBUGPKT) {
|
|
PILCallLog(LOG, PIL_DEBUG, "got %d byte packet from %s"
|
|
, numbytes, inet_ntoa(their_addr.sin_addr));
|
|
}
|
|
msgstart = (buf.cbuf + hlen + ICMP_HDR_SZ);
|
|
|
|
if (DEBUGPKTCONT && numbytes > 0) {
|
|
PILCallLog(LOG, PIL_DEBUG, "%s", msgstart);
|
|
}
|
|
|
|
for(node = ei->node; node; node = node->next) {
|
|
if(!memcmp(&(their_addr.sin_addr), &(node->addr.sin_addr)
|
|
, sizeof(struct in_addr))) {
|
|
break;
|
|
}
|
|
}
|
|
if(!node) {
|
|
goto ReRead; /* Not ours */
|
|
}
|
|
|
|
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)) {
|
|
ha_msg_del(msg);
|
|
errno = EINVAL;
|
|
return(NULL);
|
|
}
|
|
|
|
slotn = seq % NSLOT;
|
|
if(ei->slot[slotn] == seq) {
|
|
/* Duplicate within window */
|
|
ha_msg_del(msg);
|
|
goto ReRead; /* Not ours */
|
|
}
|
|
|
|
ei->slot[slotn] = seq;
|
|
|
|
pktlen = numbytes - hlen - ICMP_HDR_SZ;
|
|
pinggroup_pkt[pktlen] = 0;
|
|
|
|
memcpy(pinggroup_pkt, buf.cbuf + hlen + ICMP_HDR_SZ, pktlen);
|
|
*lenp = pktlen + 1;
|
|
|
|
ha_msg_del(msg);
|
|
return(pinggroup_pkt);
|
|
}
|
|
|
|
/*
|
|
* Send a heartbeat packet over broadcast UDP/IP interface
|
|
*
|
|
* 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_group_write(struct hb_media* mp, void *p, int len)
|
|
{
|
|
|
|
ping_group_private_t * ei;
|
|
int rc;
|
|
char* pkt;
|
|
union{
|
|
char* buf;
|
|
struct icmp ipkt;
|
|
}*icmp_pkt;
|
|
size_t size;
|
|
struct icmp * icp;
|
|
size_t pktsize;
|
|
const char * type;
|
|
const char * ts;
|
|
struct ha_msg * nmsg;
|
|
ping_group_node_t * node;
|
|
struct ha_msg* msg;
|
|
static gboolean needroot=FALSE;
|
|
|
|
PINGGROUPASSERT(mp);
|
|
|
|
|
|
if ((msg = wirefmt2msg(p, len, MSG_NEEDAUTH)) == NULL) {
|
|
PILCallLog(LOG, PIL_CRIT, "ping_write(): cannot convert wirefmt to msg");
|
|
return(HA_FAIL);
|
|
}
|
|
|
|
|
|
|
|
ei = (ping_group_private_t *) 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_group
|
|
* 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 + ICMP_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->icmp_type = ICMP_ECHO;
|
|
icp->icmp_code = 0;
|
|
icp->icmp_cksum = 0;
|
|
icp->icmp_seq = htons(ei->iseq);
|
|
icp->icmp_id = ei->ident; /* Only used by us */
|
|
++ei->iseq;
|
|
|
|
memcpy(icp->icmp_data, pkt, size);
|
|
cl_free(pkt); pkt = NULL;
|
|
|
|
/* Compute the ICMP checksum */
|
|
icp->icmp_cksum = in_cksum((u_short *)icp, pktsize);
|
|
|
|
retry:
|
|
if (needroot) {
|
|
return_to_orig_privs();
|
|
}
|
|
|
|
for(node = ei->node; node; node = node->next) {
|
|
if ((rc=sendto(ei->sock, (void *) icmp_pkt, pktsize
|
|
, MSG_DONTWAIT
|
|
, (struct sockaddr *)&node->addr
|
|
, sizeof(struct sockaddr))) != (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 (DEBUGPKT) {
|
|
PILCallLog(LOG, PIL_DEBUG, "sent %d bytes to %s"
|
|
, rc, inet_ntoa(node->addr.sin_addr));
|
|
}
|
|
|
|
cl_shortsleep();
|
|
}
|
|
if (needroot) {
|
|
return_to_dropped_privs();
|
|
}
|
|
|
|
if (DEBUGPKTCONT) {
|
|
PILCallLog(LOG, PIL_DEBUG, "%s"
|
|
, (const char*)icp->icmp_data);
|
|
}
|
|
|
|
FREE(icmp_pkt);
|
|
ha_msg_del(msg);
|
|
return HA_OK;
|
|
}
|
|
|
|
/*
|
|
* Open ping socket.
|
|
*/
|
|
|
|
static int
|
|
ping_group_open(struct hb_media* mp)
|
|
{
|
|
ping_group_private_t * ei;
|
|
int sockfd;
|
|
struct protoent *proto;
|
|
|
|
PINGGROUPASSERT(mp);
|
|
ei = (ping_group_private_t *) mp->pd;
|
|
|
|
|
|
if ((proto = getprotobyname("icmp")) == NULL) {
|
|
PILCallLog(LOG, PIL_CRIT, "protocol ICMP is unknown: %s", strerror(errno));
|
|
return HA_FAIL;
|
|
}
|
|
if ((sockfd = socket(AF_INET, 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 group heartbeat started.");
|
|
return HA_OK;
|
|
}
|
|
|
|
/*
|
|
* in_cksum --
|
|
* Checksum routine for Internet Protocol family headers (C Version)
|
|
* This function taken from Mike Muuss' ping program.
|
|
*/
|
|
static int
|
|
in_cksum (u_short *addr, size_t len)
|
|
{
|
|
size_t nleft = len;
|
|
u_short * w = addr;
|
|
int sum = 0;
|
|
u_short answer = 0;
|
|
|
|
/*
|
|
* The IP checksum algorithm is simple: using a 32 bit accumulator (sum)
|
|
* add sequential 16 bit words to it, and at the end, folding back all
|
|
* the carry bits from the top 16 bits into the lower 16 bits.
|
|
*/
|
|
while (nleft > 1) {
|
|
sum += *w++;
|
|
nleft -= 2;
|
|
}
|
|
|
|
/* Mop up an odd byte, if necessary */
|
|
if (nleft == 1) {
|
|
sum += *(u_char*)w;
|
|
}
|
|
|
|
/* Add back carry bits from top 16 bits to low 16 bits */
|
|
|
|
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
|
|
sum += (sum >> 16); /* add carry */
|
|
answer = ~sum; /* truncate to 16 bits */
|
|
|
|
return answer;
|
|
}
|
|
|
|
|
|
/* mcast_parse will parse the line in the config file that is
|
|
* associated with the media's type (hb_dev_mtype). It should
|
|
* receive the rest of the line after the mtype. And it needs
|
|
* to call hb_dev_new, add the media to the list of available media.
|
|
*
|
|
* So in this case, the config file line should look like
|
|
* mcast [device] [mcast group] [port] [mcast ttl] [mcast loop]
|
|
* for example:
|
|
* mcast eth0 225.0.0.1 694 1 0
|
|
*/
|
|
|
|
static int
|
|
ping_group_parse(const char *line)
|
|
{
|
|
char tmp[MAXLINE];
|
|
size_t len;
|
|
size_t nhost = 0;
|
|
struct hb_media *media;
|
|
|
|
/* Skip over white space, then grab the name */
|
|
line += strspn(line, WHITESPACE);
|
|
len = strcspn(line, WHITESPACE);
|
|
strncpy(tmp, line, len);
|
|
line += len;
|
|
*(tmp+len) = EOS;
|
|
|
|
if(*tmp == EOS) {
|
|
return(HA_FAIL);
|
|
}
|
|
|
|
media = ping_group_new(tmp);
|
|
if (!media) {
|
|
return(HA_FAIL);
|
|
}
|
|
|
|
while(1) {
|
|
/* Skip over white space, then grab the host */
|
|
line += strspn(line, WHITESPACE);
|
|
len = strcspn(line, WHITESPACE);
|
|
strncpy(tmp, line, len);
|
|
line += len;
|
|
*(tmp+len) = EOS;
|
|
|
|
if(*tmp == EOS) {
|
|
break;
|
|
}
|
|
|
|
if(ping_group_add_node(media, tmp) < 0) {
|
|
ping_group_destroy(media);
|
|
return(HA_FAIL);
|
|
}
|
|
nhost++;
|
|
}
|
|
|
|
if(nhost == 0) {
|
|
ping_group_destroy(media);
|
|
return(HA_FAIL);
|
|
}
|
|
|
|
OurImports->RegisterNewMedium(media);
|
|
|
|
return(HA_OK);
|
|
}
|
|
|