… | |
… | |
57 | double interval; |
57 | double interval; |
58 | tstamp next; |
58 | tstamp next; |
59 | } RANGE; |
59 | } RANGE; |
60 | |
60 | |
61 | typedef struct { |
61 | typedef struct { |
62 | int send_fd; |
|
|
63 | SV *id; |
62 | SV *id; |
64 | double interval; |
63 | double interval; |
65 | int nranges; |
64 | int nranges; |
66 | RANGE *ranges; |
65 | RANGE *ranges; |
67 | uint32_t payload; |
66 | uint32_t payload; |
… | |
… | |
103 | uint16_t id, seq; |
102 | uint16_t id, seq; |
104 | uint32_t payload; |
103 | uint32_t payload; |
105 | tstamp stamp; // be careful when accessing this |
104 | tstamp stamp; // be careful when accessing this |
106 | } PKT; |
105 | } PKT; |
107 | |
106 | |
108 | pthread_t pthrid; |
107 | static pthread_t pthrid; |
109 | int thr_send[2]; // send to worker |
108 | static int thr_send[2]; // send to worker |
110 | int thr_recv[2]; // receive from worker |
109 | static int thr_recv[2]; // receive from worker |
111 | |
110 | |
112 | int icmp4_fd, icmp6_fd; |
111 | static int icmp4_fd, icmp6_fd; |
|
|
112 | |
|
|
113 | static AV *cbs; |
113 | |
114 | |
114 | static uint16_t |
115 | static uint16_t |
115 | icmp_cksum (void *data, unsigned int len) |
116 | icmp_cksum (void *data, unsigned int len) |
116 | { |
117 | { |
117 | register int sum = 0; |
118 | register int sum = 0; |
… | |
… | |
157 | |
158 | |
158 | memset (&pkt, 0, sizeof (pkt)); |
159 | memset (&pkt, 0, sizeof (pkt)); |
159 | |
160 | |
160 | memset (&sa4, 0, sizeof (sa4)); |
161 | memset (&sa4, 0, sizeof (sa4)); |
161 | sa4.sin_family = AF_INET; |
162 | sa4.sin_family = AF_INET; |
162 | sa4.sin_port = IPPROTO_ICMP; |
163 | sa4.sin_port = 0; |
163 | #if IPV6 |
164 | #if IPV6 |
164 | memset (&sa6, 0, sizeof (sa6)); |
165 | memset (&sa6, 0, sizeof (sa6)); |
165 | sa6.sin6_family = AF_INET6; |
166 | sa6.sin6_family = AF_INET6; |
166 | sa6.sin6_port = IPPROTO_ICMPV6; |
167 | sa6.sin6_port = 0; |
167 | #endif |
168 | #endif |
168 | |
169 | |
169 | for (;;) |
170 | for (;;) |
170 | { |
171 | { |
171 | REQ *req; |
172 | REQ *req; |
… | |
… | |
225 | pkt.cksum = 0; |
226 | pkt.cksum = 0; |
226 | |
227 | |
227 | if (range->family == AF_INET) |
228 | if (range->family == AF_INET) |
228 | { |
229 | { |
229 | pkt.type = ICMP4_ECHO; |
230 | pkt.type = ICMP4_ECHO; |
230 | pkt.cksum = icmp_cksum (&pkt, sizeof (pkt));//D |
231 | pkt.cksum = icmp_cksum (&pkt, sizeof (pkt)); |
231 | |
232 | |
232 | memcpy (&sa4.sin_addr, |
233 | memcpy (&sa4.sin_addr, |
233 | sizeof (addr_t) - sizeof (sa4.sin_addr) + (char *)&range->lo, |
234 | sizeof (addr_t) - sizeof (sa4.sin_addr) + (char *)&range->lo, |
234 | sizeof (sa4.sin_addr)); |
235 | sizeof (sa4.sin_addr)); |
235 | |
236 | |
236 | if (sendto (req->send_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa4, sizeof (sa4)) > 0) |
237 | if (sendto (icmp4_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa4, sizeof (sa4)) > 0) |
237 | errno = 0; |
238 | errno = 0; |
238 | } |
239 | } |
239 | else |
240 | else |
240 | { |
241 | { |
241 | #if IPV6 |
242 | #if IPV6 |
… | |
… | |
243 | |
244 | |
244 | memcpy (&sa6.sin6_addr, |
245 | memcpy (&sa6.sin6_addr, |
245 | sizeof (addr_t) - sizeof (sa6.sin6_addr) + (char *)&range->lo, |
246 | sizeof (addr_t) - sizeof (sa6.sin6_addr) + (char *)&range->lo, |
246 | sizeof (sa6.sin6_addr)); |
247 | sizeof (sa6.sin6_addr)); |
247 | |
248 | |
248 | if (sendto (req->send_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa6, sizeof (sa6)) > 0) |
249 | if (sendto (icmp6_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa6, sizeof (sa6)) > 0) |
249 | errno = 0; |
250 | errno = 0; |
250 | #endif |
251 | #endif |
251 | } |
252 | } |
252 | |
253 | |
253 | if (errno == ENOBUFS) |
254 | if (errno == ENOBUFS) |
… | |
… | |
300 | |
301 | |
301 | return 0; |
302 | return 0; |
302 | } |
303 | } |
303 | |
304 | |
304 | static void |
305 | static void |
|
|
306 | feed_reply (AV *res_av) |
|
|
307 | { |
|
|
308 | if (av_len (res_av) < 0) |
|
|
309 | return; |
|
|
310 | |
|
|
311 | dSP; |
|
|
312 | SV *res = sv_2mortal (newRV_inc ((SV *)res_av)); |
|
|
313 | int i; |
|
|
314 | |
|
|
315 | ENTER; |
|
|
316 | SAVETMPS; |
|
|
317 | |
|
|
318 | for (i = av_len (cbs) + 1; i--; ) |
|
|
319 | { |
|
|
320 | SV *cb = *av_fetch (cbs, i, 1); |
|
|
321 | |
|
|
322 | PUSHMARK (SP); |
|
|
323 | XPUSHs (res); |
|
|
324 | PUTBACK; |
|
|
325 | call_sv (cb, G_DISCARD | G_VOID); |
|
|
326 | } |
|
|
327 | |
|
|
328 | FREETMPS; |
|
|
329 | LEAVE; |
|
|
330 | } |
|
|
331 | |
|
|
332 | static void |
305 | boot () |
333 | boot () |
306 | { |
334 | { |
307 | sigset_t fullsigset, oldsigset; |
335 | sigset_t fullsigset, oldsigset; |
308 | pthread_attr_t attr; |
336 | pthread_attr_t attr; |
309 | |
337 | |
… | |
… | |
312 | |
340 | |
313 | if (pipe (thr_recv) < 0) |
341 | if (pipe (thr_recv) < 0) |
314 | croak ("Net::FPing: unable to create receive pipe"); |
342 | croak ("Net::FPing: unable to create receive pipe"); |
315 | |
343 | |
316 | icmp4_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); |
344 | icmp4_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); |
|
|
345 | #if IPV6 |
|
|
346 | icmp6_fd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); |
|
|
347 | #endif |
317 | |
348 | |
318 | #ifdef ICMP_FILTER |
349 | #ifdef ICMP_FILTER |
319 | { |
350 | { |
320 | icmp_filter oval; |
351 | icmp_filter oval; |
321 | oval.data = 0xffffffff & ~(1 << ICMP4_ECHO); |
352 | oval.data = 0xffffffff & ~(1 << ICMP4_ECHO); |
322 | setsockopt (icmpv4_fd, SOL_RAW, ICMP_FILTER, &oval, sizeof oval); |
353 | setsockopt (icmpv4_fd, SOL_RAW, ICMP_FILTER, &oval, sizeof oval); |
323 | } |
354 | } |
324 | #endif |
355 | #endif |
325 | |
356 | |
326 | #if IPV6 |
|
|
327 | icmp6_fd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); |
|
|
328 | #endif |
|
|
329 | |
|
|
330 | pthread_attr_init (&attr); |
357 | pthread_attr_init (&attr); |
331 | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); |
358 | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); |
332 | #ifdef PTHREAD_SCOPE_PROCESS |
359 | #ifdef PTHREAD_SCOPE_PROCESS |
333 | pthread_attr_setscope (&attr, PTHREAD_SCOPE_PROCESS); |
360 | pthread_attr_setscope (&attr, PTHREAD_SCOPE_PROCESS); |
334 | #endif |
361 | #endif |
… | |
… | |
353 | |
380 | |
354 | BOOT: |
381 | BOOT: |
355 | { |
382 | { |
356 | HV *stash = gv_stashpv ("Net::FPing", 1); |
383 | HV *stash = gv_stashpv ("Net::FPing", 1); |
357 | |
384 | |
|
|
385 | cbs = get_av ("Net::FPing::CB", 1); |
|
|
386 | |
358 | boot (); |
387 | boot (); |
359 | |
388 | |
360 | newCONSTSUB (stash, "ipv4_supported", newSViv (icmp4_fd >= 0)); |
389 | newCONSTSUB (stash, "ipv4_supported", newSViv (icmp4_fd >= 0)); |
361 | newCONSTSUB (stash, "ipv6_supported", newSViv (icmp6_fd >= 0)); |
390 | newCONSTSUB (stash, "ipv6_supported", newSViv (icmp6_fd >= 0)); |
362 | |
391 | |
… | |
… | |
380 | int i; |
409 | int i; |
381 | |
410 | |
382 | if (interval < MIN_INTERVAL) |
411 | if (interval < MIN_INTERVAL) |
383 | interval = MIN_INTERVAL; |
412 | interval = MIN_INTERVAL; |
384 | |
413 | |
385 | req->send_fd = icmp4_fd; |
|
|
386 | req->id = newSVsv (id); |
414 | req->id = newSVsv (id); |
387 | req->interval = interval; |
415 | req->interval = interval; |
388 | req->payload = payload; |
416 | req->payload = payload; |
389 | req->nranges = nranges; |
417 | req->nranges = nranges; |
390 | req->ranges = (RANGE *)malloc (nranges * sizeof (RANGE)); |
418 | req->ranges = (RANGE *)malloc (nranges * sizeof (RANGE)); |
… | |
… | |
441 | addr = htonl (SvUV (hi)); memcpy (sizeof (addr_t) - 4 + (char *)&r->hi, &addr, 4); |
469 | addr = htonl (SvUV (hi)); memcpy (sizeof (addr_t) - 4 + (char *)&r->hi, &addr, 4); |
442 | } |
470 | } |
443 | else |
471 | else |
444 | croak ("addresses in range must be strings with either 4 (IPv4) or 16 (IPv6) octets"); |
472 | croak ("addresses in range must be strings with either 4 (IPv4) or 16 (IPv6) octets"); |
445 | |
473 | |
446 | if (r->family = AF_INET) |
474 | if (r->family == AF_INET) |
447 | { |
475 | { |
448 | if (icmp4_fd < 0) |
476 | if (icmp4_fd < 0) |
449 | croak ("Net::FPing: IPv4 ping support not available on this system"); |
477 | croak ("Net::FPing: IPv4 ping support not available on this system"); |
450 | } |
478 | } |
451 | else |
479 | else |
… | |
… | |
490 | char buf [512]; |
518 | char buf [512]; |
491 | struct sockaddr_in sa; |
519 | struct sockaddr_in sa; |
492 | socklen_t sl = sizeof (sa); |
520 | socklen_t sl = sizeof (sa); |
493 | AV *res_av = newAV (); |
521 | AV *res_av = newAV (); |
494 | SV *res_rv = sv_2mortal (newRV_noinc ((SV *)res_av)); |
522 | SV *res_rv = sv_2mortal (newRV_noinc ((SV *)res_av)); |
|
|
523 | tstamp now = NOW (); |
495 | |
524 | |
496 | for (;;) |
525 | for (;;) |
497 | { |
526 | { |
498 | int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl); |
527 | int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl); |
499 | |
528 | |
… | |
… | |
517 | || pkt->seq != (uint16_t)~MAGIC |
546 | || pkt->seq != (uint16_t)~MAGIC |
518 | || pkt->type != ICMP4_ECHO |
547 | || pkt->type != ICMP4_ECHO |
519 | || !isnormal (pkt->stamp)) |
548 | || !isnormal (pkt->stamp)) |
520 | continue; |
549 | continue; |
521 | |
550 | |
522 | // just drain for now |
551 | AV *av = newAV (); |
523 | //av_push |
552 | av_push (av, newSVpvn ((char *)&sa.sin_addr, 4)); |
|
|
553 | av_push (av, newSVnv (now - pkt->stamp)); |
|
|
554 | av_push (av, newSVuv (pkt->payload)); |
|
|
555 | |
|
|
556 | av_push (res_av, newRV_noinc ((SV *)av)); |
524 | } |
557 | } |
525 | |
558 | |
526 | // feed (res_av); |
559 | feed_reply (res_av); |
527 | } |
560 | } |
528 | |
561 | |
529 | void |
562 | void |
530 | _recv_icmp6 (...) |
563 | _recv_icmp6 (...) |
531 | CODE: |
564 | CODE: |
532 | { |
565 | { |
533 | char buf [512]; |
|
|
534 | struct sockaddr_in sa; |
566 | struct sockaddr_in6 sa; |
535 | socklen_t sl = sizeof (sa); |
567 | socklen_t sl = sizeof (sa); |
536 | AV *res_av = (AV *)sv_2mortal ((SV *)newAV ()); |
568 | AV *res_av = (AV *)sv_2mortal ((SV *)newAV ()); |
|
|
569 | PKT pkt; |
|
|
570 | tstamp now = NOW (); |
537 | |
571 | |
538 | for (;;) |
572 | for (;;) |
539 | { |
573 | { |
540 | int len = recvfrom (icmp6_fd, buf, sizeof (buf), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl); |
574 | int len = recvfrom (icmp6_fd, &pkt, sizeof (pkt), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl); |
541 | |
575 | |
542 | if (len <= HDR_SIZE_IP6) |
576 | if (len != sizeof (PKT)) |
543 | break; |
577 | break; |
544 | |
578 | |
545 | IP6HDR *iphdr = (IP6HDR *)buf; |
579 | if (pkt.type != ICMP6_ECHO |
546 | |
580 | || pkt.id != (uint16_t)MAGIC |
547 | int datalen = ntohs (iphdr->payload_len); |
581 | || pkt.seq != (uint16_t)~MAGIC |
548 | |
582 | || !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; |
583 | continue; |
554 | |
584 | |
555 | PKT *pkt = (PKT *)(buf + HDR_SIZE_IP6); |
585 | AV *av = newAV (); |
|
|
586 | av_push (av, newSVpvn ((char *)&sa.sin6_addr, 16)); |
|
|
587 | av_push (av, newSVnv (now - pkt.stamp)); |
|
|
588 | av_push (av, newSVuv (pkt.payload)); |
556 | |
589 | |
557 | if (pkt->id != (uint16_t)MAGIC |
590 | 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 | } |
591 | } |
567 | |
592 | |
568 | // feed (res_av); |
593 | feed_reply (res_av); |
569 | } |
594 | } |
570 | |
595 | |