/**************************************************************** * * * Copyright 2001, 2012 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 #if defined(sun) || defined(mips) #include #else #include "gtm_time.h" #endif #include #include "gtm_netdb.h" #include "gtm_socket.h" #ifndef __MVS__ #include #endif #include "gtm_inet.h" #ifndef __MVS__ #include #include #include #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__ 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_in paddr; GTM_SOCKLEN_TYPE paddr_len = SIZEOF(struct sockaddr_in); 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; struct hostent *he; char msg[64]; #ifndef SUNOS host = inet_ntoa(paddr.sin_addr.s_addr); if ((he = gethostbyaddr(paddr.sin_addr.s_addr, SIZEOF(paddr.sin_addr.s_addr), 0))) host = he->h_name; #else SPRINTF(msg, "%d.%d.%d.%d", paddr.sin_addr.s_addr >> 24, paddr.sin_addr.s_addr >> 16 & 0xFF, paddr.sin_addr.s_addr >> 8 & 0xFF, paddr.sin_addr.s_addr & 0xFF); host = msg; #endif 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_in 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); } 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; struct hostent *he; char msg[64]; #ifndef SUNOS host = inet_ntoa(from.sin_addr.s_addr); if ((he = gethostbyaddr(from.sin_addr.s_addr, SIZEOF(from.sin_addr.s_addr), 0))) host = he->h_name; #else SPRINTF(msg, "%d.%d.%d.%d", from.sin_addr.s_addr >> 24, from.sin_addr.s_addr >> 16 & 0xFF, from.sin_addr.s_addr >> 8 & 0xFF, from.sin_addr.s_addr & 0xFF); host = msg; #endif 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); }