ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-FastPing/FastPing.xs
(Generate patch)

Comparing AnyEvent-FastPing/FastPing.xs (file contents):
Revision 1.11 by root, Tue Feb 1 04:06:24 2011 UTC vs.
Revision 1.15 by root, Sat Nov 12 01:20:46 2016 UTC

18#include <poll.h> 18#include <poll.h>
19#include <unistd.h> 19#include <unistd.h>
20#include <inttypes.h> 20#include <inttypes.h>
21#include <fcntl.h> 21#include <fcntl.h>
22#include <errno.h> 22#include <errno.h>
23#include <limits.h>
23 24
24#include <sys/types.h> 25#include <sys/types.h>
25#include <sys/time.h> 26#include <sys/time.h>
26#include <sys/socket.h> 27#include <sys/socket.h>
27 28
110typedef uint8_t addr_tt[16]; 111typedef uint8_t addr_tt[16];
111 112
112typedef struct 113typedef struct
113{ 114{
114 tstamp next; 115 tstamp next;
115 double interval; 116 tstamp interval;
116 int addrlen; 117 int addrlen;
117 118
118 addr_tt lo, hi; /* only if !addrcnt */ 119 addr_tt lo, hi; /* only if !addrcnt */
119 120
120 int addrcnt; 121 int addrcnt;
124typedef struct 125typedef struct
125{ 126{
126 RANGE **ranges; 127 RANGE **ranges;
127 int rangecnt, rangemax; 128 int rangecnt, rangemax;
128 129
130 tstamp next;
129 tstamp interval; 131 tstamp interval;
132
130 tstamp maxrtt; 133 tstamp maxrtt;
134
131 uint16_t magic1; 135 uint16_t magic1;
132 uint16_t magic2; 136 uint16_t magic2;
133 uint16_t magic3; 137 uint16_t magic3;
134 138
135 int id; 139 int id;
193} 197}
194 198
195static void 199static void
196pkt_cksum (PKT *pkt) 200pkt_cksum (PKT *pkt)
197{ 201{
198 uint_fast32_t sum = -pkt->cksum; 202 uint_fast32_t sum = 0;
199 uint32_t *wp = (uint32_t *)pkt; 203 uint32_t *wp = (uint32_t *)pkt;
200 int len = sizeof (*pkt) / 4; 204 int len = sizeof (*pkt) / 4;
201 205
202 do 206 do
203 { 207 {
204 uint_fast32_t w = *(volatile uint32_t *)wp++; 208 uint_fast32_t w = *(volatile uint32_t *)wp++;
205 sum += (w & 0xffff) + (w >> 16); 209 sum += (w & 0xffff) + (w >> 16);
206 } 210 }
207 while (len--); 211 while (--len);
208 212
209 sum = (sum >> 16) + (sum & 0xffff); /* add high 16 to low 16 */ 213 sum = (sum >> 16) + (sum & 0xffff); /* add high 16 to low 16 */
210 sum += sum >> 16; /* add carry */ 214 sum += sum >> 16; /* add carry */
211 215
212 pkt->cksum = ~sum; 216 pkt->cksum = ~sum;
234static int 238static int
235range_send_ping (RANGE *self, PKT *pkt) 239range_send_ping (RANGE *self, PKT *pkt)
236{ 240{
237 // send ping 241 // send ping
238 uint8_t *addr; 242 uint8_t *addr;
243 int addrlen;
239 244
240 if (self->addrcnt) 245 if (self->addrcnt)
241 addr = (self->addrcnt - 1) * self->addrlen + (uint8_t *)(self + 1); 246 addr = (self->addrcnt - 1) * self->addrlen + (uint8_t *)(self + 1);
242 else 247 else
243 addr = sizeof (addr_tt) - self->addrlen + self->lo; 248 addr = sizeof (addr_tt) - self->addrlen + self->lo;
244 249
250 addrlen = self->addrlen;
251
252 /* convert ipv4 mapped addresses - this only works for host lists */
253 /* this tries to match 0000:0000:0000:0000:0000:ffff:a.b.c.d */
254 /* efficiently but also with few insns */
255 if (addrlen == 16 && !addr [0] && icmp4_fd >= 0
256 && !( addr [ 1]
257 | addr [ 2] | addr [ 3]
258 | addr [ 4] | addr [ 5]
259 | addr [ 6] | addr [ 7]
260 | addr [ 8] | addr [ 9]
261 | (255-addr [10]) | (255-addr [11])))
262 {
263 addr += 12;
264 addrlen -= 12;
265 }
266
245 pkt->cksum = 0; 267 pkt->cksum = 0;
246 268
247 if (self->addrlen == 4) 269 if (addrlen == 4)
248 { 270 {
249 struct sockaddr_in sa; 271 struct sockaddr_in sa;
250 272
251 pkt->type = ICMP4_ECHO; 273 pkt->type = ICMP4_ECHO;
252 pkt_cksum (pkt); 274 pkt_cksum (pkt);
253 275
254 sa.sin_family = AF_INET; 276 sa.sin_family = AF_INET;
255 sa.sin_port = 0; 277 sa.sin_port = 0;
256 278
357 PINGER *self = (PINGER *)self_; 379 PINGER *self = (PINGER *)self_;
358 PKT pkt; 380 PKT pkt;
359 381
360 memset (&pkt, 0, sizeof (pkt)); 382 memset (&pkt, 0, sizeof (pkt));
361 383
362 tstamp now = NOW (); 384 tstamp now = NOW ();
363 tstamp next = now;
364 385
365 pkt.code = 0; 386 pkt.code = 0;
366 pkt.id = self->magic1; 387 pkt.id = self->magic1;
367 pkt.seq = self->magic2; 388 pkt.seq = self->magic2;
368 pkt.magic = self->magic3; 389 pkt.magic = self->magic3;
369 pkt.pinger = self->id; 390 pkt.pinger = self->id;
391
392 if (self->next < now)
393 self->next = now;
370 394
371 while (self->rangecnt) 395 while (self->rangecnt)
372 { 396 {
373 RANGE *range = self->ranges [0]; 397 RANGE *range = self->ranges [0];
374 int n, k;
375 398
376 // ranges [0] is always the next range to ping 399 // ranges [0] is always the next range to ping
377 tstamp wait = range->next - now; 400 tstamp wait = range->next - now;
378 401
379 // compare with the global frequency limit 402 // compare with the global frequency limit
380 { 403 {
381 tstamp diff = next - now; 404 tstamp diff = self->next - now;
382 405
383 if (wait < diff) 406 if (wait < diff)
384 wait = diff; 407 wait = diff; // global rate limit overrides
385 else if (range) 408 else
386 next = range->next; 409 self->next = range->next; // fast forward
387 } 410 }
388 411
389 if (wait > 0.) 412 if (wait > 0.)
390 ssleep (wait); 413 ssleep (wait);
391 414
397 { 420 {
398 self->ranges [0] = self->ranges [--self->rangecnt]; 421 self->ranges [0] = self->ranges [--self->rangecnt];
399 range_free (range); 422 range_free (range);
400 } 423 }
401 else 424 else
402 {
403 range->next = next;
404 range->next += range->interval; 425 range->next = self->next + range->interval;
405 }
406 426
407 downheap (self); 427 downheap (self);
408 428
409 next += self->interval; 429 self->next += self->interval;
430 now = NOW ();
410 } 431 }
411 432
412 ssleep (self->maxrtt); 433 ssleep (self->maxrtt);
413 434
414 { 435 {
419 440
420 return 0; 441 return 0;
421} 442}
422 443
423/*****************************************************************************/ 444/*****************************************************************************/
445
446/* NetBSD, Solaris... */
447#ifndef PTHREAD_STACK_MIN
448# define PTHREAD_STACK_MIN 0
449#endif
424 450
425static void 451static void
426pinger_start (PINGER *self) 452pinger_start (PINGER *self)
427{ 453{
428 sigset_t fullsigset, oldsigset; 454 sigset_t fullsigset, oldsigset;
482 } 508 }
483 509
484 pingers [self->id] = self; 510 pingers [self->id] = self;
485 511
486 self->recvcb = &PL_sv_undef; 512 self->recvcb = &PL_sv_undef;
513 self->next = 0.;
487 self->interval = MIN_INTERVAL; 514 self->interval = MIN_INTERVAL;
488 self->maxrtt = 0.5; 515 self->maxrtt = 0.5;
489 self->rangemax = 16; 516 self->rangemax = 16;
490 self->ranges = malloc (sizeof (self->ranges [0]) * self->rangemax); 517 self->ranges = malloc (sizeof (self->ranges [0]) * self->rangemax);
491} 518}
609 LEAVE; 636 LEAVE;
610} 637}
611#endif 638#endif
612 639
613static void 640static void
614boot () 641boot_protocols (void)
615{ 642{
616 if (pipe (thr_res) < 0)
617 croak ("AnyEvent::FastPing: unable to create receive pipe");
618
619 sv_setiv (get_sv ("AnyEvent::FastPing::THR_RES_FD", 1), thr_res [0]);
620
621 icmp4_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); 643 icmp4_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
622 fcntl (icmp4_fd, F_SETFL, O_NONBLOCK); 644 fcntl (icmp4_fd, F_SETFL, O_NONBLOCK);
623#ifdef ICMP_FILTER 645#ifdef ICMP_FILTER
624 { 646 {
625 struct icmp_filter oval; 647 struct icmp_filter oval;
638 ICMP6_FILTER_SETPASS (ICMP6_ECHO_REPLY, &oval); 660 ICMP6_FILTER_SETPASS (ICMP6_ECHO_REPLY, &oval);
639 setsockopt (icmp6_fd, IPPROTO_ICMPV6, ICMP6_FILTER, &oval, sizeof oval); 661 setsockopt (icmp6_fd, IPPROTO_ICMPV6, ICMP6_FILTER, &oval, sizeof oval);
640 } 662 }
641# endif 663# endif
642#endif 664#endif
665}
666
667static void
668boot (void)
669{
670 if (pipe (thr_res) < 0)
671 croak ("AnyEvent::FastPing: unable to create receive pipe");
672
673 sv_setiv (get_sv ("AnyEvent::FastPing::THR_RES_FD", 1), thr_res [0]);
674
675 boot_protocols ();
643 676
644 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP4_FD", 1), icmp4_fd); 677 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP4_FD", 1), icmp4_fd);
645 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP6_FD", 1), icmp6_fd); 678 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP6_FD", 1), icmp6_fd);
646} 679}
647 680
681#define NOT_RUNNING \
682 if (self->running) \
683 croak ("AnyEvent::FastPing object has been started - you have to stop it first before calling this method, caught");
684
648MODULE = AnyEvent::FastPing PACKAGE = AnyEvent::FastPing PREFIX = pinger_ 685MODULE = AnyEvent::FastPing PACKAGE = AnyEvent::FastPing PREFIX = pinger_
649 686
687PROTOTYPES: DISABLE
688
650BOOT: 689BOOT:
651{ 690{
652 HV *stash = gv_stashpv ("AnyEvent::FastPing", 1); 691 HV *stash = gv_stashpv ("AnyEvent::FastPing", 1);
653 692
654 if (sizeof (PKT) & 3) 693 if (sizeof (PKT) & 3)
655 croak ("size of PKT structure is not a multiple of 4"); 694 croak ("size of PKT structure is not a multiple of 4");
656 695
657 boot ();
658
659 newCONSTSUB (stash, "ipv4_supported", newSViv (icmp4_fd >= 0));
660 newCONSTSUB (stash, "ipv6_supported", newSViv (icmp6_fd >= 0));
661
662 newCONSTSUB (stash, "icmp4_pktsize", newSViv (HDR_SIZE_IP4 + sizeof (PKT))); 696 newCONSTSUB (stash, "icmp4_pktsize", newSViv (HDR_SIZE_IP4 + sizeof (PKT)));
663 newCONSTSUB (stash, "icmp6_pktsize", newSViv (HDR_SIZE_IP6 + sizeof (PKT))); 697 newCONSTSUB (stash, "icmp6_pktsize", newSViv (HDR_SIZE_IP6 + sizeof (PKT)));
664}
665 698
666PROTOTYPES: DISABLE 699 boot_protocols ();
700
701 newCONSTSUB (stash, "ipv4_supported", newSViv (icmp4_fd >= 0));
702 newCONSTSUB (stash, "ipv6_supported", newSViv (icmp6_fd >= 0));
703
704 close (icmp4_fd);
705 close (icmp6_fd);
706}
707
708void
709_boot ()
710 CODE:
711 boot ();
667 712
668void 713void
669_recv_icmp4 (...) 714_recv_icmp4 (...)
670 CODE: 715 CODE:
671{ 716{
748_new (SV *klass, UV magic1, UV magic2, UV magic3) 793_new (SV *klass, UV magic1, UV magic2, UV magic3)
749 PPCODE: 794 PPCODE:
750{ 795{
751 SV *pv = NEWSV (0, sizeof (PINGER)); 796 SV *pv = NEWSV (0, sizeof (PINGER));
752 PINGER *self = (PINGER *)SvPVX (pv); 797 PINGER *self = (PINGER *)SvPVX (pv);
798
753 SvPOK_only (pv); 799 SvPOK_only (pv);
754 XPUSHs (sv_2mortal (sv_bless (newRV_noinc (pv), gv_stashpv (SvPVutf8_nolen (klass), 1)))); 800 XPUSHs (sv_2mortal (sv_bless (newRV_noinc (pv), gv_stashpv (SvPVutf8_nolen (klass), 1))));
755 pinger_init (self); 801 pinger_init (self);
756 self->magic1 = magic1; 802 self->magic1 = magic1;
757 self->magic2 = magic2; 803 self->magic2 = magic2;
781 pinger_stop (pingers [id]); 827 pinger_stop (pingers [id]);
782 828
783void 829void
784interval (PINGER *self, NV interval) 830interval (PINGER *self, NV interval)
785 CODE: 831 CODE:
832 NOT_RUNNING;
786 self->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL; 833 self->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL;
787 834
788void 835void
789max_rtt (PINGER *self, NV maxrtt) 836max_rtt (PINGER *self, NV maxrtt)
790 CODE: 837 CODE:
838 NOT_RUNNING;
791 self->maxrtt = maxrtt; 839 self->maxrtt = maxrtt;
792 840
793void 841void
842on_recv (PINGER *self, SV *cb)
843 CODE:
844 SvREFCNT_dec (self->recvcb);
845 self->recvcb = newSVsv (cb);
846
847void
794add_range (PINGER *self, SV *lo_, SV *hi_, NV interval) 848add_range (PINGER *self, SV *lo_, SV *hi_, NV interval = 0)
795 CODE: 849 CODE:
796{ 850{
797 STRLEN lo_len, hi_len; 851 STRLEN lo_len, hi_len;
798 char *lo = SvPVbyte (lo_, lo_len); 852 char *lo = SvPVbyte (lo_, lo_len);
799 char *hi = SvPVbyte (hi_, hi_len); 853 char *hi = SvPVbyte (hi_, hi_len);
800 RANGE *range; 854 RANGE *range;
855 NOT_RUNNING;
801 856
802 if (lo_len != hi_len || (lo_len != 4 && lo_len != 16)) 857 if (lo_len != hi_len || (lo_len != 4 && lo_len != 16))
803 croak ("AnyEvent::FastPing::add_range address range must be specified as two binary IPv4 or IPv6 addresses"); 858 croak ("AnyEvent::FastPing::add_range address range must be specified as two binary IPv4 or IPv6 addresses");
804 859
805 if (lo_len == 4 && icmp4_fd < 0) croak ("IPv4 support unavailable"); 860 if (lo_len == 4 && icmp4_fd < 0) croak ("IPv4 support unavailable");
819 874
820 pinger_add_range (self, range); 875 pinger_add_range (self, range);
821} 876}
822 877
823void 878void
824add_hosts (PINGER *self, SV *addrs, NV interval) 879add_hosts (PINGER *self, SV *addrs, NV interval = 0, UV interleave = 1)
825 CODE: 880 CODE:
826{ 881{
827 AV *av; 882 AV *av;
828 int i; 883 int i, j, k;
829 int cnt; 884 int cnt;
830 int addrlen; 885 int addrlen = 0;
831 RANGE *range; 886 RANGE *range;
887 NOT_RUNNING;
832 888
833 if (!SvROK (addrs) || SvTYPE (SvRV (addrs)) != SVt_PVAV) 889 if (!SvROK (addrs) || SvTYPE (SvRV (addrs)) != SVt_PVAV)
834 croak ("AnyEvent::FastPing::add_hosst expects an arrayref with binary IPv4 or IPv6 addresses"); 890 croak ("AnyEvent::FastPing::add_hosts expects an arrayref with binary IPv4 or IPv6 addresses");
835 891
836 av = (AV *)SvRV (addrs); 892 av = (AV *)SvRV (addrs);
837 cnt = av_len (av) + 1; 893 cnt = av_len (av) + 1;
838 894
895 for (i = 0; i < cnt; ++i)
896 {
897 SV *sv = *av_fetch (av, i, 1);
898 sv_utf8_downgrade (sv, 0);
899
900 j = SvCUR (sv);
901
902 if (j != 4 && j != 16)
903 croak ("AnyEvent::FastPing::add_hosts addresses must be specified as binary IPv4 or IPv6 addresses");
904
905 if (j > addrlen)
906 addrlen = j;
907 }
908
839 if (!cnt) 909 if (!cnt)
840 XSRETURN_EMPTY; 910 XSRETURN_EMPTY;
841
842 addrlen = SvCUR (*av_fetch (av, 0, 1));
843
844 if (addrlen != 4 && addrlen != 16)
845 croak ("AnyEvent::FastPing::add_hosts addresses must be specified as binary IPv4 or IPv6 addresses");
846
847 for (i = cnt; --i; )
848 {
849 SV *sv = *av_fetch (av, i, 1);
850
851 if (!sv_utf8_downgrade (sv, 1) || addrlen != SvCUR (sv))
852 croak ("AnyEvent::FastPing::add_hosts addresses must all have the same size");
853 }
854 911
855 range = calloc (1, sizeof (RANGE) + cnt * addrlen); 912 range = calloc (1, sizeof (RANGE) + cnt * addrlen);
856 913
857 range->next = 0; 914 range->next = 0;
858 range->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL; 915 range->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL;
859 range->addrlen = addrlen; 916 range->addrlen = addrlen;
860 range->addrcnt = cnt; 917 range->addrcnt = cnt;
861 918
862 for (i = cnt; i--; ) 919 if (interleave == 0)
863 memcpy ((uint8_t *)(range + 1) + (cnt - 1 - i) * addrlen, 920 interleave = cnt <= 256 * 256 ? 256 : (int)sqrtf (cnt);
864 SvPVbyte_nolen (*av_fetch (av, i, 1)), 921
922 k = cnt;
923 for (j = 0; j < interleave; ++j)
924 for (i = j; i < cnt; i += interleave)
925 {
926 uint8_t *dst = (uint8_t *)(range + 1) + --k * addrlen;
927 char *pv;
928 STRLEN pvlen;
929 SV *sv = *av_fetch (av, i, 1);
930 sv_utf8_downgrade (sv, 0);
931
932 pv = SvPVbyte (sv, pvlen);
933
934 if (pvlen != addrlen)
935 {
936 dst [ 0] = 0x00; dst [ 1] = 0x00; dst [ 2] = 0x00; dst [ 3] = 0x00;
937 dst [ 4] = 0x00; dst [ 5] = 0x00; dst [ 6] = 0x00; dst [ 7] = 0x00;
938 dst [ 8] = 0x00; dst [ 9] = 0x00; dst [10] = 0xff; dst [11] = 0xff;
939 dst [12] = pv [0]; dst [13] = pv [1]; dst [14] = pv [2]; dst [15] = pv [3];
940 }
941 else
865 addrlen); 942 memcpy (dst, pv, addrlen);
943 }
866 944
867 pinger_add_range (self, range); 945 pinger_add_range (self, range);
868} 946}
869 947
870void
871on_recv (PINGER *self, SV *cb)
872 CODE:
873 SvREFCNT_dec (self->recvcb);
874 self->recvcb = newSVsv (cb);
875

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines