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