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.14 by root, Wed Oct 8 21:12:28 2014 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}
643 670
644 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP4_FD", 1), icmp4_fd); 671 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP4_FD", 1), icmp4_fd);
645 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP6_FD", 1), icmp6_fd); 672 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP6_FD", 1), icmp6_fd);
646} 673}
647 674
675#define NOT_RUNNING \
676 if (self->running) \
677 croak ("AnyEvent::FastPing object has been started - you have to sotp t first before calling this method, caught");
678
648MODULE = AnyEvent::FastPing PACKAGE = AnyEvent::FastPing PREFIX = pinger_ 679MODULE = AnyEvent::FastPing PACKAGE = AnyEvent::FastPing PREFIX = pinger_
649 680
650BOOT: 681BOOT:
651{ 682{
652 HV *stash = gv_stashpv ("AnyEvent::FastPing", 1); 683 HV *stash = gv_stashpv ("AnyEvent::FastPing", 1);
748_new (SV *klass, UV magic1, UV magic2, UV magic3) 779_new (SV *klass, UV magic1, UV magic2, UV magic3)
749 PPCODE: 780 PPCODE:
750{ 781{
751 SV *pv = NEWSV (0, sizeof (PINGER)); 782 SV *pv = NEWSV (0, sizeof (PINGER));
752 PINGER *self = (PINGER *)SvPVX (pv); 783 PINGER *self = (PINGER *)SvPVX (pv);
784
753 SvPOK_only (pv); 785 SvPOK_only (pv);
754 XPUSHs (sv_2mortal (sv_bless (newRV_noinc (pv), gv_stashpv (SvPVutf8_nolen (klass), 1)))); 786 XPUSHs (sv_2mortal (sv_bless (newRV_noinc (pv), gv_stashpv (SvPVutf8_nolen (klass), 1))));
755 pinger_init (self); 787 pinger_init (self);
756 self->magic1 = magic1; 788 self->magic1 = magic1;
757 self->magic2 = magic2; 789 self->magic2 = magic2;
781 pinger_stop (pingers [id]); 813 pinger_stop (pingers [id]);
782 814
783void 815void
784interval (PINGER *self, NV interval) 816interval (PINGER *self, NV interval)
785 CODE: 817 CODE:
818 NOT_RUNNING;
786 self->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL; 819 self->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL;
787 820
788void 821void
789max_rtt (PINGER *self, NV maxrtt) 822max_rtt (PINGER *self, NV maxrtt)
790 CODE: 823 CODE:
824 NOT_RUNNING;
791 self->maxrtt = maxrtt; 825 self->maxrtt = maxrtt;
792
793void
794add_range (PINGER *self, SV *lo_, SV *hi_, NV interval)
795 CODE:
796{
797 STRLEN lo_len, hi_len;
798 char *lo = SvPVbyte (lo_, lo_len);
799 char *hi = SvPVbyte (hi_, hi_len);
800 RANGE *range;
801
802 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");
804
805 if (lo_len == 4 && icmp4_fd < 0) croak ("IPv4 support unavailable");
806 if (lo_len == 16 && icmp6_fd < 0) croak ("IPv6 support unavailable");
807
808 if (memcmp (lo, hi, lo_len) > 0)
809 croak ("AnyEvent::FastPing::add_range called with lo > hi");
810
811 range = calloc (1, sizeof (RANGE));
812
813 range->next = 0;
814 range->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL;
815 range->addrlen = lo_len;
816
817 memcpy (sizeof (addr_tt) - lo_len + (char *)&range->lo, lo, lo_len);
818 memcpy (sizeof (addr_tt) - lo_len + (char *)&range->hi, hi, lo_len);
819
820 pinger_add_range (self, range);
821}
822
823void
824add_hosts (PINGER *self, SV *addrs, NV interval)
825 CODE:
826{
827 AV *av;
828 int i;
829 int cnt;
830 int addrlen;
831 RANGE *range;
832
833 if (!SvROK (addrs) || SvTYPE (SvRV (addrs)) != SVt_PVAV)
834 croak ("AnyEvent::FastPing::add_hosst expects an arrayref with binary IPv4 or IPv6 addresses");
835
836 av = (AV *)SvRV (addrs);
837 cnt = av_len (av) + 1;
838
839 if (!cnt)
840 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
855 range = calloc (1, sizeof (RANGE) + cnt * addrlen);
856
857 range->next = 0;
858 range->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL;
859 range->addrlen = addrlen;
860 range->addrcnt = cnt;
861
862 for (i = cnt; i--; )
863 memcpy ((uint8_t *)(range + 1) + (cnt - 1 - i) * addrlen,
864 SvPVbyte_nolen (*av_fetch (av, i, 1)),
865 addrlen);
866
867 pinger_add_range (self, range);
868}
869 826
870void 827void
871on_recv (PINGER *self, SV *cb) 828on_recv (PINGER *self, SV *cb)
872 CODE: 829 CODE:
873 SvREFCNT_dec (self->recvcb); 830 SvREFCNT_dec (self->recvcb);
874 self->recvcb = newSVsv (cb); 831 self->recvcb = newSVsv (cb);
875 832
833void
834add_range (PINGER *self, SV *lo_, SV *hi_, NV interval = 0)
835 CODE:
836{
837 STRLEN lo_len, hi_len;
838 char *lo = SvPVbyte (lo_, lo_len);
839 char *hi = SvPVbyte (hi_, hi_len);
840 RANGE *range;
841 NOT_RUNNING;
842
843 if (lo_len != hi_len || (lo_len != 4 && lo_len != 16))
844 croak ("AnyEvent::FastPing::add_range address range must be specified as two binary IPv4 or IPv6 addresses");
845
846 if (lo_len == 4 && icmp4_fd < 0) croak ("IPv4 support unavailable");
847 if (lo_len == 16 && icmp6_fd < 0) croak ("IPv6 support unavailable");
848
849 if (memcmp (lo, hi, lo_len) > 0)
850 croak ("AnyEvent::FastPing::add_range called with lo > hi");
851
852 range = calloc (1, sizeof (RANGE));
853
854 range->next = 0;
855 range->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL;
856 range->addrlen = lo_len;
857
858 memcpy (sizeof (addr_tt) - lo_len + (char *)&range->lo, lo, lo_len);
859 memcpy (sizeof (addr_tt) - lo_len + (char *)&range->hi, hi, lo_len);
860
861 pinger_add_range (self, range);
862}
863
864void
865add_hosts (PINGER *self, SV *addrs, NV interval = 0, UV interleave = 1)
866 CODE:
867{
868 AV *av;
869 int i, j, k;
870 int cnt;
871 int addrlen = 0;
872 RANGE *range;
873 NOT_RUNNING;
874
875 if (!SvROK (addrs) || SvTYPE (SvRV (addrs)) != SVt_PVAV)
876 croak ("AnyEvent::FastPing::add_hosts expects an arrayref with binary IPv4 or IPv6 addresses");
877
878 av = (AV *)SvRV (addrs);
879 cnt = av_len (av) + 1;
880
881 for (i = 0; i < cnt; ++i)
882 {
883 SV *sv = *av_fetch (av, i, 1);
884 sv_utf8_downgrade (sv, 0);
885
886 j = SvCUR (sv);
887
888 if (j != 4 && j != 16)
889 croak ("AnyEvent::FastPing::add_hosts addresses must be specified as binary IPv4 or IPv6 addresses");
890
891 if (j > addrlen)
892 addrlen = j;
893 }
894
895 if (!cnt)
896 XSRETURN_EMPTY;
897
898 range = calloc (1, sizeof (RANGE) + cnt * addrlen);
899
900 range->next = 0;
901 range->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL;
902 range->addrlen = addrlen;
903 range->addrcnt = cnt;
904
905 if (interleave == 0)
906 interleave = cnt <= 256 * 256 ? 256 : (int)sqrtf (cnt);
907
908 k = cnt;
909 for (j = 0; j < interleave; ++j)
910 for (i = j; i < cnt; i += interleave)
911 {
912 uint8_t *dst = (uint8_t *)(range + 1) + --k * addrlen;
913 char *pv;
914 STRLEN pvlen;
915 SV *sv = *av_fetch (av, i, 1);
916 sv_utf8_downgrade (sv, 0);
917
918 pv = SvPVbyte (sv, pvlen);
919
920 if (pvlen != addrlen)
921 {
922 dst [ 0] = 0x00; dst [ 1] = 0x00; dst [ 2] = 0x00; dst [ 3] = 0x00;
923 dst [ 4] = 0x00; dst [ 5] = 0x00; dst [ 6] = 0x00; dst [ 7] = 0x00;
924 dst [ 8] = 0x00; dst [ 9] = 0x00; dst [10] = 0xff; dst [11] = 0xff;
925 dst [12] = pv [0]; dst [13] = pv [1]; dst [14] = pv [2]; dst [15] = pv [3];
926 }
927 else
928 memcpy (dst, pv, addrlen);
929 }
930
931 pinger_add_range (self, range);
932}
933

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines