… | |
… | |
26 | #include <sys/socket.h> |
26 | #include <sys/socket.h> |
27 | |
27 | |
28 | #include <netinet/in.h> |
28 | #include <netinet/in.h> |
29 | #include <arpa/inet.h> |
29 | #include <arpa/inet.h> |
30 | |
30 | |
|
|
31 | #ifdef __linux |
|
|
32 | # include <linux/icmp.h> |
|
|
33 | #endif |
|
|
34 | #if IPV6 |
|
|
35 | # include <netinet/icmp6.h> |
|
|
36 | #endif |
|
|
37 | |
31 | #define ICMP4_ECHO 8 |
38 | #define ICMP4_ECHO 8 |
|
|
39 | #define ICMP4_ECHO_REPLY 0 |
32 | #define ICMP6_ECHO 128 |
40 | #define ICMP6_ECHO 128 |
|
|
41 | #define ICMP6_ECHO_REPLY 129 |
33 | |
42 | |
34 | #define DRAIN_INTERVAL .000001 // how long to wait when sendto returns ENOBUFS, in seconds |
43 | #define DRAIN_INTERVAL .000001 // how long to wait when sendto returns ENOBUFS, in seconds |
35 | #define MIN_INTERVAL .000001 // minimum packet send interval, in seconds |
44 | #define MIN_INTERVAL .000001 // minimum packet send interval, in seconds |
36 | |
45 | |
37 | #define HDR_SIZE_IP4 20 |
46 | #define HDR_SIZE_IP4 20 |
… | |
… | |
57 | double interval; |
66 | double interval; |
58 | tstamp next; |
67 | tstamp next; |
59 | } RANGE; |
68 | } RANGE; |
60 | |
69 | |
61 | typedef struct { |
70 | typedef struct { |
62 | int send_fd; |
|
|
63 | SV *id; |
71 | SV *id; |
64 | double interval; |
72 | double interval; |
65 | int nranges; |
73 | int nranges; |
66 | RANGE *ranges; |
74 | RANGE *ranges; |
67 | uint32_t payload; |
75 | uint32_t payload; |
… | |
… | |
103 | uint16_t id, seq; |
111 | uint16_t id, seq; |
104 | uint32_t payload; |
112 | uint32_t payload; |
105 | tstamp stamp; // be careful when accessing this |
113 | tstamp stamp; // be careful when accessing this |
106 | } PKT; |
114 | } PKT; |
107 | |
115 | |
108 | pthread_t pthrid; |
116 | static pthread_t pthrid; |
109 | int thr_send[2]; // send to worker |
117 | static int thr_send[2]; // send to worker |
110 | int thr_recv[2]; // receive from worker |
118 | static int thr_recv[2]; // receive from worker |
111 | |
119 | |
112 | int icmp4_fd, icmp6_fd; |
120 | static int icmp4_fd, icmp6_fd; |
|
|
121 | |
|
|
122 | static AV *cbs; |
113 | |
123 | |
114 | static uint16_t |
124 | static uint16_t |
115 | icmp_cksum (void *data, unsigned int len) |
125 | icmp_cksum (void *data, unsigned int len) |
116 | { |
126 | { |
117 | register int sum = 0; |
127 | register int sum = 0; |
… | |
… | |
157 | |
167 | |
158 | memset (&pkt, 0, sizeof (pkt)); |
168 | memset (&pkt, 0, sizeof (pkt)); |
159 | |
169 | |
160 | memset (&sa4, 0, sizeof (sa4)); |
170 | memset (&sa4, 0, sizeof (sa4)); |
161 | sa4.sin_family = AF_INET; |
171 | sa4.sin_family = AF_INET; |
162 | sa4.sin_port = IPPROTO_ICMP; |
172 | sa4.sin_port = 0; |
163 | #if IPV6 |
173 | #if IPV6 |
164 | memset (&sa6, 0, sizeof (sa6)); |
174 | memset (&sa6, 0, sizeof (sa6)); |
165 | sa6.sin6_family = AF_INET6; |
175 | sa6.sin6_family = AF_INET6; |
166 | sa6.sin6_port = IPPROTO_ICMPV6; |
176 | sa6.sin6_port = 0; |
167 | #endif |
177 | #endif |
168 | |
178 | |
169 | for (;;) |
179 | for (;;) |
170 | { |
180 | { |
171 | REQ *req; |
181 | REQ *req; |
… | |
… | |
184 | pkt.code = 0; |
194 | pkt.code = 0; |
185 | pkt.id = (uint16_t)MAGIC; |
195 | pkt.id = (uint16_t)MAGIC; |
186 | pkt.seq = (uint16_t)~MAGIC; |
196 | pkt.seq = (uint16_t)~MAGIC; |
187 | pkt.payload = req->payload; |
197 | pkt.payload = req->payload; |
188 | |
198 | |
189 | tstamp next = NOW (); |
199 | tstamp now = NOW (); |
|
|
200 | tstamp next = now; |
|
|
201 | |
|
|
202 | { |
|
|
203 | int r; |
|
|
204 | for (r = req->nranges; r--; ) |
|
|
205 | inc_addr (&req->ranges [r].hi); |
|
|
206 | } |
190 | |
207 | |
191 | while (req->nranges) |
208 | while (req->nranges) |
192 | { |
209 | { |
193 | RANGE *range = req->ranges; |
210 | RANGE *range = req->ranges; |
194 | |
211 | |
195 | if (!memcmp (&range->lo, &range->hi, sizeof (addr_t))) |
212 | if (!memcmp (&range->lo, &range->hi, sizeof (addr_t))) |
196 | req->ranges [0] = req->ranges [--req->nranges]; |
213 | req->ranges [0] = req->ranges [--req->nranges]; |
197 | else |
214 | else |
198 | { |
215 | { |
199 | tstamp now = NOW (); |
|
|
200 | |
|
|
201 | // ranges [0] is always the next range to ping |
216 | // ranges [0] is always the next range to ping |
202 | tstamp wait = range->next - now; |
217 | tstamp wait = range->next - now; |
203 | |
218 | |
204 | // compare with the global frequency limit |
219 | // compare with the global frequency limit |
205 | { |
220 | { |
… | |
… | |
219 | ts.tv_nsec = (wait - ts.tv_sec) * 1000000000.; |
234 | ts.tv_nsec = (wait - ts.tv_sec) * 1000000000.; |
220 | |
235 | |
221 | nanosleep (&ts, 0); |
236 | nanosleep (&ts, 0); |
222 | } |
237 | } |
223 | |
238 | |
|
|
239 | now = NOW (); |
|
|
240 | |
224 | pkt.stamp = now; |
241 | pkt.stamp = now; |
225 | pkt.cksum = 0; |
242 | pkt.cksum = 0; |
226 | |
243 | |
227 | if (range->family == AF_INET) |
244 | if (range->family == AF_INET) |
228 | { |
245 | { |
229 | pkt.type = ICMP4_ECHO; |
246 | pkt.type = ICMP4_ECHO; |
230 | pkt.cksum = icmp_cksum (&pkt, sizeof (pkt));//D |
247 | pkt.cksum = icmp_cksum (&pkt, sizeof (pkt)); |
231 | |
248 | |
232 | memcpy (&sa4.sin_addr, |
249 | memcpy (&sa4.sin_addr, |
233 | sizeof (addr_t) - sizeof (sa4.sin_addr) + (char *)&range->lo, |
250 | sizeof (addr_t) - sizeof (sa4.sin_addr) + (char *)&range->lo, |
234 | sizeof (sa4.sin_addr)); |
251 | sizeof (sa4.sin_addr)); |
235 | |
252 | |
236 | if (sendto (req->send_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa4, sizeof (sa4)) > 0) |
253 | if (sendto (icmp4_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa4, sizeof (sa4)) > 0) |
237 | errno = 0; |
254 | errno = 0; |
238 | } |
255 | } |
239 | else |
256 | else |
240 | { |
257 | { |
241 | #if IPV6 |
258 | #if IPV6 |
… | |
… | |
243 | |
260 | |
244 | memcpy (&sa6.sin6_addr, |
261 | memcpy (&sa6.sin6_addr, |
245 | sizeof (addr_t) - sizeof (sa6.sin6_addr) + (char *)&range->lo, |
262 | sizeof (addr_t) - sizeof (sa6.sin6_addr) + (char *)&range->lo, |
246 | sizeof (sa6.sin6_addr)); |
263 | sizeof (sa6.sin6_addr)); |
247 | |
264 | |
248 | if (sendto (req->send_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa6, sizeof (sa6)) > 0) |
265 | if (sendto (icmp6_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa6, sizeof (sa6)) > 0) |
249 | errno = 0; |
266 | errno = 0; |
250 | #endif |
267 | #endif |
251 | } |
268 | } |
252 | |
269 | |
253 | if (errno == ENOBUFS) |
270 | if (errno == ENOBUFS) |
… | |
… | |
300 | |
317 | |
301 | return 0; |
318 | return 0; |
302 | } |
319 | } |
303 | |
320 | |
304 | static void |
321 | static void |
|
|
322 | feed_reply (AV *res_av) |
|
|
323 | { |
|
|
324 | if (av_len (res_av) < 0) |
|
|
325 | return; |
|
|
326 | |
|
|
327 | dSP; |
|
|
328 | SV *res = sv_2mortal (newRV_inc ((SV *)res_av)); |
|
|
329 | int i; |
|
|
330 | |
|
|
331 | ENTER; |
|
|
332 | SAVETMPS; |
|
|
333 | |
|
|
334 | for (i = av_len (cbs) + 1; i--; ) |
|
|
335 | { |
|
|
336 | SV *cb = *av_fetch (cbs, i, 1); |
|
|
337 | |
|
|
338 | PUSHMARK (SP); |
|
|
339 | XPUSHs (res); |
|
|
340 | PUTBACK; |
|
|
341 | call_sv (cb, G_DISCARD | G_VOID); |
|
|
342 | } |
|
|
343 | |
|
|
344 | FREETMPS; |
|
|
345 | LEAVE; |
|
|
346 | } |
|
|
347 | |
|
|
348 | static void |
305 | boot () |
349 | boot () |
306 | { |
350 | { |
307 | sigset_t fullsigset, oldsigset; |
351 | sigset_t fullsigset, oldsigset; |
308 | pthread_attr_t attr; |
352 | pthread_attr_t attr; |
309 | |
353 | |
… | |
… | |
312 | |
356 | |
313 | if (pipe (thr_recv) < 0) |
357 | if (pipe (thr_recv) < 0) |
314 | croak ("Net::FPing: unable to create receive pipe"); |
358 | croak ("Net::FPing: unable to create receive pipe"); |
315 | |
359 | |
316 | icmp4_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); |
360 | icmp4_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); |
317 | |
|
|
318 | #ifdef ICMP_FILTER |
361 | #ifdef ICMP_FILTER |
319 | { |
362 | { |
320 | icmp_filter oval; |
363 | struct icmp_filter oval; |
321 | oval.data = 0xffffffff & ~(1 << ICMP4_ECHO); |
364 | oval.data = 0xffffffff & ~(1 << ICMP4_ECHO_REPLY); |
322 | setsockopt (icmpv4_fd, SOL_RAW, ICMP_FILTER, &oval, sizeof oval); |
365 | setsockopt (icmp4_fd, SOL_RAW, ICMP_FILTER, &oval, sizeof oval); |
323 | } |
366 | } |
324 | #endif |
367 | #endif |
325 | |
368 | |
326 | #if IPV6 |
369 | #if IPV6 |
327 | icmp6_fd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); |
370 | icmp6_fd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); |
|
|
371 | # ifdef ICMP6_FILTER |
|
|
372 | { |
|
|
373 | struct icmp6_filter oval; |
|
|
374 | ICMP6_FILTER_SETBLOCKALL (&oval); |
|
|
375 | ICMP6_FILTER_SETPASS (ICMP6_ECHO_REPLY, &oval); |
|
|
376 | setsockopt (icmp6_fd, IPPROTO_ICMPV6, ICMP6_FILTER, &oval, sizeof oval); |
|
|
377 | } |
|
|
378 | # endif |
328 | #endif |
379 | #endif |
329 | |
380 | |
330 | pthread_attr_init (&attr); |
381 | pthread_attr_init (&attr); |
331 | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); |
382 | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); |
332 | #ifdef PTHREAD_SCOPE_PROCESS |
383 | #ifdef PTHREAD_SCOPE_PROCESS |
… | |
… | |
353 | |
404 | |
354 | BOOT: |
405 | BOOT: |
355 | { |
406 | { |
356 | HV *stash = gv_stashpv ("Net::FPing", 1); |
407 | HV *stash = gv_stashpv ("Net::FPing", 1); |
357 | |
408 | |
|
|
409 | cbs = get_av ("Net::FPing::CB", 1); |
|
|
410 | |
358 | boot (); |
411 | boot (); |
359 | |
412 | |
360 | newCONSTSUB (stash, "ipv4_supported", newSViv (icmp4_fd >= 0)); |
413 | newCONSTSUB (stash, "ipv4_supported", newSViv (icmp4_fd >= 0)); |
361 | newCONSTSUB (stash, "ipv6_supported", newSViv (icmp6_fd >= 0)); |
414 | newCONSTSUB (stash, "ipv6_supported", newSViv (icmp6_fd >= 0)); |
362 | |
415 | |
363 | newCONSTSUB (stash, "ipv4_pktsize", newSViv (HDR_SIZE_IP4 + sizeof (PKT))); |
416 | newCONSTSUB (stash, "icmp4_pktsize", newSViv (HDR_SIZE_IP4 + sizeof (PKT))); |
364 | newCONSTSUB (stash, "ipv6_pktsize", newSViv (HDR_SIZE_IP6 + sizeof (PKT))); |
417 | newCONSTSUB (stash, "icmp6_pktsize", newSViv (HDR_SIZE_IP6 + sizeof (PKT))); |
365 | } |
418 | } |
366 | |
419 | |
367 | PROTOTYPES: DISABLE |
420 | PROTOTYPES: DISABLE |
368 | |
421 | |
369 | SV * |
422 | SV * |
… | |
… | |
380 | int i; |
433 | int i; |
381 | |
434 | |
382 | if (interval < MIN_INTERVAL) |
435 | if (interval < MIN_INTERVAL) |
383 | interval = MIN_INTERVAL; |
436 | interval = MIN_INTERVAL; |
384 | |
437 | |
385 | req->send_fd = icmp4_fd; |
|
|
386 | req->id = newSVsv (id); |
438 | req->id = newSVsv (id); |
387 | req->interval = interval; |
439 | req->interval = interval; |
388 | req->payload = payload; |
440 | req->payload = payload; |
389 | req->nranges = nranges; |
441 | req->nranges = nranges; |
390 | req->ranges = (RANGE *)malloc (nranges * sizeof (RANGE)); |
442 | req->ranges = (RANGE *)malloc (nranges * sizeof (RANGE)); |
… | |
… | |
441 | addr = htonl (SvUV (hi)); memcpy (sizeof (addr_t) - 4 + (char *)&r->hi, &addr, 4); |
493 | addr = htonl (SvUV (hi)); memcpy (sizeof (addr_t) - 4 + (char *)&r->hi, &addr, 4); |
442 | } |
494 | } |
443 | else |
495 | else |
444 | croak ("addresses in range must be strings with either 4 (IPv4) or 16 (IPv6) octets"); |
496 | croak ("addresses in range must be strings with either 4 (IPv4) or 16 (IPv6) octets"); |
445 | |
497 | |
446 | if (r->family = AF_INET) |
498 | if (r->family == AF_INET) |
447 | { |
499 | { |
448 | if (icmp4_fd < 0) |
500 | if (icmp4_fd < 0) |
449 | croak ("Net::FPing: IPv4 ping support not available on this system"); |
501 | croak ("Net::FPing: IPv4 ping support not available on this system"); |
450 | } |
502 | } |
451 | else |
503 | else |
… | |
… | |
490 | char buf [512]; |
542 | char buf [512]; |
491 | struct sockaddr_in sa; |
543 | struct sockaddr_in sa; |
492 | socklen_t sl = sizeof (sa); |
544 | socklen_t sl = sizeof (sa); |
493 | AV *res_av = newAV (); |
545 | AV *res_av = newAV (); |
494 | SV *res_rv = sv_2mortal (newRV_noinc ((SV *)res_av)); |
546 | SV *res_rv = sv_2mortal (newRV_noinc ((SV *)res_av)); |
|
|
547 | tstamp now = NOW (); |
495 | |
548 | |
496 | for (;;) |
549 | for (;;) |
497 | { |
550 | { |
498 | int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl); |
551 | int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl); |
499 | |
552 | |
… | |
… | |
511 | || hdrlen < HDR_SIZE_IP4 || hdrlen + sizeof (PKT) != totlen) |
564 | || hdrlen < HDR_SIZE_IP4 || hdrlen + sizeof (PKT) != totlen) |
512 | continue; |
565 | continue; |
513 | |
566 | |
514 | PKT *pkt = (PKT *)(buf + hdrlen); |
567 | PKT *pkt = (PKT *)(buf + hdrlen); |
515 | |
568 | |
|
|
569 | if (pkt->type != ICMP4_ECHO_REPLY |
516 | if (pkt->id != (uint16_t)MAGIC |
570 | || pkt->id != (uint16_t) MAGIC |
517 | || pkt->seq != (uint16_t)~MAGIC |
571 | || pkt->seq != (uint16_t)~MAGIC |
518 | || pkt->type != ICMP4_ECHO |
|
|
519 | || !isnormal (pkt->stamp)) |
572 | || !isnormal (pkt->stamp)) |
520 | continue; |
573 | continue; |
521 | |
574 | |
522 | // just drain for now |
575 | AV *av = newAV (); |
523 | //av_push |
576 | av_push (av, newSVpvn ((char *)&sa.sin_addr, 4)); |
|
|
577 | av_push (av, newSVnv (now - pkt->stamp)); |
|
|
578 | av_push (av, newSVuv (pkt->payload)); |
|
|
579 | |
|
|
580 | av_push (res_av, newRV_noinc ((SV *)av)); |
524 | } |
581 | } |
525 | |
582 | |
526 | // feed (res_av); |
583 | feed_reply (res_av); |
527 | } |
584 | } |
528 | |
585 | |
529 | void |
586 | void |
530 | _recv_icmp6 (...) |
587 | _recv_icmp6 (...) |
531 | CODE: |
588 | CODE: |
532 | { |
589 | { |
533 | char buf [512]; |
|
|
534 | struct sockaddr_in sa; |
590 | struct sockaddr_in6 sa; |
535 | socklen_t sl = sizeof (sa); |
591 | socklen_t sl = sizeof (sa); |
536 | AV *res_av = (AV *)sv_2mortal ((SV *)newAV ()); |
592 | AV *res_av = (AV *)sv_2mortal ((SV *)newAV ()); |
|
|
593 | PKT pkt; |
|
|
594 | tstamp now = NOW (); |
537 | |
595 | |
538 | for (;;) |
596 | for (;;) |
539 | { |
597 | { |
540 | int len = recvfrom (icmp6_fd, buf, sizeof (buf), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl); |
598 | int len = recvfrom (icmp6_fd, &pkt, sizeof (pkt), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl); |
541 | |
599 | |
542 | if (len <= HDR_SIZE_IP6) |
600 | if (len != sizeof (PKT)) |
543 | break; |
601 | break; |
544 | |
602 | |
545 | IP6HDR *iphdr = (IP6HDR *)buf; |
603 | if (pkt.type != ICMP6_ECHO_REPLY |
546 | |
604 | || pkt.id != (uint16_t) MAGIC |
547 | int datalen = ntohs (iphdr->payload_len); |
605 | || pkt.seq != (uint16_t)~MAGIC |
548 | |
606 | || !isnormal (pkt.stamp)) |
549 | // packet corrupt? |
|
|
550 | if (HDR_SIZE_IP6 + datalen > len |
|
|
551 | || iphdr->nxt_hdr != IPPROTO_ICMPV6 |
|
|
552 | || HDR_SIZE_IP6 + sizeof (PKT) != datalen) |
|
|
553 | continue; |
607 | continue; |
554 | |
608 | |
555 | PKT *pkt = (PKT *)(buf + HDR_SIZE_IP6); |
609 | AV *av = newAV (); |
|
|
610 | av_push (av, newSVpvn ((char *)&sa.sin6_addr, 16)); |
|
|
611 | av_push (av, newSVnv (now - pkt.stamp)); |
|
|
612 | av_push (av, newSVuv (pkt.payload)); |
556 | |
613 | |
557 | if (pkt->id != (uint16_t)MAGIC |
614 | av_push (res_av, newRV_noinc ((SV *)av)); |
558 | || pkt->seq != (uint16_t)~MAGIC |
|
|
559 | || pkt->type != ICMP6_ECHO |
|
|
560 | || !isnormal (pkt->stamp)) |
|
|
561 | continue; |
|
|
562 | |
|
|
563 | //fprintf (stderr, "ip6 echo received\n"); |
|
|
564 | // just drain for now |
|
|
565 | //av_push |
|
|
566 | } |
615 | } |
567 | |
616 | |
568 | // feed (res_av); |
617 | feed_reply (res_av); |
569 | } |
618 | } |
570 | |
619 | |