1 | #define _POSIX_C_SOURCE 199309 |
1 | #define _POSIX_C_SOURCE 199309 |
2 | #define _GNU_SOURCE 1 |
2 | #define _GNU_SOURCE 1 |
3 | |
3 | |
4 | #define IPV6 1 |
4 | #define IPV6 1 // if you get compilation problems try to disable IPv6 |
5 | |
5 | |
6 | #include "EXTERN.h" |
6 | #include "EXTERN.h" |
7 | #include "perl.h" |
7 | #include "perl.h" |
8 | #include "XSUB.h" |
8 | #include "XSUB.h" |
9 | |
9 | |
… | |
… | |
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; |
… | |
… | |
99 | |
98 | |
100 | typedef struct { |
99 | typedef struct { |
101 | uint8_t type, code; |
100 | uint8_t type, code; |
102 | uint16_t cksum; |
101 | uint16_t cksum; |
103 | uint16_t id, seq; |
102 | uint16_t id, seq; |
|
|
103 | uint32_t payload; |
104 | tstamp stamp; // be careful when accessing this |
104 | tstamp stamp; // be careful when accessing this |
105 | uint32_t payload; |
|
|
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; |
113 | |
112 | |
114 | uint16_t |
113 | static AV *cbs; |
|
|
114 | |
|
|
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; |
118 | uint16_t *wp; |
119 | uint16_t *wp; |
119 | |
120 | |
… | |
… | |
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 = 0; // unused |
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 = 0; // unused |
167 | sa6.sin6_port = 0; |
167 | #endif |
168 | #endif |
168 | |
169 | |
169 | for (;;) |
170 | for (;;) |
170 | { |
171 | { |
171 | REQ *req; |
172 | REQ *req; |
… | |
… | |
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 | |
… | |
… | |
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); |
317 | #if IPV6 |
345 | #if IPV6 |
318 | icmp6_fd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); |
346 | icmp6_fd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); |
|
|
347 | #endif |
|
|
348 | |
|
|
349 | #ifdef ICMP_FILTER |
|
|
350 | { |
|
|
351 | icmp_filter oval; |
|
|
352 | oval.data = 0xffffffff & ~(1 << ICMP4_ECHO); |
|
|
353 | setsockopt (icmpv4_fd, SOL_RAW, ICMP_FILTER, &oval, sizeof oval); |
|
|
354 | } |
319 | #endif |
355 | #endif |
320 | |
356 | |
321 | pthread_attr_init (&attr); |
357 | pthread_attr_init (&attr); |
322 | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); |
358 | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); |
323 | #ifdef PTHREAD_SCOPE_PROCESS |
359 | #ifdef PTHREAD_SCOPE_PROCESS |
… | |
… | |
341 | } |
377 | } |
342 | |
378 | |
343 | MODULE = Net::FPing PACKAGE = Net::FPing |
379 | MODULE = Net::FPing PACKAGE = Net::FPing |
344 | |
380 | |
345 | BOOT: |
381 | BOOT: |
|
|
382 | { |
|
|
383 | HV *stash = gv_stashpv ("Net::FPing", 1); |
|
|
384 | |
|
|
385 | cbs = get_av ("Net::FPing::CB", 1); |
|
|
386 | |
346 | boot (); |
387 | boot (); |
347 | |
388 | |
|
|
389 | newCONSTSUB (stash, "ipv4_supported", newSViv (icmp4_fd >= 0)); |
|
|
390 | newCONSTSUB (stash, "ipv6_supported", newSViv (icmp6_fd >= 0)); |
|
|
391 | |
|
|
392 | newCONSTSUB (stash, "ipv4_pktsize", newSViv (HDR_SIZE_IP4 + sizeof (PKT))); |
|
|
393 | newCONSTSUB (stash, "ipv6_pktsize", newSViv (HDR_SIZE_IP6 + sizeof (PKT))); |
|
|
394 | } |
|
|
395 | |
348 | PROTOTYPES: DISABLE |
396 | PROTOTYPES: DISABLE |
349 | |
397 | |
350 | SV * |
398 | SV * |
351 | _req_ranges4 (SV *ranges, NV interval, U32 payload, SV *id) |
399 | _req_icmp_ping (SV *ranges, NV interval, U32 payload, SV *id) |
352 | CODE: |
400 | CODE: |
353 | { |
401 | { |
354 | if (!SvROK (ranges) || SvTYPE (SvRV (ranges)) != SVt_PVAV) |
402 | if (!SvROK (ranges) || SvTYPE (SvRV (ranges)) != SVt_PVAV) |
355 | croak ("address ranges must be given as arrayref with lo, hi pairs"); |
403 | croak ("address ranges must be given as arrayref with lo, hi pairs"); |
356 | |
404 | |
… | |
… | |
361 | int i; |
409 | int i; |
362 | |
410 | |
363 | if (interval < MIN_INTERVAL) |
411 | if (interval < MIN_INTERVAL) |
364 | interval = MIN_INTERVAL; |
412 | interval = MIN_INTERVAL; |
365 | |
413 | |
366 | req->send_fd = icmp4_fd; |
|
|
367 | req->id = newSVsv (id); |
414 | req->id = newSVsv (id); |
368 | req->interval = interval; |
415 | req->interval = interval; |
369 | req->payload = payload; |
416 | req->payload = payload; |
370 | req->nranges = nranges; |
417 | req->nranges = nranges; |
371 | req->ranges = (RANGE *)malloc (nranges * sizeof (RANGE)); |
418 | req->ranges = (RANGE *)malloc (nranges * sizeof (RANGE)); |
… | |
… | |
422 | 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); |
423 | } |
470 | } |
424 | else |
471 | else |
425 | 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"); |
426 | |
473 | |
427 | if (r->family = AF_INET) |
474 | if (r->family == AF_INET) |
428 | { |
475 | { |
429 | if (icmp4_fd < 0) |
476 | if (icmp4_fd < 0) |
430 | croak ("Net::FPing: IPv4 ping support not available on this system"); |
477 | croak ("Net::FPing: IPv4 ping support not available on this system"); |
431 | } |
478 | } |
432 | else |
479 | else |
… | |
… | |
471 | char buf [512]; |
518 | char buf [512]; |
472 | struct sockaddr_in sa; |
519 | struct sockaddr_in sa; |
473 | socklen_t sl = sizeof (sa); |
520 | socklen_t sl = sizeof (sa); |
474 | AV *res_av = newAV (); |
521 | AV *res_av = newAV (); |
475 | 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 (); |
476 | |
524 | |
477 | for (;;) |
525 | for (;;) |
478 | { |
526 | { |
479 | 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); |
480 | |
528 | |
… | |
… | |
498 | || pkt->seq != (uint16_t)~MAGIC |
546 | || pkt->seq != (uint16_t)~MAGIC |
499 | || pkt->type != ICMP4_ECHO |
547 | || pkt->type != ICMP4_ECHO |
500 | || !isnormal (pkt->stamp)) |
548 | || !isnormal (pkt->stamp)) |
501 | continue; |
549 | continue; |
502 | |
550 | |
503 | // just drain for now |
551 | AV *av = newAV (); |
504 | //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)); |
505 | } |
557 | } |
506 | |
558 | |
507 | // feed (res_av); |
559 | feed_reply (res_av); |
508 | } |
560 | } |
509 | |
561 | |
510 | void |
562 | void |
511 | _recv_icmp6 (...) |
563 | _recv_icmp6 (...) |
512 | CODE: |
564 | CODE: |
513 | { |
565 | { |
514 | char buf [512]; |
|
|
515 | struct sockaddr_in sa; |
566 | struct sockaddr_in6 sa; |
516 | socklen_t sl = sizeof (sa); |
567 | socklen_t sl = sizeof (sa); |
517 | AV *res_av = (AV *)sv_2mortal ((SV *)newAV ()); |
568 | AV *res_av = (AV *)sv_2mortal ((SV *)newAV ()); |
|
|
569 | PKT pkt; |
|
|
570 | tstamp now = NOW (); |
518 | |
571 | |
519 | for (;;) |
572 | for (;;) |
520 | { |
573 | { |
521 | 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); |
522 | |
575 | |
523 | if (len <= HDR_SIZE_IP6) |
576 | if (len != sizeof (PKT)) |
524 | break; |
577 | break; |
525 | |
578 | |
526 | IP6HDR *iphdr = (IP6HDR *)buf; |
579 | if (pkt.type != ICMP6_ECHO |
527 | |
580 | || pkt.id != (uint16_t)MAGIC |
528 | int datalen = ntohs (iphdr->payload_len); |
581 | || pkt.seq != (uint16_t)~MAGIC |
529 | |
582 | || !isnormal (pkt.stamp)) |
530 | // packet corrupt? |
|
|
531 | if (HDR_SIZE_IP6 + datalen > len |
|
|
532 | || iphdr->nxt_hdr != IPPROTO_ICMPV6 |
|
|
533 | || HDR_SIZE_IP6 + sizeof (PKT) != datalen) |
|
|
534 | continue; |
583 | continue; |
535 | |
584 | |
536 | 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)); |
537 | |
589 | |
538 | if (pkt->id != (uint16_t)MAGIC |
590 | av_push (res_av, newRV_noinc ((SV *)av)); |
539 | || pkt->seq != (uint16_t)~MAGIC |
|
|
540 | || pkt->type != ICMP6_ECHO |
|
|
541 | || !isnormal (pkt->stamp)) |
|
|
542 | continue; |
|
|
543 | |
|
|
544 | //fprintf (stderr, "ip6 echo received\n"); |
|
|
545 | // just drain for now |
|
|
546 | //av_push |
|
|
547 | } |
591 | } |
548 | |
592 | |
549 | // feed (res_av); |
593 | feed_reply (res_av); |
550 | } |
594 | } |
551 | |
595 | |