diff -urN Heartbeat-STABLE-2-1-STABLE-2.1.4/configure.in hb-ipv6/configure.in --- Heartbeat-STABLE-2-1-STABLE-2.1.4/configure.in 2008-08-18 21:32:19.000000000 +0900 +++ hb-ipv6/configure.in 2009-09-29 11:49:09.000000000 +0900 @@ -81,6 +81,7 @@ *linux*) USE_MODULES=1 REBOOT_OPTIONS="-nf" POWEROFF_OPTIONS="-nf" + AC_DEFINE_UNQUOTED(ON_LINUX, 1, Compiling for Linux platform) ;; dnl anything? darwin*) @@ -2886,6 +2887,7 @@ dnl The Makefiles and shell scripts we output AC_CONFIG_FILES(heartbeat.spec \ +hb-ipv6.spec \ Makefile \ README \ logd/Makefile \ diff -urN Heartbeat-STABLE-2-1-STABLE-2.1.4/hb-ipv6.spec.in hb-ipv6/hb-ipv6.spec.in --- Heartbeat-STABLE-2-1-STABLE-2.1.4/hb-ipv6.spec.in 1970-01-01 09:00:00.000000000 +0900 +++ hb-ipv6/hb-ipv6.spec.in 2009-09-29 11:52:04.000000000 +0900 @@ -0,0 +1,124 @@ +######################################## +# Derived definitions +######################################## +%define name hb-ipv6 +%define version 1.00 +%define release 1.hb214 +%define prefix @libdir@/@PACKAGE@ +%define ORGARCH Heartbeat-STABLE-2-1-STABLE-2.1.4 +# +%define ocfdir @OCF_RA_DIR@/heartbeat +%define installdir %{prefix}/hb-extras +# +%define __check_files %{nil} +# +# +# +Summary: Heartbeat command corresponding to IPv6. +Name: %{name} +Version: %{version} +Release: %{release} +Group: Applications +Source: %{ORGARCH}.tar.gz +License: GPL/LGPL +Vendor: NIPPON TELEGRAPH AND TELEPHONE CORPORATION +BuildRoot: %{_tmppath}/%{name}-%{version} +BuildRequires: autoconf automake libtool +Requires: heartbeat = 2.1.4 +Patch: %{name}-%{version}-%{release}.patch + +######################################## +%description +######################################## +Heartbeat's tools and OCF Resource Agents corresponding to IPv6. + - pingd + - IPv6addr + +######################################## +%prep +######################################## +rm -rf $RPM_BUILD_ROOT +%setup -q -n %{ORGARCH} +%patch -p1 +pushd $RPM_BUILD_DIR/%{ORGARCH} +./ConfigureMe bootstrap +popd + +######################################## +%build +######################################## +pushd $RPM_BUILD_DIR/%{ORGARCH}/replace +make DESTDIR=$RPM_BUILD_ROOT +popd +pushd $RPM_BUILD_DIR/%{ORGARCH}/libltdl +make DESTDIR=$RPM_BUILD_ROOT +popd +pushd $RPM_BUILD_DIR/%{ORGARCH}/lib +make DESTDIR=$RPM_BUILD_ROOT +popd + +######################################## +%pre +######################################## +if [ ! -e %{prefix}/pingd.org -a ! -L %{prefix}/pingd ]; then + mv %{prefix}/pingd %{prefix}/pingd.org +fi + +if [ ! -e %{ocfdir}/IPv6addr.org -a ! -L %{ocfdir}/IPv6addr ]; then + mv %{ocfdir}/IPv6addr %{ocfdir}/IPv6addr.org +fi + +######################################## +%install +######################################## +pushd $RPM_BUILD_DIR/%{ORGARCH}/resources/OCF +make DESTDIR=$RPM_BUILD_ROOT install +popd +pushd $RPM_BUILD_DIR/%{ORGARCH}/tools +make DESTDIR=$RPM_BUILD_ROOT install +popd + +######################################## +%clean +######################################## +if [ -n "${RPM_BUILD_ROOT}" -a "${RPM_BUILD_ROOT}" != "/" ]; then + rm -rf $RPM_BUILD_ROOT +fi +rm -rf $RPM_BUILD_DIR/%{ORGARCH} + +######################################## +%post +######################################## +ln -fs %{installdir}/pingd %{prefix}/pingd +ln -fs %{installdir}/IPv6addr %{ocfdir}/IPv6addr + +######################################## +%preun +######################################## +true + +######################################## +%postun +######################################## +if [ ! -e %{installdir}/pingd ]; then + rm -f %{prefix}/pingd + if [ -e %{prefix}/pingd.org ]; then + mv %{prefix}/pingd.org %{prefix}/pingd + fi +fi + +if [ ! -e %{installdir}/IPv6addr ]; then + rm -f %{ocfdir}/IPv6addr + if [ -e %{ocfdir}/IPv6addr.org ]; then + mv %{ocfdir}/IPv6addr.org %{ocfdir}/IPv6addr + fi +fi + +######################################## +%files +######################################## +%defattr(-,root,root) +%dir %{installdir} +%defattr(755,root,root) +%{installdir}/pingd +%{installdir}/IPv6addr diff -urN Heartbeat-STABLE-2-1-STABLE-2.1.4/resources/OCF/IPv6addr.c hb-ipv6/resources/OCF/IPv6addr.c --- Heartbeat-STABLE-2-1-STABLE-2.1.4/resources/OCF/IPv6addr.c 2008-08-18 21:32:19.000000000 +0900 +++ hb-ipv6/resources/OCF/IPv6addr.c 2009-09-29 11:49:09.000000000 +0900 @@ -45,6 +45,8 @@ * * It should be passed by environment variant: * OCF_RESKEY_ipv6addr=3ffe:ffff:0:f101::3 + * OCF_RESKEY_cidr_netmask=64 + * OCF_RESKEY_nic=eth0 * */ @@ -145,11 +147,11 @@ unsigned int ifr6_ifindex; }; -static int start_addr6(struct in6_addr* addr6, int prefix_len); -static int stop_addr6(struct in6_addr* addr6, int prefix_len); -static int status_addr6(struct in6_addr* addr6, int prefix_len); +static int start_addr6(struct in6_addr* addr6, int prefix_len, char* prov_ifname); +static int stop_addr6(struct in6_addr* addr6, int prefix_len, char* prov_ifname); +static int status_addr6(struct in6_addr* addr6, int prefix_len, char* prov_ifname); static int monitor_addr6(struct in6_addr* addr6, int prefix_len); -static int advt_addr6(struct in6_addr* addr6, int prefix_len); +static int advt_addr6(struct in6_addr* addr6, int prefix_len, char* prov_ifname); static int meta_data_addr6(void); @@ -159,9 +161,9 @@ static void byebye(int nsig); static char* scan_if(struct in6_addr* addr_target, int* plen_target, - int use_mask); -static char* find_if(struct in6_addr* addr_target, int* plen_target); -static char* get_if(struct in6_addr* addr_target, int* plen_target); + int use_mask, char* prov_ifname); +static char* find_if(struct in6_addr* addr_target, int* plen_target, char* prov_ifname); +static char* get_if(struct in6_addr* addr_target, int* plen_target, char* prov_ifname); static int assign_addr6(struct in6_addr* addr6, int prefix_len, char* if_name); static int unassign_addr6(struct in6_addr* addr6, int prefix_len, char* if_name); int is_addr6_available(struct in6_addr* addr6); @@ -172,9 +174,11 @@ { char pid_file[256]; char* ipv6addr; + char* cidr_netmask; int ret; char* cp; - int prefix_len; + char* prov_ifname = NULL; + int prefix_len = -1; struct in6_addr addr6; /* Check the count of parameters first */ @@ -190,6 +194,7 @@ /* open system log */ cl_log_set_entity(APP_NAME); cl_log_set_facility(HA_LOG_FACILITY); + cl_log_set_uselogd(TRUE); /* the meta-data dont need any parameter */ if (0 == strncmp(META_DATA_CMD, argv[1], strlen(META_DATA_CMD))) { @@ -204,6 +209,8 @@ usage(argv[0]); return OCF_ERR_ARGS; } + + /* legacy option */ if ((cp = strchr(ipv6addr, '/'))) { prefix_len = atol(cp + 1); if ((prefix_len < 0) || (prefix_len > 128)) { @@ -212,10 +219,30 @@ return OCF_ERR_ARGS; } *cp=0; - } else { + } + + /* get provided netmask (optional) */ + cidr_netmask = getenv("OCF_RESKEY_cidr_netmask"); + if (cidr_netmask != NULL) { + if ((atol(cidr_netmask) < 0) || (atol(cidr_netmask) > 128)) { + cl_log(LOG_ERR, "Invalid prefix_len [%s], " + "should be an integer in [0, 128]", cidr_netmask); + usage(argv[0]); + return OCF_ERR_ARGS; + } + if (prefix_len != -1 && prefix_len != atol(cidr_netmask)) { + cl_log(LOG_DEBUG, "prefix_len(%d) is overwritted by cidr_netmask(%s)", + prefix_len, cidr_netmask); + } + prefix_len = atol(cidr_netmask); + + } else if (prefix_len == -1) { prefix_len = 0; } + /* get provided interface name (optional) */ + prov_ifname = getenv("OCF_RESKEY_nic"); + if (inet_pton(AF_INET6, ipv6addr, &addr6) <= 0) { cl_log(LOG_ERR, "Invalid IPv6 address [%s]", ipv6addr); usage(argv[0]); @@ -244,11 +271,11 @@ /* switch the command */ if (0 == strncmp(START_CMD,argv[1], strlen(START_CMD))) { - ret = start_addr6(&addr6, prefix_len); + ret = start_addr6(&addr6, prefix_len, prov_ifname); }else if (0 == strncmp(STOP_CMD,argv[1], strlen(STOP_CMD))) { - ret = stop_addr6(&addr6, prefix_len); + ret = stop_addr6(&addr6, prefix_len, prov_ifname); }else if (0 == strncmp(STATUS_CMD,argv[1], strlen(STATUS_CMD))) { - ret = status_addr6(&addr6, prefix_len); + ret = status_addr6(&addr6, prefix_len, prov_ifname); }else if (0 ==strncmp(MONITOR_CMD,argv[1], strlen(MONITOR_CMD))) { ret = monitor_addr6(&addr6, prefix_len); }else if (0 ==strncmp(RELOAD_CMD,argv[1], strlen(RELOAD_CMD))) { @@ -259,7 +286,7 @@ /* ipv6addr has been validated by inet_pton, hence a valid IPv6 address */ ret = OCF_SUCCESS; }else if (0 ==strncmp(ADVT_CMD,argv[1], strlen(MONITOR_CMD))) { - ret = advt_addr6(&addr6, prefix_len); + ret = advt_addr6(&addr6, prefix_len, prov_ifname); }else{ usage(argv[0]); ret = OCF_ERR_ARGS; @@ -268,19 +295,20 @@ /* release the pid file */ unlink(pid_file); + cl_log(LOG_DEBUG, "%s %s : %d", getenv("OCF_RESOURCE_INSTANCE"), argv[1], ret); return ret; } int -start_addr6(struct in6_addr* addr6, int prefix_len) +start_addr6(struct in6_addr* addr6, int prefix_len, char* prov_ifname) { int i; char* if_name; - if(OCF_SUCCESS == status_addr6(addr6,prefix_len)) { + if(OCF_SUCCESS == status_addr6(addr6,prefix_len,prov_ifname)) { return OCF_SUCCESS; } /* we need to find a proper device to assign the address */ - if_name = find_if(addr6, &prefix_len); + if_name = find_if(addr6, &prefix_len, prov_ifname); if (NULL == if_name) { cl_log(LOG_ERR, "no valid mecahnisms"); return OCF_ERR_GENERIC; @@ -313,10 +341,10 @@ } int -advt_addr6(struct in6_addr* addr6, int prefix_len) +advt_addr6(struct in6_addr* addr6, int prefix_len, char* prov_ifname) { /* First, we need to find a proper device to assign the address */ - char* if_name = get_if(addr6, &prefix_len); + char* if_name = get_if(addr6, &prefix_len, prov_ifname); int i; if (NULL == if_name) { cl_log(LOG_ERR, "no valid mecahnisms"); @@ -331,14 +359,14 @@ } int -stop_addr6(struct in6_addr* addr6, int prefix_len) +stop_addr6(struct in6_addr* addr6, int prefix_len, char* prov_ifname) { char* if_name; - if(OCF_NOT_RUNNING == status_addr6(addr6,prefix_len)) { + if(OCF_NOT_RUNNING == status_addr6(addr6,prefix_len,prov_ifname)) { return OCF_SUCCESS; } - if_name = get_if(addr6, &prefix_len); + if_name = get_if(addr6, &prefix_len, prov_ifname); if (NULL == if_name) { cl_log(LOG_ERR, "no valid mechanisms."); @@ -356,9 +384,9 @@ } int -status_addr6(struct in6_addr* addr6, int prefix_len) +status_addr6(struct in6_addr* addr6, int prefix_len, char* prov_ifname) { - char* if_name = get_if(addr6, &prefix_len); + char* if_name = get_if(addr6, &prefix_len, prov_ifname); if (NULL == if_name) { return OCF_NOT_RUNNING; } @@ -387,12 +415,21 @@ struct libnet_in6_addr dst_ip; struct libnet_ether_addr *mac_address; char payload[24]; + int ifindex; if ((l=libnet_init(LIBNET_RAW6, if_name, errbuf)) == NULL) { cl_log(LOG_ERR, "libnet_init failure on %s", if_name); goto err; } + /* set the outgoing interface */ + ifindex = if_nametoindex(if_name); + if (setsockopt(libnet_getfd(l), IPPROTO_IPV6, IPV6_MULTICAST_IF, + &ifindex, sizeof(ifindex)) < 0) { + cl_log(LOG_ERR, "setsockopt(IPV6_MULTICAST_IF): %s", + strerror(errno)); + goto err; + } mac_address = libnet_get_hwaddr(l); if (!mac_address) { @@ -414,6 +451,9 @@ libnet_build_ipv6(0,0,LIBNET_ICMPV6_H + sizeof(payload),IPPROTO_ICMP6, 255,*(struct libnet_in6_addr*)src_ip, dst_ip,NULL,0,l,0); + /* Hack: adjust the correct checksum offset. see LF #2034 */ + libnet_pblock_record_ip_offset(l, l->total_size); + if (libnet_write(l) == -1) { @@ -429,7 +469,7 @@ /* find the network interface associated with an address */ char* -scan_if(struct in6_addr* addr_target, int* plen_target, int use_mask) +scan_if(struct in6_addr* addr_target, int* plen_target, int use_mask, char* prov_ifname) { FILE *f; static char devname[21]=""; @@ -475,7 +515,15 @@ if (*plen_target!=0 && plen != *plen_target) { continue; } - *plen_target = plen; + + /* If interface name provided, only same devname entry + * would be considered + */ + if (prov_ifname!=0 && *prov_ifname!=0) + { + if (strcmp(devname, prov_ifname)) + continue; + } for (i = 0; i< 4; i++) { addr.s6_addr32[i] = htonl(addr6p[i]); @@ -487,7 +535,10 @@ n = plen / 32; memset(mask.s6_addr32 + n + 1, 0, (3 - n) * 4); s = 32 - plen % 32; - mask.s6_addr32[n] = 0xffffffff << s; + if (s == 32) + mask.s6_addr32[n] = 0x0; + else + mask.s6_addr32[n] = 0xffffffff << s; mask.s6_addr32[n] = htonl(mask.s6_addr32[n]); } @@ -504,6 +555,7 @@ /* We found it! */ if (same) { fclose(f); + *plen_target = plen; return devname; } } @@ -512,15 +564,15 @@ } /* find a proper network interface to assign the address */ char* -find_if(struct in6_addr* addr_target, int* plen_target) +find_if(struct in6_addr* addr_target, int* plen_target, char* prov_ifname) { - return scan_if(addr_target, plen_target, 1); + return scan_if(addr_target, plen_target, 1, prov_ifname); } /* get the device name and the plen_target of a special address */ char* -get_if(struct in6_addr* addr_target, int* plen_target) +get_if(struct in6_addr* addr_target, int* plen_target, char* prov_ifname) { - return scan_if(addr_target, plen_target, 0); + return scan_if(addr_target, plen_target, 0, prov_ifname); } int assign_addr6(struct in6_addr* addr6, int prefix_len, char* if_name) @@ -836,12 +888,29 @@ " IPv6 address\n" " \n" " \n" + " \n" + " \n" + " The netmask for the interface in CIDR format. (ie, 24).\n" + " The value of this parameter overwrites the value of _prefix_\n" + " of ipv6addr parameter.\n" + " \n" + " Netmask\n" + " \n" + " \n" + " \n" + " \n" + " The base network interface on which the IPv6 address will\n" + " be brought online.\n" + " \n" + " Network interface\n" + " \n" + " \n" " \n" " \n" " \n" " \n" - " \n" - " \n" + " \n" + " \n" " \n" " \n" " \n" diff -urN Heartbeat-STABLE-2-1-STABLE-2.1.4/resources/OCF/Makefile.am hb-ipv6/resources/OCF/Makefile.am --- Heartbeat-STABLE-2-1-STABLE-2.1.4/resources/OCF/Makefile.am 2008-08-18 21:32:19.000000000 +0900 +++ hb-ipv6/resources/OCF/Makefile.am 2009-09-29 11:49:09.000000000 +0900 @@ -24,15 +24,16 @@ dtddir = $(HA_NOARCHDATAHBDIR) ocfdir = @OCF_RA_DIR@/heartbeat +ocf2dir = $(libdir)/@HB_PKG@/hb-extras dtd_SCRIPTS = ra-api-1.dtd gliblib = @GLIBLIB@ if USE_IPV6ADDR -ocf_PROGRAMS = IPv6addr +ocf2_PROGRAMS = IPv6addr else -ocf_PROGRAMS = +ocf2_PROGRAMS = endif IPv6addr_SOURCES = IPv6addr.c diff -urN Heartbeat-STABLE-2-1-STABLE-2.1.4/resources/heartbeat/IPv6addr.in hb-ipv6/resources/heartbeat/IPv6addr.in --- Heartbeat-STABLE-2-1-STABLE-2.1.4/resources/heartbeat/IPv6addr.in 2008-08-18 21:32:19.000000000 +0900 +++ hb-ipv6/resources/heartbeat/IPv6addr.in 2009-09-29 11:49:09.000000000 +0900 @@ -25,5 +25,24 @@ OCF_RESOURCE_INSTANCE=${OCF_TYPE}_$1 export OCF_TYPE OCF_RESOURCE_INSTANCE -OCF_RESKEY_ipv6addr=$1; export OCF_RESKEY_ipv6addr +# We need to split the argument into pieces that IPv6addr OCF RA can +# recognize, sed is prefered over Bash specific builtin functions +# for portability. +BASEIP=`echo $1 | sed 's%/.*%%'` +OCF_RESKEY_ipv6addr=$BASEIP; export OCF_RESKEY_ipv6addr + +str=`echo $1 | sed 's%^'$BASEIP'*%%'` +if [ ! -z "$str" ]; then + NETMASK=`echo ${str#/} | sed 's%/.*%%'` + OCF_RESKEY_cidr_netmask=$NETMASK; export OCF_RESKEY_cidr_netmask + + str=`echo $str | sed 's%^/'$NETMASK'/*%%'` + NIC=`echo $str | sed 's%/.*%%'` + case $NIC in + "") ;; + *) OCF_RESKEY_nic=$NIC; export OCF_RESKEY_nic + ;; + esac +fi + ra_execocf $2 diff -urN Heartbeat-STABLE-2-1-STABLE-2.1.4/tools/Makefile.am hb-ipv6/tools/Makefile.am --- Heartbeat-STABLE-2-1-STABLE-2.1.4/tools/Makefile.am 2008-08-18 21:32:19.000000000 +0900 +++ hb-ipv6/tools/Makefile.am 2009-09-29 11:49:09.000000000 +0900 @@ -28,6 +28,7 @@ apigid = @HA_APIGID@ habindir = @bindir@ halibdir = $(libdir)/@HB_PKG@ +halib2dir = $(libdir)/@HB_PKG@/hb-extras hanoarchdir = @HA_NOARCHDATAHBDIR@ gliblib = @GLIBLIB@ @@ -37,7 +38,8 @@ sbin_SCRIPTS = ciblint hb_report ocf-tester if CRM_BUILD -halib_PROGRAMS = attrd pingd +halib_PROGRAMS = attrd +halib2_PROGRAMS = pingd sbin_PROGRAMS = attrd_updater endif diff -urN Heartbeat-STABLE-2-1-STABLE-2.1.4/tools/pingd.c hb-ipv6/tools/pingd.c --- Heartbeat-STABLE-2-1-STABLE-2.1.4/tools/pingd.c 2008-08-18 21:32:19.000000000 +0900 +++ hb-ipv6/tools/pingd.c 2009-09-29 11:49:09.000000000 +0900 @@ -19,63 +19,984 @@ #include -#include - -#include - #include -#include -#include #include - #include #include #include #include +#include +#include -#include -#include -#include -#include +#ifdef HAVE_SYS_SOCKET_H +# include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef ON_LINUX +#include +#include +# ifndef ICMP_FILTER +# define ICMP_FILTER 1 +struct icmp_filter { + uint32_t data; +}; +# endif +#endif +#include #include #include +#include #ifdef HAVE_GETOPT_H # include #endif +#define crm_perror(level, fmt, args...) do { \ + const char *err = strerror(errno); \ + do_crm_log(level, fmt ": %s (%d)", ##args, err, errno); \ + } while(0) + +#define OPTARGS "?VDp:N:a:d:S:s:i:n:t:m:Uh:" + /* GMainLoop *mainloop = NULL; */ -#define OPTARGS "V?p:a:d:s:S:h:Dm:" -IPC_Channel *attrd = NULL; +GListPtr ping_list = NULL; GMainLoop* mainloop = NULL; GHashTable *ping_nodes = NULL; const char *pingd_attr = "pingd"; gboolean do_filter = FALSE; -ll_cluster_t *pingd_cluster = NULL; gboolean need_shutdown = FALSE; +gboolean stand_alone = FALSE; +gboolean do_updates = TRUE; +ll_cluster_t *pingd_cluster = NULL; const char *attr_set = NULL; const char *attr_section = NULL; -const char *attr_dampen = NULL; +int attr_dampen = 5000; /* 5s */ int attr_multiplier = 1; +int pings_per_host = 2; +int ping_timeout = 2; +int re_ping_interval = 1000; /* 1s */ + +int ident; /* our pid */ + +unsigned char cmsgbuf[4096]; +int cmsglen = 0; + +typedef struct ping_node_s { + int fd; /* ping socket */ + uint16_t iseq; /* sequence number */ + gboolean type; + gboolean extra_filters; + union { + struct sockaddr raw; + struct sockaddr_in v4; /* ipv4 ping addr */ + struct sockaddr_in6 v6; /* ipv6 ping addr */ + } addr; + char dest[256]; + char *host; +} ping_node; void pingd_nstatus_callback( const char *node, const char *status, void *private_data); void pingd_lstatus_callback( const char *node, const char *link, const char *status, void *private_data); +void send_update(int active); +int process_icmp6_error(ping_node *node, struct sockaddr_in6 *whereto); +int process_icmp4_error(ping_node *node, struct sockaddr_in *whereto); void do_node_walk(ll_cluster_t *hb_cluster); -void send_update(void); + +/* + * 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; +} + +static const char *ping_desc(gboolean family, uint8_t type, uint8_t code) +{ + if(family == AF_INET6) { + switch(type) { + case ICMP6_DST_UNREACH: + switch(code) { + case ICMP6_DST_UNREACH_NOROUTE: + return "No Route to Destination"; + case ICMP6_DST_UNREACH_ADMIN: + return "Destination Administratively Unreachable"; + case ICMP6_DST_UNREACH_BEYONDSCOPE: + return "Destination Unreachable Beyond Scope"; + case ICMP6_DST_UNREACH_ADDR: + return "Destination Address Unreachable"; + case ICMP6_DST_UNREACH_NOPORT: + return "Destination Port Unreachable"; + default: + crm_err("Unreachable: Unkown subtype: %d", code); + return "Unreachable: Unkown Subtype"; + } + case ICMP6_PACKET_TOO_BIG: + return "Packet too big"; + case ICMP6_TIME_EXCEEDED: + switch(code) { + case ICMP6_TIME_EXCEED_TRANSIT: + return "Time to live exceeded"; + case ICMP6_TIME_EXCEED_REASSEMBLY: + return "Frag reassembly time exceeded"; + default: + crm_err("Timeout: Unkown subtype: %d", code); + return "Timeout: Unkown Subtype"; + } + case ICMP6_PARAM_PROB: + switch(code) { + case ICMP6_PARAMPROB_HEADER: + return "Parameter problem: Erroneous Header"; + case ICMP6_PARAMPROB_NEXTHEADER: + return "Parameter problem: Unknown Nextheader"; + case ICMP6_PARAMPROB_OPTION: + return "Parameter problem: Unrecognized Option"; + default: + crm_err("Invalid header: Unkown subtype: %d", code); + return "Invalid header: Unkown Subtype"; + } + case ICMP6_ECHO_REQUEST: + return "Echo Request"; + case ICMP6_ECHO_REPLY: + return "Echo Reply"; + case MLD_LISTENER_QUERY: + return "Multicast Listener Query"; + case MLD_LISTENER_REPORT: + return "Multicast Listener Report"; + case MLD_LISTENER_REDUCTION: + return "Multicast Listener Done"; + case ND_ROUTER_SOLICIT: + return "Router Solicitation"; + case ND_ROUTER_ADVERT: + return "Router Advertisement"; + case ND_NEIGHBOR_SOLICIT: + return "Neighbor Solicitation"; + case ND_NEIGHBOR_ADVERT: + return "Neighbor Advertisement"; + case ND_REDIRECT: + return "Redirect"; + case ICMP6_ROUTER_RENUMBERING: + return "Router renumbering"; + default: + crm_err("Unknown type: %d", type); + return "Unknown type"; + } + } else { + switch(type) { + case ICMP_ECHOREPLY: + return "Echo Reply"; + case ICMP_ECHO: + return "Echo Request"; + case ICMP_PARAMPROB: + return "Bad Parameter"; + case ICMP_SOURCEQUENCH: + return "Packet lost, slow down"; + case ICMP_TSTAMP: + return "Timestamp Request"; + case ICMP_TSTAMPREPLY: + return "Timestamp Reply"; + case ICMP_IREQ: + return "Information Request"; + case ICMP_IREQREPLY: + return "Information Reply"; + + case ICMP_UNREACH: + switch(code) { + case ICMP_UNREACH_NET: + return "Unreachable Network"; + case ICMP_UNREACH_HOST: + return "Unreachable Host"; + case ICMP_UNREACH_PROTOCOL: + return "Unreachable Protocol"; + case ICMP_UNREACH_PORT: + return "Unreachable Port"; + case ICMP_UNREACH_NEEDFRAG: + return "Unreachable: Fragmentation needed"; + case ICMP_UNREACH_SRCFAIL: + return "Unreachable Source Route"; + case ICMP_UNREACH_NET_UNKNOWN: + return "Unknown Network"; + case ICMP_UNREACH_HOST_UNKNOWN: + return "Unknown Host"; + case ICMP_UNREACH_ISOLATED: + return "Unreachable: Isolated"; + case ICMP_UNREACH_NET_PROHIB: + return "Prohibited network"; + case ICMP_UNREACH_HOST_PROHIB: + return "Prohibited host"; + case ICMP_UNREACH_FILTER_PROHIB: + return "Unreachable: Prohibited filter"; + case ICMP_UNREACH_TOSNET: + return "Unreachable: Type of Service and Network"; + case ICMP_UNREACH_TOSHOST: + return "Unreachable: Type of Service and Host"; + case ICMP_UNREACH_HOST_PRECEDENCE: + return "Unreachable: Prec vio"; + case ICMP_UNREACH_PRECEDENCE_CUTOFF: + return "Unreachable: Prec cutoff"; + default: + crm_err("Unreachable: Unknown subtype: %d", code); + return "Unreachable: Unknown Subtype"; + } + break; + + case ICMP_REDIRECT: + switch(code) { + case ICMP_REDIRECT_NET: + return "Redirect: Network"; + case ICMP_REDIRECT_HOST: + return "Redirect: Host"; + case ICMP_REDIRECT_TOSNET: + return "Redirect: Type of Service and Network"; + case ICMP_REDIRECT_TOSHOST: + return "Redirect: Type of Service and Host"; + default: + crm_err("Redirect: Unknown subtype: %d", code); + return "Redirect: Unknown Subtype"; + } + + case ICMP_TIMXCEED: + switch(code) { + case ICMP_TIMXCEED_INTRANS: + return "Timeout: TTL"; + case ICMP_TIMXCEED_REASS: + return "Timeout: Fragmentation reassembly"; + default: + crm_err("Timeout: Unkown subtype: %d", code); + return "Timeout: Unkown Subtype"; + } + break; + + default: + crm_err("Unknown type: %d", type); + return "Unknown type"; + } + } +} + +#ifdef ON_LINUX +# define MAX_HOST 1024 +int process_icmp6_error(ping_node *node, struct sockaddr_in6 *whereto) +{ + int rc = 0; + char buf[512]; + struct iovec iov; + struct msghdr msg; + struct icmp6_hdr icmph; + struct sockaddr_in6 target; + struct cmsghdr *cmsg = NULL; + struct sock_extended_err *s_err = NULL; + + iov.iov_base = &icmph; + iov.iov_len = sizeof(icmph); + msg.msg_name = (void*)⌖ + msg.msg_namelen = sizeof(target); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + + rc = recvmsg(node->fd, &msg, MSG_ERRQUEUE|MSG_DONTWAIT); + if (rc < 0 || rc < sizeof(icmph)) { + crm_perror(LOG_DEBUG, "No error message: %d", rc); + return 0; + } + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR) { + s_err = (struct sock_extended_err *)CMSG_DATA(cmsg); + } + } + + CRM_ASSERT(s_err != NULL); + + if (s_err->ee_origin == SO_EE_ORIGIN_LOCAL) { + if (s_err->ee_errno == EMSGSIZE) { + crm_info("local error: Message too long, mtu=%u", s_err->ee_info); + } else { + crm_info("local error: %s", strerror(s_err->ee_errno)); + } + return 0; + + } else if (s_err->ee_origin == SO_EE_ORIGIN_ICMP6) { + struct sockaddr_in6 *sin = (struct sockaddr_in6*)(s_err+1); + const char *ping_result = ping_desc(node->type, s_err->ee_type, s_err->ee_code); + static char target_s[64], whereto_s[64], ping_host_s[64]; + inet_ntop(AF_INET6, (struct in6_addr *)&(target.sin6_addr), target_s, sizeof(target_s)); + inet_ntop(AF_INET6, (struct in6_addr *)&(whereto->sin6_addr), whereto_s, sizeof(whereto_s)); + + if (ntohs(icmph.icmp6_id) != ident) { + /* Result was not for us */ + crm_debug("Not our error (ident): %d %d", ntohs(icmph.icmp6_id), ident); + return -1; + + } else if (memcmp(&target.sin6_addr, &whereto->sin6_addr, 16)) { + /* Result was not for us */ + crm_debug("Not our error (addr): %s %s", target_s, whereto_s); + return -1; + + } else if (icmph.icmp6_type != ICMP6_ECHO_REQUEST) { + /* Not an error */ + crm_info("Not an error: %d", icmph.icmp6_type); + return -1; + } + + inet_ntop(AF_INET6, (struct in6_addr *)&(sin->sin6_addr), ping_host_s, sizeof(ping_host_s)); + crm_debug("From %s icmp_seq=%u %s", ping_host_s, ntohs(icmph.icmp6_seq), ping_result); + + } else { + crm_debug("else: %d", s_err->ee_origin); + } + + return 0; +} + +int process_icmp4_error(ping_node *node, struct sockaddr_in *whereto) +{ + int rc = 0; + char buf[512]; + struct iovec iov; + struct msghdr msg; + struct icmphdr icmph; + struct sockaddr_in target; + struct cmsghdr *cmsg = NULL; + struct sock_extended_err *s_err = NULL; + + iov.iov_base = &icmph; + iov.iov_len = sizeof(icmph); + msg.msg_name = (void*)⌖ + msg.msg_namelen = sizeof(target); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + + rc = recvmsg(node->fd, &msg, MSG_ERRQUEUE|MSG_DONTWAIT); + if (rc < 0 || rc < sizeof(icmph)) { + crm_perror(LOG_DEBUG, "No error message: %d", rc); + return 0; + } + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) { + s_err = (struct sock_extended_err *)CMSG_DATA(cmsg); + } + } + + CRM_ASSERT(s_err != NULL); + + if (s_err->ee_origin == SO_EE_ORIGIN_LOCAL) { + if (s_err->ee_errno == EMSGSIZE) { + crm_info("local error: Message too long, mtu=%u", s_err->ee_info); + } else { + crm_info("local error: %s", strerror(s_err->ee_errno)); + } + return 0; + + } else if (s_err->ee_origin == SO_EE_ORIGIN_ICMP) { + char ping_host[MAX_HOST]; + struct sockaddr_in *sin = (struct sockaddr_in*)(s_err+1); + const char *ping_result = ping_desc(node->type, s_err->ee_type, s_err->ee_code); + char *target_s = inet_ntoa(*(struct in_addr *)&(target.sin_addr.s_addr)); + char *whereto_s = inet_ntoa(*(struct in_addr *)&(whereto->sin_addr.s_addr)); + + if (ntohs(icmph.un.echo.id) != ident) { + /* Result was not for us */ + crm_debug("Not our error (ident): %d %d", ntohs(icmph.un.echo.id), ident); + return -1; + + } else if (safe_str_neq(target_s, whereto_s)) { + /* Result was not for us */ + crm_debug("Not our error (addr): %s %s", target_s, whereto_s); + return -1; + + } else if (icmph.type != ICMP_ECHO) { + /* Not an error */ + crm_info("Not an error: %d", icmph.type); + return -1; + } + + /* snprintf(ping_host, MAX_HOST, "%s", inet_ntoa(*(struct in_addr *)&(sin->sin_addr.s_addr))); */ + snprintf(ping_host, MAX_HOST, "%s", inet_ntoa(sin->sin_addr)); + + if (node->extra_filters == FALSE) { + /* Now that we got some sort of reply, add extra filters to + * ensure we keep getting the _right_ replies for dead hosts + */ + struct icmp_filter filt; + crm_debug("Installing additional ICMP filters"); + node->extra_filters = TRUE; /* only try once */ + + filt.data = ~((1<fd, SOL_RAW, ICMP_FILTER, (char*)&filt, sizeof(filt)) == -1) { + crm_perror(LOG_WARNING, "setsockopt failed: Cannot install ICMP filters for %s", ping_host); + } + } + + crm_debug("From %s icmp_seq=%u %s", ping_host, ntohs(icmph.un.echo.sequence), ping_result); + + } else { + crm_debug("else: %d", s_err->ee_origin); + } + + return 0; +} +#else +int process_icmp6_error(ping_node *node, struct sockaddr_in6 *whereto) +{ + /* dummy function */ + return 0; +} + +int process_icmp4_error(ping_node *node, struct sockaddr_in *whereto) +{ + /* dummy function */ + return 0; +} +#endif + +static ping_node *ping_new(const char *host) +{ + ping_node *node; + + crm_malloc0(node, sizeof(ping_node)); + + if(strstr(host, ":")) { + node->type = AF_INET6; + } else { + node->type = AF_INET; + } + + node->host = crm_strdup(host); + + return node; +} + +static gboolean ping_open(ping_node *node) +{ + int ret_ga = 0; + char *hostname = NULL; + struct addrinfo *res = NULL; + struct addrinfo hints; + char *addr = NULL; + char *cp = NULL; + + /* getaddrinfo */ + bzero(&hints, sizeof(struct addrinfo)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = node->type; + hints.ai_socktype = SOCK_RAW; + + if(node->type == AF_INET6) { + hints.ai_protocol = IPPROTO_ICMPV6; + } else { + hints.ai_protocol = IPPROTO_ICMP; + } + + addr = crm_strdup(node->host); + if ((cp = strchr(addr, '%'))) { + *cp = 0; + } + crm_debug("node->host[%s], addr[%s]", node->host, addr); + ret_ga = getaddrinfo(addr, NULL, &hints, &res); + crm_free(addr); + if (ret_ga) { + crm_warn("getaddrinfo: %s", gai_strerror(ret_ga)); + goto bail; + } + + if (res->ai_canonname) { + hostname = res->ai_canonname; + } else { + hostname = node->host; + } + + crm_debug_2("Got address %s for %s", hostname, node->host); + + if(!res->ai_addr) { + crm_warn("getaddrinfo failed: no address"); + goto bail; + } + + memcpy(&(node->addr.raw), res->ai_addr, res->ai_addrlen); + node->fd = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol); + /* node->fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); */ + + if(node->fd < 0) { + crm_perror(LOG_WARNING, "Can't open socket to %s", hostname); + goto bail; + } + + if(node->type == AF_INET6) { + int sockopt; + + inet_ntop(node->type, &node->addr.v6.sin6_addr, node->dest, sizeof(node->dest)); + + /* set recv buf for broadcast pings */ + sockopt = 48 * 1024; + setsockopt(node->fd, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt, sizeof(sockopt)); + + } else { + inet_ntop(node->type, &node->addr.v4.sin_addr, node->dest, sizeof(node->dest)); + } + + if(ping_timeout > 0) { + struct timeval timeout_opt; + + timeout_opt.tv_sec = ping_timeout; + timeout_opt.tv_usec = 0; + + setsockopt(node->fd, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout_opt, sizeof(timeout_opt)); + } + +#ifdef ON_LINUX + { + int dummy = 1; + + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); + cmsglen = 0; + + if(node->type == AF_INET6) { + struct icmp6_filter filt; + + ICMP6_FILTER_SETBLOCKALL(&filt); + ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt); + + if (setsockopt(node->fd, IPPROTO_ICMPV6, ICMP6_FILTER, (char*)&filt, sizeof(filt)) == -1) { + crm_perror(LOG_WARNING, "setsockopt failed: Cannot install ICMP6 filters for %s", node->dest); + } + setsockopt(node->fd, SOL_IPV6, IPV6_RECVERR, (char *)&dummy, sizeof(dummy)); + + if ((cp = strchr(node->host, '%'))) { + struct ifreq ifr; + struct cmsghdr *cmsg; + struct in6_pktinfo *ipi; + + memset(&ifr, 0, sizeof(ifr)); + cp++; + crm_debug("set interface: [%s]", cp); + strncpy(ifr.ifr_name, cp, IFNAMSIZ-1); + + if (ioctl(node->fd, SIOCGIFINDEX, &ifr) >= 0) { + cmsg = (struct cmsghdr*)cmsgbuf; + cmsglen = CMSG_SPACE(sizeof(*ipi)); + cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi)); + cmsg->cmsg_level = SOL_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + + ipi = (struct in6_pktinfo*)CMSG_DATA(cmsg); + memset(ipi, 0, sizeof(*ipi)); + ipi->ipi6_ifindex = ifr.ifr_ifindex; + } else { + crm_warn("unknown interface %s specified", cp); + } + } + } else { + struct icmp_filter filt; + filt.data = ~((1<fd, SOL_RAW, ICMP_FILTER, (char*)&filt, sizeof(filt)) == -1) { + crm_perror(LOG_WARNING, "setsockopt failed: Cannot install ICMP filters for %s", node->dest); + } + setsockopt(node->fd, SOL_IP, IP_RECVERR, (char *)&dummy, sizeof(dummy)); + + if ((cp = strchr(node->host, '%'))) { + struct ifreq ifr; + struct cmsghdr *cmsg; + struct in_pktinfo *ipi; + + memset(&ifr, 0, sizeof(ifr)); + cp++; + crm_debug("set interface: [%s]", cp); + strncpy(ifr.ifr_name, cp, IFNAMSIZ-1); + + if (ioctl(node->fd, SIOCGIFINDEX, &ifr) >= 0) { + cmsg = (struct cmsghdr*)cmsgbuf; + cmsglen = CMSG_SPACE(sizeof(*ipi)); + cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi)); + cmsg->cmsg_level = SOL_IP; + cmsg->cmsg_type = IP_PKTINFO; + + ipi = (struct in_pktinfo*)CMSG_DATA(cmsg); + memset(ipi, 0, sizeof(*ipi)); + ipi->ipi_ifindex = ifr.ifr_ifindex; + } else { + crm_warn("unknown interface %s specified", cp); + } + } + } + } +#endif + + crm_debug_2("Opened connection to %s", node->dest); + freeaddrinfo(res); + return TRUE; + + bail: + if(res) { + freeaddrinfo(res); + } + return FALSE; +} + +static gboolean ping_close(ping_node *node) +{ + int tmp_fd = node->fd; + node->fd = -1; + + if (tmp_fd >= 0) { + if(close(tmp_fd) < 0) { + crm_perror(LOG_ERR,"Could not close ping socket"); + } else { + tmp_fd = -1; + crm_debug_2("Closed connection to %s", node->dest); + } + } + return (tmp_fd == -1); +} + +#define MAXPACKETLEN 131072 +#define ICMP6ECHOLEN 8 /* icmp echo header len excluding time */ +#define ICMP6ECHOTMLEN 20 +#define DEFDATALEN ICMP6ECHOTMLEN +#define EXTRA 256 /* for AH and various other headers. weird. */ +#define IP6LEN 40 + +static int +dump_v6_echo(ping_node *node, u_char *buf, int bytes, struct msghdr *hdr) +{ + int rc = -1; /* Try again */ + int fromlen; + char from_host[1024]; + + struct icmp6_hdr *icp; + struct sockaddr *from; + + if (!hdr || !hdr->msg_name || hdr->msg_namelen != sizeof(struct sockaddr_in6) + || ((struct sockaddr *)hdr->msg_name)->sa_family != AF_INET6) { + crm_warn("Invalid echo peer"); + return rc; + } + + fromlen = hdr->msg_namelen; + from = (struct sockaddr *)hdr->msg_name; + getnameinfo(from, fromlen, from_host, sizeof(from_host), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); + + if (bytes < (int)sizeof(struct icmp6_hdr)) { + crm_warn("Invalid echo packet (too short: %d bytes) from %s", bytes, from_host); + return rc; + } + icp = (struct icmp6_hdr *)buf; + + if (icp->icmp6_type == ICMP6_ECHO_REPLY) { + if (ident == ntohs(icp->icmp6_id) + && node->iseq == ntohs(icp->icmp6_seq)) { + rc = 1; /* Alive */ + } + + } else if(icp->icmp6_type != ICMP6_ECHO_REQUEST) { + rc = process_icmp6_error(node, (struct sockaddr_in6*)&(node->addr)); + } + + do_crm_log(LOG_DEBUG_2, + "Echo from %s (exp=%d, seq=%d, id=%d, dest=%s, data=%s): %s", + from_host, node->iseq, ntohs(icp->icmp6_seq), + ntohs(icp->icmp6_id), node->dest, (char*)(buf + ICMP6ECHOLEN), + ping_desc(node->type, icp->icmp6_type, icp->icmp6_code)); + + return rc; +} + +static int +dump_v4_echo(ping_node *node, u_char *buf, int bytes, struct msghdr *hdr) +{ + int rc = -1; /* Try again */ + int iplen, fromlen; + char from_host[1024]; + + struct ip *ip; + struct icmp *icp; + struct sockaddr *from; + + if (hdr == NULL + || !hdr->msg_name + || hdr->msg_namelen != sizeof(struct sockaddr_in) + || ((struct sockaddr *)hdr->msg_name)->sa_family != AF_INET) { + crm_warn("Invalid echo peer"); + return rc; + } + + fromlen = hdr->msg_namelen; + from = (struct sockaddr *)hdr->msg_name; + getnameinfo(from, fromlen, from_host, sizeof(from_host), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); + + ip = (struct ip*)buf; + iplen = ip->ip_hl * 4; + + if (bytes < (iplen + sizeof(struct icmp))) { + crm_warn("Invalid echo packet (too short: %d bytes) from %s", bytes, from_host); + return rc; + } + + /* Check the IP header */ + icp = (struct icmp*)(buf + iplen); + + if (icp->icmp_type == ICMP_ECHOREPLY) { + if (ident == ntohs(icp->icmp_id) + && node->iseq == ntohs(icp->icmp_seq)) { + rc = 1; /* Alive */ + } + + } else if(icp->icmp_type != ICMP_ECHO) { + rc = process_icmp4_error(node, (struct sockaddr_in*)from); + } + + /* TODO: Stop logging icmp_id once we're sure everything works */ + do_crm_log(LOG_DEBUG_2, + "Echo from %s (exp=%d, seq=%d, id=%d, dest=%s, data=%s): %s", + from_host, node->iseq, ntohs(icp->icmp_seq), + ntohs(icp->icmp_id), node->dest, icp->icmp_data, + ping_desc(node->type, icp->icmp_type, icp->icmp_code)); + + return rc; +} + +static int +ping_read(ping_node *node, int *lenp) +{ + int bytes; + char fromaddr[128]; + struct msghdr m; + struct cmsghdr *cm; + u_char buf[1024]; + struct iovec iov[2]; + int saved_errno = 0; + + struct timeval recv_start_time; + struct timeval recv_time; + int packlen; + u_char *packet; + + gettimeofday(&recv_start_time, NULL); + packlen = DEFDATALEN + IP6LEN + ICMP6ECHOLEN + EXTRA; + + crm_malloc0(packet, packlen); + + retry: + m.msg_name = &fromaddr; + m.msg_namelen = sizeof(fromaddr); + memset(&iov, 0, sizeof(iov)); + iov[0].iov_base = (caddr_t)packet; + iov[0].iov_len = packlen; + m.msg_iov = iov; + m.msg_iovlen = 1; + cm = (struct cmsghdr *)buf; + m.msg_control = (caddr_t)buf; + m.msg_controllen = sizeof(buf); + + + bytes = recvmsg(node->fd, &m, 0); + saved_errno = errno; + crm_debug_2("Got %d bytes", bytes); + + if(bytes < 0) { + crm_perror(LOG_DEBUG, "Read failed"); + if (saved_errno != EAGAIN && saved_errno != EINTR) { + int rc = 0; + if(node->type == AF_INET6) { + rc = process_icmp6_error(node, (struct sockaddr_in6*)&(node->addr)); + } else { + rc = process_icmp4_error(node, (struct sockaddr_in*)&fromaddr); + } + + if(rc < 0) { + crm_info("Retrying..."); + goto retry; + } + } + + } else if (bytes > 0) { + int rc = 0; + if(node->type == AF_INET6) { + rc = dump_v6_echo(node, packet, bytes, &m); + } else { + rc = dump_v4_echo(node, packet, bytes, &m); + } + + gettimeofday(&recv_time, NULL); + if ((recv_start_time.tv_sec + ping_timeout) < recv_time.tv_sec) { + crm_warn("failed to receive for timeout."); + crm_free(packet); + return FALSE; + } + + if(rc < 0) { + crm_info("Retrying..."); + goto retry; + + } else if(rc > 0) { + crm_free(packet); + return TRUE; + } + + } else { + crm_err("Unexpected reply"); + } + + crm_free(packet); + return FALSE; +} + +static int +ping_write(ping_node *node, const char *data, size_t size) +{ + struct iovec iov; + int rc, bytes, namelen; + /* static int ntransmitted = 9; */ + struct msghdr smsghdr; + u_char outpack[MAXPACKETLEN]; + memset(outpack, 0, MAXPACKETLEN); + + node->iseq++; + + if(node->type == AF_INET6) { + struct icmp6_hdr *icp; + namelen = sizeof(struct sockaddr_in6); + bytes = ICMP6ECHOLEN + DEFDATALEN; + + icp = (struct icmp6_hdr *)outpack; + + icp->icmp6_code = 0; + icp->icmp6_cksum = 0; + icp->icmp6_type = ICMP6_ECHO_REQUEST; + icp->icmp6_id = htons(ident); + icp->icmp6_seq = htons(node->iseq); + + /* Sanity check */ + if(ntohs(icp->icmp6_seq) != node->iseq) { + crm_debug("Wrapping at %u", node->iseq); + node->iseq = ntohs(icp->icmp6_seq); + } + + memcpy(&outpack[ICMP6ECHOLEN], "pingd-v6", 8); + + } else { + struct icmp *icp; + namelen = sizeof(struct sockaddr_in); + bytes = sizeof(struct icmp) + 11; + + icp = (struct icmp *)outpack; + + icp->icmp_code = 0; + icp->icmp_cksum = 0; + icp->icmp_type = ICMP_ECHO; + icp->icmp_id = htons(ident); + icp->icmp_seq = htons(node->iseq); + + /* Sanity check */ + if(ntohs(icp->icmp_seq) != node->iseq) { + crm_debug("Wrapping at %u", node->iseq); + node->iseq = ntohs(icp->icmp_seq); + } + + memcpy(icp->icmp_data, "pingd-v4", 8); + icp->icmp_cksum = in_cksum((u_short *)icp, bytes); + } + + memset(&iov, 0, sizeof(struct iovec)); + memset(&smsghdr, 0, sizeof(struct msghdr)); + + smsghdr.msg_name = (caddr_t)&(node->addr); + smsghdr.msg_namelen = namelen; + iov.iov_base = (caddr_t)outpack; + iov.iov_len = bytes; + smsghdr.msg_iov = &iov; + smsghdr.msg_iovlen = 1; + smsghdr.msg_control = cmsgbuf; + smsghdr.msg_controllen = cmsglen; + + rc = sendmsg(node->fd, &smsghdr, 0); + + if (rc < 0 || rc != bytes) { + crm_perror(LOG_WARNING, "Wrote %d of %d chars", rc, bytes); + return FALSE; + } + + crm_debug_2("Sent %d bytes to %s", rc, node->dest); + return TRUE; +} static gboolean pingd_shutdown(int nsig, gpointer unused) { need_shutdown = TRUE; - send_update(); - crm_info("Exiting"); + send_update(0); + g_hash_table_destroy(ping_nodes); + slist_destroy(ping_node, p, ping_list, + crm_free(p->host); + crm_free(p); + ); + if (mainloop != NULL && g_main_is_running(mainloop)) { g_main_quit(mainloop); } else { @@ -91,28 +1012,31 @@ stream = exit_status ? stderr : stdout; - fprintf(stream, "usage: %s [-%s]\n", cmd, OPTARGS); - fprintf(stream, "\t--%s (-%c) \t\t\tThis text\n", "help", '?'); - fprintf(stream, "\t--%s (-%c) \t\tRun in daemon mode\n", "daemonize", 'D'); - fprintf(stream, "\t--%s (-%c) \tFile in which to store the process' PID\n" - "\t\t\t\t\t* Default=/tmp/pingd.pid\n", "pid-file", 'p'); - fprintf(stream, "\t--%s (-%c) \tName of the node attribute to set\n" - "\t\t\t\t\t* Default=pingd\n", "attr-name", 'a'); - fprintf(stream, "\t--%s (-%c) \tName of the set in which to set the attribute\n" - "\t\t\t\t\t* Default=cib-bootstrap-options\n", "attr-set", 's'); - fprintf(stream, "\t--%s (-%c) \tWhich part of the CIB to put the attribute in\n" - "\t\t\t\t\t* Default=status\n", "attr-section", 'S'); - fprintf(stream, "\t--%s (-%c) \tMonitor a subset of the ping nodes listed in ha.cf (can be specified multiple times)\n", "ping-host", 'h'); - fprintf(stream, "\t--%s (-%c) \t\tHow long to wait for no further changes to occur before updating the CIB with a changed attribute\n", "attr-dampen", 'd'); - fprintf(stream, "\t--%s (-%c) \tFor every connected node, add to the value set in the CIB\n" - "\t\t\t\t\t\t* Default=1\n", "value-multiplier", 'm'); + fprintf(stream, "usage: %s [-?VDp:N:a:d:S:s:i:n:t:m:]\n", cmd); + + fprintf(stream, "\t--%s (-%c) \t\tThis text\n", "help", '?'); + fprintf(stream, "\t--%s (-%c) \t\tIncrease debug output\n\n", "verbose", 'V'); + + fprintf(stream, "\t--%s (-%c) \tRun in daemon mode\n", "daemonize", 'D'); + fprintf(stream, "\t--%s (-%c) \tFile in which to store the process' PID\n\n", "pid-file", 'p'); + + fprintf(stream, "\t--%s (-%c) \t\tDNS name or IP address of a host to check (can be specified more than once\n\n", "node", 'N'); + + fprintf(stream, "\t--%s (-%c) \tName of the node attribute to set\n", "attr-name", 'a'); + fprintf(stream, "\t--%s (-%c) \tHow long to wait for no further changes to occur before updating the CIB with a changed attribute\n", "attr-dampen", 'd'); + fprintf(stream, "\t--%s (-%c) \t(Advanced) Which part of the CIB to put the attribute in\n", "attr-section", 'S'); + fprintf(stream, "\t--%s (-%c) \t(Advanced) Name of the set in which to put the attribute\n\n", "attr-set", 's'); + + fprintf(stream, "\t--%s (-%c) \tHow often, in seconds, to check for node liveliness (default=1)\n", "ping-interval", 'i'); + fprintf(stream, "\t--%s (-%c) \tNumber of ping attempts, per host, before declaring it dead (default=2)\n", "ping-attempts", 'n'); + fprintf(stream, "\t--%s (-%c) \tHow long, in seconds, to wait before declaring a ping lost (default=2)\n", "ping-timeout", 't'); + fprintf(stream, "\t--%s (-%c) \tFor every connected node, add to the value set in the CIB\n", "ping-multiplier",'m'); fflush(stream); exit(exit_status); } - static gboolean pingd_ha_dispatch(IPC_Channel *channel, gpointer user_data) { @@ -190,8 +1114,7 @@ if (pingd_cluster->llc_ops->set_ifstatus_callback( pingd_cluster, pingd_lstatus_callback, NULL) != HA_OK) { - cl_log(LOG_ERR, "Cannot set if status callback"); - crm_err("REASON: %s", pingd_cluster->llc_ops->errmsg(pingd_cluster)); + crm_err("Cannot set if status callback: %s", pingd_cluster->llc_ops->errmsg(pingd_cluster)); return FALSE; } @@ -205,48 +1128,142 @@ return TRUE; } +void +do_node_walk(ll_cluster_t *hb_cluster) +{ + const char *ha_node = NULL; + + /* Async get client status information in the cluster */ + crm_debug_2("Invoked"); + crm_debug_3("Requesting an initial dump of CRMD client_status"); + hb_cluster->llc_ops->client_status( + hb_cluster, NULL, CRM_SYSTEM_CRMD, -1); + + crm_info("Requesting the list of configured nodes"); + hb_cluster->llc_ops->init_nodewalk(hb_cluster); + + do { + const char *ha_node_type = NULL; + const char *ha_node_status = NULL; + + ha_node = hb_cluster->llc_ops->nextnode(hb_cluster); + if(ha_node == NULL) { + continue; + } + + ha_node_type = hb_cluster->llc_ops->node_type( + hb_cluster, ha_node); + if(safe_str_neq("ping", ha_node_type)) { + crm_debug("Node %s: skipping '%s'", + ha_node, ha_node_type); + continue; + } + + if(do_filter + && g_hash_table_lookup(ping_nodes, ha_node) == NULL) { + crm_debug("Filtering: %s", ha_node); + continue; + } + + ha_node_status = hb_cluster->llc_ops->node_status( + hb_cluster, ha_node); + + crm_debug("Adding: %s=%s", ha_node, ha_node_status); + g_hash_table_replace(ping_nodes, crm_strdup(ha_node), + crm_strdup(ha_node_status)); + + } while(ha_node != NULL); + + hb_cluster->llc_ops->end_nodewalk(hb_cluster); + crm_debug_2("Complete"); + send_update(-1); +} + +static gboolean stand_alone_ping(gpointer data) +{ + int num_active = 0; + + crm_debug_2("Checking connectivity"); + slist_iter( + ping, ping_node, ping_list, num, + + if(ping_open(ping)) { + int lpc = 0; + for(;lpc < pings_per_host; lpc++) { + int len = 0; + if(ping_write(ping, "test", 4) == FALSE) { + crm_info("Node %s is unreachable (write)", ping->host); + + } else if(ping_read(ping, &len)) { + crm_debug("Node %s is alive", ping->host); + num_active++; + break; + } else { + crm_info("Node %s is unreachable (read)", ping->host); + } + sleep(1); + } + } + + ping_close(ping); + ); + + send_update(num_active); + + return TRUE; +} + int main(int argc, char **argv) { - int lpc; int argerr = 0; int flag; - char *pid_file = NULL; + const char *pid_file = NULL; gboolean daemonize = FALSE; + ping_node *p = NULL; #ifdef HAVE_GETOPT_H int option_index = 0; static struct option long_options[] = { /* Top-level Options */ - {"verbose", 0, 0, 'V'}, - {"help", 0, 0, '?'}, - {"pid-file", 1, 0, 'p'}, - {"ping-host", 1, 0, 'h'}, - {"attr-name", 1, 0, 'a'}, - {"attr-set", 1, 0, 's'}, - {"daemonize", 0, 0, 'D'}, - {"attr-section", 1, 0, 'S'}, - {"attr-dampen", 1, 0, 'd'}, - {"value-multiplier", 1, 0, 'm'}, + {"help", 0, 0, '?'}, + {"verbose", 0, 0, 'V'}, + {"daemonize", 0, 0, 'D'}, + {"pid-file", 1, 0, 'p'}, + {"node", 1, 0, 'N'}, + {"attr-name", 1, 0, 'a'}, + {"attr-dampen", 1, 0, 'd'}, + {"attr-section", 1, 0, 'S'}, + {"attr-set", 1, 0, 's'}, + {"ping-interval", 1, 0, 'i'}, + {"ping-attempts", 1, 0, 'n'}, + {"ping-timeout", 1, 0, 't'}, + {"ping-multiplier", 1, 0, 'm'}, + {"no-updates", 0, 0, 'U',}, + + /* Legacy */ + {"ping-host", 1, 0, 'h'}, + {"value-multiplier", 1, 0, 'm'}, + {"interval", 1, 0, 'i'}, {0, 0, 0, 0} }; #endif - pid_file = crm_strdup("/tmp/pingd.pid"); + + pid_file = "/tmp/pingd.pid"; G_main_add_SignalHandler( G_PRIORITY_HIGH, SIGTERM, pingd_shutdown, NULL, NULL); - + ping_nodes = g_hash_table_new_full( - g_str_hash, g_str_equal, - g_hash_destroy_str, g_hash_destroy_str); + g_str_hash, g_str_equal, + g_hash_destroy_str, g_hash_destroy_str); crm_log_init(basename(argv[0]), LOG_INFO, TRUE, FALSE, argc, argv); - + while (1) { #ifdef HAVE_GETOPT_H - flag = getopt_long(argc, argv, OPTARGS, - long_options, &option_index); + flag = getopt_long(argc, argv, OPTARGS, long_options, &option_index); #else flag = getopt(argc, argv, OPTARGS); #endif @@ -259,17 +1276,17 @@ alter_debug(DEBUG_INC); break; case 'p': - pid_file = crm_strdup(optarg); + pid_file = optarg; break; case 'a': - pingd_attr = crm_strdup(optarg); + pingd_attr = optarg; break; + case 'N': case 'h': - do_filter = TRUE; - fprintf(stdout, "Adding ping host %s", optarg); - g_hash_table_insert(ping_nodes, - crm_strdup(optarg), - crm_strdup(optarg)); + stand_alone = TRUE; + crm_debug("Adding ping host %s", optarg); + p = ping_new(optarg); + ping_list = g_list_append(ping_list, p); break; case 's': attr_set = crm_strdup(optarg); @@ -281,11 +1298,24 @@ attr_section = crm_strdup(optarg); break; case 'd': - attr_dampen = crm_strdup(optarg); + attr_dampen = crm_get_msec(optarg); + break; + case 'i': + re_ping_interval = crm_get_msec(optarg); + break; + case 'n': + pings_per_host = crm_atoi(optarg, NULL); + break; + case 't': + ping_timeout = crm_atoi(optarg, NULL); break; case 'D': daemonize = TRUE; break; + case 'U': + cl_log_enable_stderr(TRUE); + do_updates = FALSE; + break; case '?': usage(crm_system_name, LSB_EXIT_GENERIC); break; @@ -301,39 +1331,44 @@ crm_err("non-option ARGV-elements: "); printf("non-option ARGV-elements: "); while (optind < argc) { - crm_err("%s ", argv[optind++]); + crm_err("%s ", argv[optind]); printf("%s ", argv[optind++]); } printf("\n"); } if (argerr) { - usage(crm_system_name, LSB_EXIT_GENERIC); + usage(crm_system_name, LSB_EXIT_GENERIC); } crm_make_daemon(crm_system_name, daemonize, pid_file); + ident = getpid(); - for(lpc = 0; attrd == NULL && lpc < 30; lpc++) { - crm_debug("attrd registration attempt: %d", lpc); - sleep(5); - attrd = init_client_ipc_comms_nodispatch(T_ATTRD); - } - - if(attrd == NULL) { - crm_err("attrd registration failed"); - cl_flush_logs(); - exit(LSB_EXIT_GENERIC); + if(do_updates == FALSE) { + goto start_ping; } - if(register_with_ha() == FALSE) { + if(stand_alone == FALSE && register_with_ha() == FALSE) { crm_err("HA registration failed"); cl_flush_logs(); exit(LSB_EXIT_GENERIC); } + + start_ping: + if(stand_alone && ping_list == NULL) { + crm_err("You must specify a list of hosts to monitor"); + exit(LSB_EXIT_GENERIC); + } crm_info("Starting %s", crm_system_name); mainloop = g_main_new(FALSE); + + if(stand_alone) { + stand_alone_ping(NULL); + g_timeout_add(re_ping_interval, stand_alone_ping, NULL); + } + g_main_run(mainloop); - + crm_info("Exiting %s", crm_system_name); return 0; } @@ -348,41 +1383,73 @@ return; } - if(safe_str_eq(value, PINGSTATUS)) { + if(safe_str_eq(value, "ping")) { (*num_active)++; - } else if(safe_str_eq(value, LINKUP)) { + } else if(safe_str_eq(value, "up")) { (*num_active)++; } } void -send_update(void) +send_update(int num_active) { - int num_active = 0; + int lpc = 0; + static IPC_Channel *attrd = NULL; HA_Message *update = ha_msg_new(4); ha_msg_add(update, F_TYPE, T_ATTRD); ha_msg_add(update, F_ORIG, crm_system_name); ha_msg_add(update, F_ATTRD_TASK, "update"); ha_msg_add(update, F_ATTRD_ATTRIBUTE, pingd_attr); - g_hash_table_foreach(ping_nodes, count_ping_nodes, &num_active); + if(num_active < 0) { + num_active = 0; + g_hash_table_foreach(ping_nodes, count_ping_nodes, &num_active); + } crm_info("%d active ping nodes", num_active); ha_msg_add_int(update, F_ATTRD_VALUE, attr_multiplier*num_active); - + if(attr_set != NULL) { - ha_msg_add(update, F_ATTRD_SET, attr_set); + ha_msg_add(update, F_ATTRD_SET, attr_set); } if(attr_section != NULL) { ha_msg_add(update, F_ATTRD_SECTION, attr_section); } - if(attr_dampen != NULL) { - ha_msg_add(update, F_ATTRD_DAMPEN, attr_dampen); + if(attr_dampen > 0) { + ha_msg_add_int(update, F_ATTRD_DAMPEN, attr_dampen/1000); + } + + if(do_updates && attrd == NULL) { + crm_info("Attempting attrd (re-)registration"); + attrd = init_client_ipc_comms_nodispatch(T_ATTRD); + for(lpc = 0; attrd == NULL && lpc < 10; lpc++) { + sleep(1); + crm_debug("attrd registration attempt: %d", lpc); + attrd = init_client_ipc_comms_nodispatch(T_ATTRD); + } } - if(send_ipc_message(attrd, update) == FALSE) { - crm_err("Could not send update"); - exit(1); + if(do_updates == FALSE) { + char *buf = NULL; + if ((buf = dump_xml_formatted(update))) { + if ((strlen(buf)>0) && (buf[strlen(buf)-1]=='\n')) { + buf[strlen(buf)-1] = 0; + } + crm_info("%s", buf); + crm_free(buf); + } + + } else if(attrd == NULL) { + crm_err("Could not send update: %s=%d (Not connected)", + pingd_attr, attr_multiplier*num_active); + + } else if(send_ipc_message(attrd, update) == FALSE) { + crm_err("Could not send update: %s=%d (Connection failed)", + pingd_attr, attr_multiplier*num_active); + attrd = NULL; + } else { + crm_debug("Sent update: %s=%d (%d active ping nodes)", + pingd_attr, attr_multiplier*num_active, num_active); } crm_msg_del(update); } @@ -397,7 +1464,7 @@ if(g_hash_table_lookup(ping_nodes, node) != NULL) { g_hash_table_replace( ping_nodes, crm_strdup(node), crm_strdup(status)); - send_update(); + send_update(-1); } } @@ -410,53 +1477,3 @@ pingd_nstatus_callback(node, status, private); } -void -do_node_walk(ll_cluster_t *hb_cluster) -{ - const char *ha_node = NULL; - - /* Async get client status information in the cluster */ - crm_debug_2("Invoked"); - crm_debug_3("Requesting an initial dump of CRMD client_status"); - hb_cluster->llc_ops->client_status( - hb_cluster, NULL, CRM_SYSTEM_CRMD, -1); - - crm_info("Requesting the list of configured nodes"); - hb_cluster->llc_ops->init_nodewalk(hb_cluster); - - do { - const char *ha_node_type = NULL; - const char *ha_node_status = NULL; - - ha_node = hb_cluster->llc_ops->nextnode(hb_cluster); - if(ha_node == NULL) { - continue; - } - - ha_node_type = hb_cluster->llc_ops->node_type( - hb_cluster, ha_node); - if(safe_str_neq(PINGNODE, ha_node_type)) { - crm_debug("Node %s: skipping '%s'", - ha_node, ha_node_type); - continue; - } - - if(do_filter - && g_hash_table_lookup(ping_nodes, ha_node) == NULL) { - crm_debug("Filtering: %s", ha_node); - continue; - } - - ha_node_status = hb_cluster->llc_ops->node_status( - hb_cluster, ha_node); - - crm_debug("Adding: %s=%s", ha_node, ha_node_status); - g_hash_table_replace(ping_nodes, crm_strdup(ha_node), - crm_strdup(ha_node_status)); - - } while(ha_node != NULL); - - hb_cluster->llc_ops->end_nodewalk(hb_cluster); - crm_debug_2("Complete"); - send_update(); -}