| /* $Header: /cvsroot/watchdog/watchdog/src/net.c,v 1.3 2007/01/08 12:01:58 meskes Exp $ */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <errno.h> |
| #include <sys/time.h> |
| #include <netinet/ip.h> |
| #include <netinet/ip_icmp.h> |
| |
| #include "extern.h" |
| #include "watch_err.h" |
| |
| #if USE_SYSLOG |
| #include <syslog.h> |
| #endif |
| |
| /* |
| * in_cksum -- |
| * Checksum routine for Internet Protocol family headers (C Version) |
| */ |
| static int in_cksum(unsigned short *addr, int len) |
| { |
| int nleft = len, sum = 0; |
| unsigned short *w = addr, 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) { |
| sum += htons(*(u_char *) w << 8); |
| } |
| /* 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); |
| } |
| |
| int check_net(char *target, int sock_fp, struct sockaddr to, unsigned char *packet, int time, int count) |
| { |
| int i; |
| unsigned char outpack[MAXPACKET]; |
| |
| if (target == NULL) |
| return (ENOERR); |
| |
| /* try "ping-count" times */ |
| for (i = 0; i < count; i++) { |
| |
| struct sockaddr_in from; |
| int fdmask, j; |
| socklen_t fromlen; |
| struct timeval timeout, dtimeout; |
| struct icmphdr *icp = (struct icmphdr *) outpack; |
| |
| /* setup a ping message */ |
| icp->type = ICMP_ECHO; |
| icp->code = icp->checksum = 0; |
| icp->un.echo.sequence = htons(i + 1); |
| icp->un.echo.id = pid; /* ID */ |
| |
| /* compute ICMP checksum here */ |
| icp->checksum = in_cksum((unsigned short *) icp, DATALEN + 8); |
| |
| /* and send it out */ |
| j = sendto(sock_fp, (char *) outpack, DATALEN + 8, 0, &to, sizeof(struct sockaddr)); |
| |
| if (j < 0) { |
| int err = errno; |
| |
| /* if our kernel tells us the network is unreachable we are done */ |
| if (err == ENETUNREACH) { |
| #if USE_SYSLOG |
| syslog(LOG_ERR, "network is unreachable (target: %s)", target); |
| #endif /* USE_SYSLOG */ |
| |
| return (ENETUNREACH); |
| |
| } else { |
| #if USE_SYSLOG |
| syslog(LOG_ERR, "sendto gave errno = %d = '%m'\n", err); |
| #else /* USE_SYSLOG */ |
| perror(progname); |
| #endif /* USE_SYSLOG */ |
| |
| if (softboot) |
| return (err); |
| } |
| |
| } else { |
| gettimeofday(&timeout, NULL); |
| /* set the timeout value */ |
| j = time / count; |
| /* we have to wait at least one second */ |
| if (j == 0) |
| j = 1; |
| timeout.tv_sec += j; |
| |
| /* wait for reply */ |
| fdmask = 1 << sock_fp; |
| while (1) { |
| gettimeofday(&dtimeout, NULL); |
| dtimeout.tv_sec = timeout.tv_sec - dtimeout.tv_sec; |
| dtimeout.tv_usec = timeout.tv_usec - dtimeout.tv_usec; |
| if (dtimeout.tv_usec < 0) { |
| dtimeout.tv_usec += 1000000; |
| dtimeout.tv_sec--; |
| } |
| while (dtimeout.tv_usec >= 1000000) { |
| dtimeout.tv_usec -= 1000000; |
| dtimeout.tv_sec++; |
| } |
| if (dtimeout.tv_sec <= 0) |
| break; |
| #if USE_SYSLOG |
| if (verbose && logtick && ticker == 1) |
| syslog(LOG_ERR, "ping select timeout = %ld seconds and %ld useconds\n", dtimeout.tv_sec, dtimeout.tv_usec); |
| #endif /* USE_SYSLOG */ |
| |
| if (select(sock_fp + 1, (fd_set *) & fdmask, (fd_set *) NULL, (fd_set *) NULL, &dtimeout) >= 1) { |
| |
| /* read reply */ |
| fromlen = sizeof(from); |
| if (recvfrom(sock_fp, (char *) packet, DATALEN + MAXIPLEN + MAXICMPLEN, 0, (struct sockaddr *) &from, &fromlen) < 0) { |
| int err = errno; |
| |
| if (err != EINTR) |
| #if USE_SYSLOG |
| syslog(LOG_ERR, "recvfrom gave errno = %d = '%m'\n", err); |
| #else /* USE_SYSLOG */ |
| perror(progname); |
| #endif /* USE_SYSLOG */ |
| if (softboot) |
| return (err); |
| |
| continue; |
| } |
| |
| /* check if packet is our ECHO */ |
| icp = (struct icmphdr *) (packet + (((struct ip *) packet)->ip_hl << 2)); |
| |
| if (icp->type == ICMP_ECHOREPLY && icp->un.echo.id == pid) { |
| if (from.sin_addr.s_addr == ((struct sockaddr_in *) &to)->sin_addr.s_addr) { |
| #if USE_SYSLOG |
| if (verbose && logtick && ticker == 1) |
| syslog(LOG_INFO, "got answer from target %s", target); |
| #endif |
| return (ENOERR); |
| } |
| } |
| } |
| } |
| } |
| } |
| #if USE_SYSLOG |
| syslog(LOG_ERR, "no response from ping (target: %s)", target); |
| #endif /* USE_SYSLOG */ |
| return (ENETUNREACH); |
| } |