3
* $Id: pinger.cc,v 1.59 2007/04/30 16:56:09 wessels Exp $
5
* DEBUG: section 42 ICMP Pinger program
6
* AUTHOR: Duane Wessels
8
* SQUID Web Proxy Cache http://www.squid-cache.org/
9
* ----------------------------------------------------------
11
* Squid is the result of efforts by numerous individuals from
12
* the Internet community; see the CONTRIBUTORS file for full
13
* details. Many organizations have provided support for Squid's
14
* development; see the SPONSORS file for full details. Squid is
15
* Copyrighted (C) 2001 by the Regents of the University of
16
* California; see the COPYRIGHT file for full details. Squid
17
* incorporates software developed and/or copyrighted by other
18
* sources; see the CREDITS file for full details.
20
* This program is free software; you can redistribute it and/or modify
21
* it under the terms of the GNU General Public License as published by
22
* the Free Software Foundation; either version 2 of the License, or
23
* (at your option) any later version.
25
* This program is distributed in the hope that it will be useful,
26
* but WITHOUT ANY WARRANTY; without even the implied warranty of
27
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28
* GNU General Public License for more details.
30
* You should have received a copy of the GNU General Public License
31
* along with this program; if not, write to the Free Software
32
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
36
#define SQUID_HELPER 1
39
#include "SquidTime.h"
43
/* Native Windows port doesn't have netinet support, so we emulate it.
44
At this time, Cygwin lacks icmp support in its include files, so we need
45
to use the native Windows port definitions.
50
#include <netinet/in_systm.h>
51
#include <netinet/in.h>
52
#include <netinet/ip.h>
53
#include <netinet/ip_icmp.h>
55
#define PINGER_TIMEOUT 10
57
static int socket_from_squid = 0;
58
static int socket_to_squid = 1;
60
#else /* _SQUID_WIN32_ */
71
#define PINGER_TIMEOUT 5
73
static int socket_to_squid = -1;
74
#define socket_from_squid socket_to_squid
76
#else /* _SQUID_CYGWIN_ */
77
#include <netinet/in_systm.h>
78
#include <netinet/in.h>
79
#include <netinet/ip.h>
80
#include <netinet/ip_icmp.h>
82
#define PINGER_TIMEOUT 10
84
static int socket_from_squid = 0;
85
static int socket_to_squid = 1;
90
#define ICMP_ECHOREPLY 0
96
4; /* Length of the header in dwords */
99
4; /* Version of IP */
100
u_int8_t tos; /* Type of service */
101
u_int16_t total_len; /* Length of the packet in dwords */
102
u_int16_t ident; /* unique identifier */
103
u_int16_t flags; /* Flags */
104
u_int8_t ip_ttl; /* Time to live */
105
u_int8_t proto; /* Protocol number (TCP, UDP etc) */
106
u_int16_t checksum; /* IP checksum */
115
typedef struct icmphdr
117
u_int8_t icmp_type; /* ICMP packet type */
118
u_int8_t icmp_code; /* Type sub code */
119
u_int16_t icmp_cksum;
122
u_int32_t timestamp; /* not part of ICMP, but we need it */
127
#endif /* _SQUID_MSWIN_ */
129
#ifndef _SQUID_LINUX_
130
#ifndef _SQUID_CYGWIN_
131
#ifndef _SQUID_MSWIN_
138
#if defined (_SQUID_LINUX_)
145
#define icmp_type type
146
#define icmp_code code
147
#define icmp_cksum checksum
148
#define icmp_id un.echo.id
149
#define icmp_seq un.echo.sequence
153
#define ip_len tot_len
155
#define ip_off frag_off
157
#define ip_p protocol
163
#if ALLOW_SOURCE_PING
164
#define MAX_PKT_SZ 8192
165
#define MAX_PAYLOAD (MAX_PKT_SZ - sizeof(struct icmphdr) - sizeof (char) - sizeof(struct timeval) - 1)
167
#define MAX_PAYLOAD SQUIDHOSTNAMELEN
168
#define MAX_PKT_SZ (MAX_PAYLOAD + sizeof(struct timeval) + sizeof (char) + sizeof(struct icmphdr) + 1)
175
unsigned char opcode;
176
char payload[MAX_PAYLOAD];
182
int icmp_pkts_sent = 0;
184
static const char *icmpPktStr[] =
189
"Destination Unreachable",
206
static int in_cksum(unsigned short *ptr, int size);
207
static void pingerRecv(void);
209
static void pingerLog(struct icmphdr *, struct IN_ADDR, int, int);
210
static int ipHops(int ttl);
211
static void pingerSendtoSquid(pingerReplyData * preply);
212
static void pingerOpen(void);
213
static void pingerClose(void);
217
int Win32__WSAFDIsSet(int fd, fd_set FAR * set
220
fde *F = &fd_table[fd];
221
SOCKET s = F->win32.handle;
223
return __WSAFDIsSet(s, set
228
Win32SockCleanup(void)
239
struct protoent *proto = NULL;
243
WSAPROTOCOL_INFO wpi;
244
char buf[sizeof(wpi)];
247
struct sockaddr_in PS;
249
WSAStartup(2, &wsaData);
250
atexit(Win32SockCleanup);
253
_db_init(NULL, "ALL,1");
254
setmode(0, O_BINARY);
255
setmode(1, O_BINARY);
256
x = read(0, buf, sizeof(wpi));
258
if (x < (int)sizeof(wpi)) {
260
debugs(42, 0, "pingerOpen: read: FD 0: " << xstrerror());
261
write(1, "ERR\n", 4);
265
xmemcpy(&wpi, buf, sizeof(wpi));
268
x = read(0, buf, sizeof(PS));
270
if (x < (int)sizeof(PS)) {
272
debugs(42, 0, "pingerOpen: read: FD 0: " << xstrerror());
273
write(1, "ERR\n", 4);
277
xmemcpy(&PS, buf, sizeof(PS));
280
if ((proto = getprotobyname("icmp")) == 0) {
281
debugs(42, 0, "pingerOpen: unknown protocol: icmp");
285
icmp_sock = socket(PF_INET, SOCK_RAW, proto->p_proto);
288
debugs(50, 0, "pingerOpen: icmp_sock: " << xstrerror());
292
icmp_ident = getpid() & 0xffff;
293
debugs(42, 0, "pinger: ICMP socket opened");
297
WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
300
if (socket_to_squid == -1) {
302
debugs(42, 0, "pingerOpen: WSASocket: " << xstrerror());
303
write(1, "ERR\n", 4);
307
x = connect(socket_to_squid, (struct sockaddr *) &PS, sizeof(PS));
309
if (SOCKET_ERROR == x) {
311
debugs(42, 0, "pingerOpen: connect: " << xstrerror());
312
write(1, "ERR\n", 4);
317
memset(buf, 0, sizeof(buf));
318
x = recv(socket_to_squid, (void *) buf, sizeof(buf), 0);
321
debugs(42, 0, "icmpOpen: recv: " << xstrerror());
325
x = send(socket_to_squid, (const void *) buf, strlen(buf), 0);
327
if (x < 3 || strncmp("OK\n", buf, 3)) {
328
debugs(42, 0, "icmpOpen: recv: " << xstrerror());
333
debugs(42, 0, "pinger: Squid socket opened");
343
shutdown(socket_to_squid, SD_BOTH);
344
close(socket_to_squid);
345
socket_to_squid = -1;
354
pingerSendEcho(struct IN_ADDR to, int opcode, char *payload, int len)
356
LOCAL_ARRAY(char, pkt, MAX_PKT_SZ);
358
struct icmphdr *icmp = NULL;
361
size_t icmp_pktsize = sizeof(struct icmphdr);
363
struct sockaddr_in S;
364
memset(pkt, '\0', MAX_PKT_SZ);
366
icmp = (struct icmphdr *) (void *) pkt;
369
* cevans - beware signed/unsigned issues in untrusted data from
378
icmp->icmp_type = ICMP_ECHO;
380
icmp->icmp_cksum = 0;
381
icmp->icmp_id = icmp_ident;
382
icmp->icmp_seq = (u_short) icmp_pkts_sent++;
383
echo = (icmpEchoData *) (icmp + 1);
384
echo->opcode = (unsigned char) opcode;
385
echo->tv = current_time;
387
icmp_pktsize += sizeof(struct timeval) + sizeof(char);
391
if (len > MAX_PAYLOAD)
394
xmemcpy(echo->payload, payload, len);
399
icmp->icmp_cksum = in_cksum((u_short *) icmp, icmp_pktsize);
400
S.sin_family = AF_INET;
402
* cevans: alert: trusting to-host, was supplied in network packet
406
assert(icmp_pktsize <= MAX_PKT_SZ);
412
(struct sockaddr *) &S,
414
sizeof(struct sockaddr_in));
415
pingerLog(icmp, to, 0, 0);
424
struct sockaddr_in from;
427
struct iphdr *ip = NULL;
429
struct icmphdr *icmp = NULL;
430
static char *pkt = NULL;
434
static pingerReplyData preply;
437
pkt = (char *)xmalloc(MAX_PKT_SZ);
439
fromlen = sizeof(from);
441
n = recvfrom(icmp_sock,
446
(struct sockaddr *) &from,
449
#if GETTIMEOFDAY_NO_TZP
455
gettimeofday(&now, NULL);
459
debugs(42, 9, "pingerRecv: " << n << " bytes from " <<
460
inet_ntoa(from.sin_addr));
462
ip = (struct iphdr *) (void *) pkt;
464
#if HAVE_STRUCT_IPHDR_IP_HL
466
iphdrlen = ip->ip_hl << 2;
468
#else /* HAVE_STRUCT_IPHDR_IP_HL */
471
iphdrlen = (ip->ip_vhl >> 4) << 2;
475
iphdrlen = (ip->ip_vhl & 0xF) << 2;
478
#endif /* HAVE_STRUCT_IPHDR_IP_HL */
480
icmp = (struct icmphdr *) (void *) (pkt + iphdrlen);
482
if (icmp->icmp_type != ICMP_ECHOREPLY)
485
if (icmp->icmp_id != icmp_ident)
488
echo = (icmpEchoData *) (void *) (icmp + 1);
490
preply.from = from.sin_addr;
492
preply.opcode = echo->opcode;
494
preply.hops = ipHops(ip->ip_ttl);
496
preply.rtt = tvSubMsec(echo->tv, now);
498
preply.psize = n - iphdrlen - (sizeof(icmpEchoData) - MAX_PKT_SZ);
500
pingerSendtoSquid(&preply);
502
pingerLog(icmp, from.sin_addr, preply.rtt, preply.hops);
507
in_cksum(unsigned short *ptr, int size)
510
unsigned short oddbyte;
511
unsigned short answer;
521
*((unsigned char *) &oddbyte) = *(unsigned char *) ptr;
525
sum = (sum >> 16) + (sum & 0xffff);
527
answer = (unsigned short) ~sum;
533
pingerLog(struct icmphdr *icmp, struct IN_ADDR addr, int rtt, int hops)
535
debugs(42, 2, "pingerLog: " << std::setw(9) << current_time.tv_sec <<
536
"."<< std::setfill('0') << std::setw(6) <<
537
current_time.tv_usec << " "<< std::left << std::setfill(' ')<<
538
std::setw(16) << inet_ntoa(addr) << " "<< icmp->icmp_type <<
539
" " << std::setw(15) << icmpPktStr[icmp->icmp_type] << " " << rtt <<
540
"ms " << hops << " hops");
550
return 63 - ttl; /* 62 = (64+60)/2 */
553
return 65 - ttl; /* 62 = (64+60)/2 */
565
pingerReadRequest(void)
567
static pingerEchoData pecho;
570
memset(&pecho, '\0', sizeof(pecho));
571
n = recv(socket_from_squid, &pecho, sizeof(pecho), 0);
578
fprintf(stderr, "EOF encountered\n");
583
guess_size = n - (sizeof(pingerEchoData) - PINGER_PAYLOAD_SZ);
585
if (guess_size != pecho.psize) {
586
fprintf(stderr, "size mismatch, guess=%d psize=%d\n",
587
guess_size, pecho.psize);
588
/* don't process this message, but keep running */
592
pingerSendEcho(pecho.to,
600
pingerSendtoSquid(pingerReplyData * preply)
602
int len = sizeof(pingerReplyData) - MAX_PKT_SZ + preply->psize;
604
if (send(socket_to_squid, preply, len, 0) < 0) {
605
debugs(50, 0, "pinger: send: " << xstrerror());
612
main(int argc, char *argv[])
618
const char *debug_args = "ALL,1";
620
time_t last_check_time = 0;
623
* cevans - do this first. It grabs a raw socket. After this we can
630
if ((t = getenv("SQUID_DEBUG")))
631
debug_args = xstrdup(t);
635
_db_init(NULL, debug_args);
638
tv.tv_sec = PINGER_TIMEOUT;
641
FD_SET(socket_from_squid, &R);
642
FD_SET(icmp_sock, &R);
643
x = select(icmp_sock + 1, &R, NULL, NULL, &tv);
651
if (FD_ISSET(socket_from_squid, &R))
652
if (pingerReadRequest() < 0) {
653
debugs(42, 0, "Pinger exiting.");
658
if (FD_ISSET(icmp_sock, &R))
661
if (PINGER_TIMEOUT + last_check_time < squid_curtime) {
662
if (send(socket_to_squid, &tv, 0, 0) < 0) {
667
last_check_time = squid_curtime;
678
main(int argc, char *argv[])
680
fprintf(stderr, "%s: ICMP support not compiled in.\n", argv[0]);
684
#endif /* USE_ICMP */