fis-gtm/sr_unix_cm/gtcm_ping.c

264 lines
6.8 KiB
C

/****************************************************************
* *
* Copyright 2001, 2013 Fidelity Information Services, Inc *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
/* gtcm_ping.c - routines providing the capability of pinging remote
* machines.
*/
#include "mdef.h"
#include "gtm_stdio.h"
#include "gtm_stdlib.h" /* for exit() */
#include "gtm_unistd.h" /* for getpid() */
#include <errno.h>
#if defined(sun) || defined(mips)
#include <sys/time.h>
#else
#include "gtm_time.h"
#endif
#include <sys/types.h>
#include "gtm_socket.h"
#include "gtm_netdb.h"
#include "gtm_ipv6.h"
#include "gtm_inet.h"
#ifndef __MVS__
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#endif
#include "omi.h"
#include "gtmio.h"
#include "have_crit.h"
#if (defined(__CYGWIN__) || defined(__MVS__)) && !defined(ICMP_ECHO)
/* Note that we may actually need to use Windows sockets for icmp for now */
struct icmp
{
u_char icmp_type;
u_char icmp_code;
u_char icmp_cksum;
u_char icmp_id;
u_char icmp_seq;
u_char icmp_data[1];
};
#ifndef ICMP_ECHOREPLY
#define ICMP_ECHOREPLY 0
#endif
#ifndef ICMP_ECHO
#define ICMP_ECHO 8
#endif
#ifndef ICMP_MINLEN
#define ICMP_MINLEN 8
#endif
#ifndef IP_MAXPACKET
#define IP_MAXPACKET 65535
#endif
#endif /* __CYGWIN__ || __MVS */
#ifdef __MVS__
error_def(ERR_GETNAMEINFO);
struct ip
{
unsigned int ip_v:4; /* version */
unsigned int ip_hl:4; /* header length */
unsigned char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
unsigned char ip_ttl; /* time to live */
unsigned char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src, ip_dst; /* source and dest address */
};
#endif
static int pingsock = -1, ident;
static char pingrcv[IP_MAXPACKET], pingsend[256];
/* init_ping
* Set up a raw socket to ping machines
* Returns the socket id of the "ping" socket.
*/
int init_ping(void)
{
struct protoent *proto;
ident = getpid() & 0xFFFF;
if (!(proto = getprotobyname("icmp")))
{
FPRINTF(stderr, "ping: unknown protocol icmp.\n");
pingsock = -1;
return pingsock;
}
if ((pingsock = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
perror("ping: socket");
if (errno == EACCES)
OMI_DBG((omi_debug,"You must run this program as root in order to use the -ping option.\n"));
pingsock = -1;
}
return pingsock;
}
/* icmp_ping --
* Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
* will be added on by the kernel. The ID field is our UNIX process ID,
* and the sequence number is an ascending integer. The first 8 bytes
* of the data portion are used to hold a UNIX "timeval" struct in VAX
* byte-order, to compute the round-trip time.
*/
int icmp_ping(int conn)
{
fd_set fdmask;
struct sockaddr_storage paddr;
GTM_SOCKLEN_TYPE paddr_len = SIZEOF(struct sockaddr_storage);
struct icmp *icp;
struct ip *ip;
struct timeval timeout;
int cc;
if (pingsock < 0)
{
FPRINTF(stderr,"icmp_ping: no ping socket.\n");
exit(1);
}
if (getpeername(conn, (struct sockaddr *)&paddr, (GTM_SOCKLEN_TYPE *)&paddr_len) < 0)
{
perror("getpeername");
return -1; /* to denote error return */
}
icp = (struct icmp *)pingsend;
icp->icmp_type = ICMP_ECHO;
icp->icmp_code = 0;
icp->icmp_cksum = 0;
icp->icmp_seq = conn;
icp->icmp_id = ident; /* ID */
{
time_t time_val;
time(&time_val);
*((int *)(&pingsend[ICMP_MINLEN])) = (int) time_val; /* time stamp */
}
/* compute ICMP checksum here */
icp->icmp_cksum = in_cksum((u_short *)icp, ICMP_MINLEN + SIZEOF(int));
while (cc = sendto(pingsock, (char *)pingsend, ICMP_MINLEN + SIZEOF(int), 0, (struct sockaddr *)&paddr, paddr_len) < 0)
{
if (errno == EINTR)
continue;
perror("ping: sendto");
continue;
}
#ifdef DEBUG_PING
{
char host[SA_MAXLEN];
struct hostent *he;
if (0 != (errcode = getnameinfo((struct sockaddr *)&paddr, paddr_len, host, SA_MAXLEN, NULL, 0 0)))
{
assert(FALSE);
RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode);
return FALSE;
}
OMI_DBG((omi_debug, "ping: send to %s\n",host));
}
#endif
return 1; /* No Error. Added to make compiler happy */
}
/* get_ping_rsp
* Read the ping socket and determine the packet type.
*
* Returns: The sequence field in the incoming ECHO_REPLY packet, or -1
* if the packet is not a response to one of our pings.
*/
int get_ping_rsp(void)
{
struct sockaddr_storage from;
register int cc;
GTM_SOCKLEN_TYPE fromlen;
struct icmp *icp;
struct ip *ip;
if (pingsock < 0)
{
FPRINTF(stderr,"icmp_ping: no ping socket.\n");
exit(1);
}
/* SIZEOF() does not provide correct fromlen.
* fromlen in fact decided by recvfrom() below, so no need getaddrinfo() is needed to obtain the correct fromlen
*/
fromlen = SIZEOF(from);
while ((cc = (int)(recvfrom(pingsock, (char *)pingrcv, IP_MAXPACKET, 0, (struct sockaddr *)&from,
(GTM_SOCKLEN_TYPE *)&fromlen))) < 0)
{
if (errno == EINTR)
continue;
perror("ping: recvfrom");
continue;
}
ip = (struct ip *) pingrcv;
icp = (struct icmp *)(pingrcv + ((ip->ip_hl) << 2));
/* xxxxxxx icp = (struct icmp *)(pingrcv + (ip->ip_hl << 2)); */
#ifdef DEBUG_PING
{
char host[SA_MAXLEN];
struct hostent *he;
if (0 != (errcode = getnameinfo((struct sockaddr *)&from, fromlen, host, SA_MAXLEN, NULL, 0 0)))
{
assert(FALSE);
RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode);
return FALSE;
}
OMI_DBG((omi_debug, "ping: response from %s\n",host));
}
#endif
/* check to see if it is a reply and if it belongs to us */
if (icp->icmp_type == ICMP_ECHOREPLY && icp->icmp_id == ident)
return icp->icmp_seq;
else
return -1;
}
/* in_cksum --
* Checksum routine for Internet Protocol family headers (C Version)
*/
int in_cksum(u_short *addr, int len)
{
register int nleft = len;
register u_short *w = addr;
register int sum = 0;
u_short answer = 0;
/* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold 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) {
*(u_char *)(&answer) = *(u_char *)w ;
sum += answer;
}
/* add back carry outs 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);
}