]>
Commit | Line | Data |
---|---|---|
9d90e665 | 1 | |
2 | /* | |
d57288d2 | 3 | * $Id: pinger.cc,v 1.28 1997/10/31 05:15:08 wessels Exp $ |
9d90e665 | 4 | * |
f43e2ec2 | 5 | * DEBUG: section 42 ICMP Pinger program |
9d90e665 | 6 | * AUTHOR: Duane Wessels |
7 | * | |
42c04c16 | 8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ |
9d90e665 | 9 | * -------------------------------------------------------- |
10 | * | |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
13 | * National Laboratory for Applied Network Research and funded by | |
14 | * the National Science Foundation. | |
15 | * | |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License as published by | |
18 | * the Free Software Foundation; either version 2 of the License, or | |
19 | * (at your option) any later version. | |
20 | * | |
21 | * This program is distributed in the hope that it will be useful, | |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 | * GNU General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU General Public License | |
27 | * along with this program; if not, write to the Free Software | |
28 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
29 | * | |
30 | */ | |
31 | ||
9d90e665 | 32 | #include "squid.h" |
9d90e665 | 33 | |
43426991 | 34 | #if USE_ICMP |
35 | ||
9d90e665 | 36 | #include <netinet/in_systm.h> |
37 | #include <netinet/in.h> | |
38 | #include <netinet/ip.h> | |
39 | #include <netinet/ip_icmp.h> | |
40 | ||
41 | #ifndef _SQUID_LINUX_ | |
42 | #define icmphdr icmp | |
43 | #define iphdr ip | |
44 | #endif | |
45 | ||
46 | #ifdef _SQUID_LINUX_ | |
47 | #define icmp_type type | |
48 | #define icmp_code code | |
49 | #define icmp_cksum checksum | |
50 | #define icmp_id un.echo.id | |
51 | #define icmp_seq un.echo.sequence | |
52 | #define icmp_gwaddr un.gateway | |
53 | #define ip_hl ihl | |
54 | #define ip_v version | |
55 | #define ip_tos tos | |
56 | #define ip_len tot_len | |
57 | #define ip_id id | |
58 | #define ip_off frag_off | |
59 | #define ip_ttl ttl | |
60 | #define ip_p protocol | |
61 | #define ip_sum check | |
62 | #define ip_src saddr | |
63 | #define ip_dst daddr | |
64 | #endif | |
65 | ||
429fdbec | 66 | #define MAX_PKT_SZ 8192 |
67 | #define MAX_PAYLOAD (MAX_PKT_SZ - sizeof(struct icmphdr) - sizeof (char) - sizeof(struct timeval) - 1) | |
9d90e665 | 68 | |
69 | typedef struct { | |
70 | struct timeval tv; | |
71 | unsigned char opcode; | |
72 | char payload[MAX_PAYLOAD]; | |
73 | } icmpEchoData; | |
74 | ||
5be53f2d | 75 | int icmp_ident = -1; |
76 | int icmp_pkts_sent = 0; | |
9d90e665 | 77 | |
0ee4272b | 78 | static const char *icmpPktStr[] = |
9d90e665 | 79 | { |
80 | "Echo Reply", | |
81 | "ICMP 1", | |
82 | "ICMP 2", | |
83 | "Destination Unreachable", | |
84 | "Source Quench", | |
85 | "Redirect", | |
86 | "ICMP 6", | |
87 | "ICMP 7", | |
88 | "Echo", | |
89 | "ICMP 9", | |
90 | "ICMP 10", | |
91 | "Time Exceeded", | |
92 | "Parameter Problem", | |
93 | "Timestamp", | |
94 | "Timestamp Reply", | |
95 | "Info Request", | |
96 | "Info Reply", | |
97 | "Out of Range Type" | |
98 | }; | |
99 | ||
f5b8bbc4 | 100 | static int in_cksum(unsigned short *ptr, int size); |
101 | static void pingerRecv(void); | |
102 | static void pingerLog(struct icmphdr *, struct in_addr, int, int); | |
103 | static int ipHops(int ttl); | |
104 | static void pingerSendtoSquid(pingerReplyData * preply); | |
9d90e665 | 105 | |
106 | void | |
107 | pingerOpen(void) | |
108 | { | |
109 | struct protoent *proto = NULL; | |
110 | if ((proto = getprotobyname("icmp")) == 0) { | |
a3d5953d | 111 | debug(42, 0) ("pingerOpen: unknown protocol: icmp\n"); |
9d90e665 | 112 | exit(1); |
113 | } | |
114 | icmp_sock = socket(PF_INET, SOCK_RAW, proto->p_proto); | |
115 | if (icmp_sock < 0) { | |
a3d5953d | 116 | debug(50, 0) ("pingerOpen: icmp_sock: %s\n", xstrerror()); |
9d90e665 | 117 | exit(1); |
118 | } | |
119 | icmp_ident = getpid() & 0xffff; | |
a3d5953d | 120 | debug(42, 0) ("ICMP socket opened\n", icmp_sock); |
9d90e665 | 121 | } |
122 | ||
9d90e665 | 123 | void |
124 | pingerClose(void) | |
125 | { | |
126 | close(icmp_sock); | |
127 | icmp_sock = -1; | |
128 | icmp_ident = 0; | |
129 | } | |
130 | ||
131 | static void | |
132 | pingerSendEcho(struct in_addr to, int opcode, char *payload, int len) | |
133 | { | |
429fdbec | 134 | LOCAL_ARRAY(char, pkt, MAX_PKT_SZ); |
9d90e665 | 135 | struct icmphdr *icmp = NULL; |
136 | icmpEchoData *echo; | |
137 | int icmp_pktsize = sizeof(struct icmphdr); | |
138 | int x; | |
139 | struct sockaddr_in S; | |
429fdbec | 140 | memset(pkt, '\0', MAX_PKT_SZ); |
9d90e665 | 141 | icmp = (struct icmphdr *) (void *) pkt; |
142 | icmp->icmp_type = ICMP_ECHO; | |
143 | icmp->icmp_code = 0; | |
144 | icmp->icmp_cksum = 0; | |
145 | icmp->icmp_id = icmp_ident; | |
146 | icmp->icmp_seq = icmp_pkts_sent++; | |
147 | echo = (icmpEchoData *) (icmp + 1); | |
148 | echo->opcode = (unsigned char) opcode; | |
149 | echo->tv = current_time; | |
150 | icmp_pktsize += sizeof(icmpEchoData) - MAX_PAYLOAD; | |
151 | if (payload) { | |
152 | if (len > MAX_PAYLOAD) | |
153 | len = MAX_PAYLOAD; | |
3c0117c9 | 154 | xmemcpy(echo->payload, payload, len); |
9d90e665 | 155 | icmp_pktsize += len; |
156 | } | |
157 | icmp->icmp_cksum = in_cksum((u_short *) icmp, icmp_pktsize); | |
158 | S.sin_family = AF_INET; | |
159 | S.sin_addr = to; | |
160 | S.sin_port = 0; | |
161 | x = sendto(icmp_sock, | |
162 | pkt, | |
163 | icmp_pktsize, | |
164 | 0, | |
165 | (struct sockaddr *) &S, | |
166 | sizeof(struct sockaddr_in)); | |
167 | pingerLog(icmp, to, 0, 0); | |
168 | } | |
169 | ||
170 | static void | |
171 | pingerRecv(void) | |
172 | { | |
173 | int n; | |
174 | int fromlen; | |
175 | struct sockaddr_in from; | |
176 | int iphdrlen = 20; | |
177 | struct iphdr *ip = NULL; | |
639ed75c | 178 | struct icmphdr *icmp = NULL; |
429fdbec | 179 | LOCAL_ARRAY(char, pkt, MAX_PKT_SZ); |
9d90e665 | 180 | struct timeval now; |
181 | icmpEchoData *echo; | |
182 | static pingerReplyData preply; | |
183 | ||
184 | fromlen = sizeof(from); | |
185 | n = recvfrom(icmp_sock, | |
186 | pkt, | |
429fdbec | 187 | MAX_PKT_SZ, |
9d90e665 | 188 | 0, |
189 | (struct sockaddr *) &from, | |
190 | &fromlen); | |
191 | gettimeofday(&now, NULL); | |
a3d5953d | 192 | debug(42, 9) ("pingerRecv: %d bytes from %s\n", n, inet_ntoa(from.sin_addr)); |
9d90e665 | 193 | ip = (struct iphdr *) (void *) pkt; |
194 | #if HAVE_IP_HL | |
195 | iphdrlen = ip->ip_hl << 2; | |
9c4f4529 | 196 | #else /* HAVE_IP_HL */ |
d57288d2 | 197 | #if WORDS_BIGENDIAN |
9d90e665 | 198 | iphdrlen = (ip->ip_vhl >> 4) << 2; |
d57288d2 | 199 | #else |
9d90e665 | 200 | iphdrlen = (ip->ip_vhl & 0xF) << 2; |
201 | #endif | |
9c4f4529 | 202 | #endif /* HAVE_IP_HL */ |
9d90e665 | 203 | icmp = (struct icmphdr *) (void *) (pkt + iphdrlen); |
204 | if (icmp->icmp_type != ICMP_ECHOREPLY) | |
205 | return; | |
206 | if (icmp->icmp_id != icmp_ident) | |
207 | return; | |
208 | echo = (icmpEchoData *) (void *) (icmp + 1); | |
209 | preply.from = from.sin_addr; | |
210 | preply.opcode = echo->opcode; | |
211 | preply.hops = ipHops(ip->ip_ttl); | |
212 | preply.rtt = tvSubMsec(echo->tv, now); | |
429fdbec | 213 | preply.psize = n - iphdrlen - (sizeof(icmpEchoData) - MAX_PKT_SZ); |
9d90e665 | 214 | pingerSendtoSquid(&preply); |
215 | pingerLog(icmp, from.sin_addr, preply.rtt, preply.hops); | |
216 | } | |
217 | ||
218 | ||
219 | static int | |
220 | in_cksum(unsigned short *ptr, int size) | |
221 | { | |
639ed75c | 222 | long sum; |
9d90e665 | 223 | unsigned short oddbyte; |
639ed75c | 224 | unsigned short answer; |
9d90e665 | 225 | sum = 0; |
226 | while (size > 1) { | |
227 | sum += *ptr++; | |
228 | size -= 2; | |
229 | } | |
230 | if (size == 1) { | |
231 | oddbyte = 0; | |
232 | *((unsigned char *) &oddbyte) = *(unsigned char *) ptr; | |
233 | sum += oddbyte; | |
234 | } | |
235 | sum = (sum >> 16) + (sum & 0xffff); | |
236 | sum += (sum >> 16); | |
237 | answer = ~sum; | |
238 | return (answer); | |
239 | } | |
240 | ||
241 | static void | |
242 | pingerLog(struct icmphdr *icmp, struct in_addr addr, int rtt, int hops) | |
243 | { | |
a3d5953d | 244 | debug(42, 2) ("pingerLog: %9d.%06d %-16s %d %-15.15s %dms %d hops\n", |
9d90e665 | 245 | (int) current_time.tv_sec, |
246 | (int) current_time.tv_usec, | |
247 | inet_ntoa(addr), | |
248 | (int) icmp->icmp_type, | |
249 | icmpPktStr[icmp->icmp_type], | |
250 | rtt, | |
251 | hops); | |
252 | } | |
253 | ||
254 | static int | |
255 | ipHops(int ttl) | |
256 | { | |
f15528f6 | 257 | if (ttl < 33) |
258 | return 33 - ttl; | |
259 | if (ttl < 63) | |
260 | return 63 - ttl; /* 62 = (64+60)/2 */ | |
261 | if (ttl < 65) | |
262 | return 65 - ttl; /* 62 = (64+60)/2 */ | |
263 | if (ttl < 129) | |
264 | return 129 - ttl; | |
265 | if (ttl < 193) | |
266 | return 193 - ttl; | |
267 | return 256 - ttl; | |
9d90e665 | 268 | } |
269 | ||
270 | static int | |
271 | pingerReadRequest(void) | |
272 | { | |
273 | static pingerEchoData pecho; | |
274 | int n; | |
275 | int guess_size; | |
e924600d | 276 | memset(&pecho, '\0', sizeof(pecho)); |
9d90e665 | 277 | n = recv(0, (char *) &pecho, sizeof(pecho), 0); |
429fdbec | 278 | if (n < 0) |
9d90e665 | 279 | return n; |
429fdbec | 280 | guess_size = n - (sizeof(pingerEchoData) - MAX_PKT_SZ); |
9d90e665 | 281 | if (guess_size != pecho.psize) |
282 | fprintf(stderr, "size mismatch, guess=%d psize=%d\n", | |
283 | guess_size, pecho.psize); | |
284 | pingerSendEcho(pecho.to, | |
285 | pecho.opcode, | |
286 | pecho.payload, | |
287 | pecho.psize); | |
288 | return n; | |
289 | } | |
290 | ||
291 | static void | |
292 | pingerSendtoSquid(pingerReplyData * preply) | |
293 | { | |
429fdbec | 294 | int len = sizeof(pingerReplyData) - MAX_PKT_SZ + preply->psize; |
d56d7f57 | 295 | if (send(1, (char *) preply, len, 0) < 0) { |
a3d5953d | 296 | debug(50, 0) ("pinger: send: %s\n", xstrerror()); |
365a4bce | 297 | exit(1); |
298 | } | |
9d90e665 | 299 | } |
300 | ||
301 | time_t | |
302 | getCurrentTime(void) | |
303 | { | |
304 | #if GETTIMEOFDAY_NO_TZP | |
305 | gettimeofday(¤t_time); | |
306 | #else | |
307 | gettimeofday(¤t_time, NULL); | |
308 | #endif | |
309 | return squid_curtime = current_time.tv_sec; | |
310 | } | |
311 | ||
312 | ||
313 | int | |
314 | main(int argc, char *argv[]) | |
315 | { | |
316 | fd_set R; | |
317 | int x; | |
365a4bce | 318 | struct timeval tv; |
0ee4272b | 319 | const char *debug_args = "ALL,1"; |
365a4bce | 320 | char *t; |
145766ef | 321 | time_t last_check_time = 0; |
365a4bce | 322 | |
323 | if ((t = getenv("SQUID_DEBUG"))) | |
324 | debug_args = xstrdup(t); | |
9d90e665 | 325 | getCurrentTime(); |
365a4bce | 326 | _db_init(NULL, debug_args); |
327 | ||
9d90e665 | 328 | pingerOpen(); |
329 | for (;;) { | |
145766ef | 330 | tv.tv_sec = 10; |
365a4bce | 331 | tv.tv_usec = 0; |
9d90e665 | 332 | FD_ZERO(&R); |
333 | FD_SET(0, &R); | |
334 | FD_SET(icmp_sock, &R); | |
365a4bce | 335 | x = select(icmp_sock + 1, &R, NULL, NULL, &tv); |
9d90e665 | 336 | getCurrentTime(); |
365a4bce | 337 | if (x < 0) |
9d90e665 | 338 | return 1; |
339 | if (FD_ISSET(0, &R)) | |
429fdbec | 340 | if (pingerReadRequest() < 0) { |
a3d5953d | 341 | debug(42, 0) ("Pinger exiting.\n"); |
9d90e665 | 342 | return 1; |
429fdbec | 343 | } |
9d90e665 | 344 | if (FD_ISSET(icmp_sock, &R)) |
345 | pingerRecv(); | |
eb647c47 | 346 | if (10 + last_check_time < squid_curtime) { |
d56d7f57 | 347 | if (send(1, (char *) &tv, 0, 0) < 0) |
f6c78bd2 | 348 | return 1; |
145766ef | 349 | last_check_time = squid_curtime; |
350 | } | |
9d90e665 | 351 | } |
145766ef | 352 | /* NOTREACHED */ |
9d90e665 | 353 | } |
365a4bce | 354 | |
9d90e665 | 355 | #else |
356 | #include <stdio.h> | |
357 | int | |
358 | main(int argc, char *argv[]) | |
359 | { | |
360 | fprintf(stderr, "%s: ICMP support not compiled in.\n", argv[0]); | |
361 | return 1; | |
362 | } | |
363 | #endif /* USE_ICMP */ |