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.10 by root, Mon Jan 31 05:35:48 2011 UTC vs.
Revision 1.13 by root, Sat Feb 5 23:37:21 2011 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
44#define MIN_INTERVAL 1e-6 // minimum packet send interval, in seconds 45#define MIN_INTERVAL 1e-6 // minimum packet send interval, in seconds
45 46
46#define HDR_SIZE_IP4 20 47#define HDR_SIZE_IP4 20
47#define HDR_SIZE_IP6 48 48#define HDR_SIZE_IP6 48
48 49
49//TODO: xread/xwrite for atomicity? we currently rely on the fact that the pip buffersize divides exactly by pointer sizes 50static int thr_res[2]; // worker thread finished status
50
51typedef uint8_t addr_tt[16];
52
53static int thr_res[2]; // receive from worker(s)
54static int icmp4_fd = -1, icmp6_fd = -1; 51static int icmp4_fd = -1;
52static int icmp6_fd = -1;
55 53
56/*****************************************************************************/ 54/*****************************************************************************/
57 55
58typedef double tstamp; 56typedef double tstamp;
59 57
106 104
107 uint32_t src; 105 uint32_t src;
108 uint32_t dst; 106 uint32_t dst;
109} IP4HDR; 107} IP4HDR;
110 108
111#if 0 109/*****************************************************************************/
110
111typedef uint8_t addr_tt[16];
112
112typedef struct 113typedef struct
113{ 114{
114 uint8_t version;
115 uint8_t x1, x2, x3;
116
117 uint16_t payload_len;
118 uint8_t nxt_hdr;
119 uint8_t hop_limit;
120
121 uint8_t src[16];
122 uint8_t dst[16];
123} IP6HDR;
124#endif
125
126/*****************************************************************************/
127
128typedef struct
129{
130 tstamp next; 115 tstamp next;
131 double interval; 116 tstamp interval;
132 int addrlen; 117 int addrlen;
133 addr_tt lo, hi; 118
134 void *items; 119 addr_tt lo, hi; /* only if !addrcnt */
120
121 int addrcnt;
122 /* addrcnt addresses follow */
135} RANGE; 123} RANGE;
136 124
137typedef struct 125typedef struct
138{ 126{
139 RANGE **ranges; 127 RANGE **ranges;
140 int rangecnt, rangemax; 128 int rangecnt, rangemax;
141 129
130 tstamp next;
142 tstamp interval; 131 tstamp interval;
132
143 tstamp maxrtt; 133 tstamp maxrtt;
134
144 uint16_t magic1; 135 uint16_t magic1;
145 uint16_t magic2; 136 uint16_t magic2;
146 uint16_t magic3; 137 uint16_t magic3;
147 138
148 int id; 139 int id;
166 157
167typedef struct 158typedef struct
168{ 159{
169 uint8_t type, code; 160 uint8_t type, code;
170 uint16_t cksum; 161 uint16_t cksum;
162
171 uint16_t id, seq; 163 uint16_t id, seq;
164
172 uint16_t pinger; 165 uint16_t pinger;
173 uint16_t magic; 166 uint16_t magic;
167
174 uint32_t stamp_hi; 168 uint32_t stamp_hi;
175 uint32_t stamp_lo; 169 uint32_t stamp_lo;
176} PKT; 170} PKT;
177 171
178static int 172static int
228range_free (RANGE *self) 222range_free (RANGE *self)
229{ 223{
230 free (self); 224 free (self);
231} 225}
232 226
233static void
234inc_addr (addr_tt *addr)
235{
236 int len = sizeof (addr_tt) - 1;
237
238 while (!++(*addr)[len])
239 --len;
240}
241
242/*****************************************************************************/
243
244/* like sendto, but retries on failure */ 227/* like sendto, but retries on failure */
245static void 228static void
246xsendto (int fd, void *buf, size_t len, int flags, void *sa, int salen) 229xsendto (int fd, void *buf, size_t len, int flags, void *sa, int salen)
247{ 230{
248 tstamp wait = 0; 231 tstamp wait = DRAIN_INTERVAL / 2.;
249 232
250 while (sendto (fd, buf, len, flags, sa, salen) < 0 && errno == ENOBUFS) 233 while (sendto (fd, buf, len, flags, sa, salen) < 0 && errno == ENOBUFS)
251 ssleep (wait += DRAIN_INTERVAL); 234 ssleep (wait *= 2.);
252} 235}
253 236
237// ping current address, return true and increment if more to ping
254static void 238static int
255send_ping (PKT *pkt, addr_tt *addr, int addr_len) 239range_send_ping (RANGE *self, PKT *pkt)
256{ 240{
241 // send ping
242 uint8_t *addr;
243 int addrlen;
244
245 if (self->addrcnt)
246 addr = (self->addrcnt - 1) * self->addrlen + (uint8_t *)(self + 1);
247 else
248 addr = sizeof (addr_tt) - self->addrlen + self->lo;
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
257 pkt->cksum = 0; 267 pkt->cksum = 0;
258 268
259 if (addr_len == 4) 269 if (addrlen == 4)
260 { 270 {
261 struct sockaddr_in sa; 271 struct sockaddr_in sa;
262 272
263 pkt->type = ICMP4_ECHO; 273 pkt->type = ICMP4_ECHO;
264 pkt_cksum (pkt); 274 pkt_cksum (pkt);
265 275
266 sa.sin_family = AF_INET; 276 sa.sin_family = AF_INET;
267 sa.sin_port = 0; 277 sa.sin_port = 0;
268 278
269 memcpy (&sa.sin_addr, 279 memcpy (&sa.sin_addr, addr, sizeof (sa.sin_addr));
270 sizeof (addr_tt) - sizeof (sa.sin_addr) + (char *)addr,
271 sizeof (sa.sin_addr));
272 280
273 xsendto (icmp4_fd, pkt, sizeof (*pkt), 0, &sa, sizeof (sa)); 281 xsendto (icmp4_fd, pkt, sizeof (*pkt), 0, &sa, sizeof (sa));
274 } 282 }
275 else 283 else
276 { 284 {
282 sa.sin6_family = AF_INET6; 290 sa.sin6_family = AF_INET6;
283 sa.sin6_port = 0; 291 sa.sin6_port = 0;
284 sa.sin6_flowinfo = 0; 292 sa.sin6_flowinfo = 0;
285 sa.sin6_scope_id = 0; 293 sa.sin6_scope_id = 0;
286 294
287 memcpy (&sa.sin6_addr, 295 memcpy (&sa.sin6_addr, addr, sizeof (sa.sin6_addr));
288 sizeof (addr_tt) - sizeof (sa.sin6_addr) + (char *)addr,
289 sizeof (sa.sin6_addr));
290 296
291 xsendto (icmp6_fd, &pkt, sizeof (pkt), 0, &sa, sizeof (sa)); 297 xsendto (icmp6_fd, pkt, sizeof (*pkt), 0, &sa, sizeof (sa));
292#endif 298#endif
293 } 299 }
300
301 // see if we have any more addresses
302 if (self->addrcnt)
303 {
304 if (!--self->addrcnt)
305 return 0;
306 }
307 else
308 {
309 if (!memcmp (&self->lo, &self->hi, sizeof (addr_tt)))
310 return 0;
311
312 // increment self->lo
313 {
314 int len = sizeof (addr_tt) - 1;
315
316 while (!++self->lo [len])
317 --len;
318 }
319 }
320
321 return 1;
294} 322}
323
324/*****************************************************************************/
295 325
296static void 326static void
297downheap (PINGER *self) 327downheap (PINGER *self)
298{ 328{
299 RANGE *elem = self->ranges [0]; /* always exists */ 329 RANGE *elem = self->ranges [0]; /* always exists */
349 PINGER *self = (PINGER *)self_; 379 PINGER *self = (PINGER *)self_;
350 PKT pkt; 380 PKT pkt;
351 381
352 memset (&pkt, 0, sizeof (pkt)); 382 memset (&pkt, 0, sizeof (pkt));
353 383
354 tstamp now = NOW (); 384 tstamp now = NOW ();
355 tstamp next = now;
356 385
357 pkt.code = 0; 386 pkt.code = 0;
358 pkt.id = self->magic1; 387 pkt.id = self->magic1;
359 pkt.seq = self->magic2; 388 pkt.seq = self->magic2;
360 pkt.magic = self->magic3; 389 pkt.magic = self->magic3;
361 pkt.pinger = self->id; 390 pkt.pinger = self->id;
391
392 if (self->next < now)
393 self->next = now;
362 394
363 while (self->rangecnt) 395 while (self->rangecnt)
364 { 396 {
365 RANGE *range = self->ranges [0]; 397 RANGE *range = self->ranges [0];
366 int n, k;
367 398
368 // ranges [0] is always the next range to ping 399 // ranges [0] is always the next range to ping
369 tstamp wait = range->next - now; 400 tstamp wait = range->next - now;
370 401
371 // compare with the global frequency limit 402 // compare with the global frequency limit
372 { 403 {
373 tstamp diff = next - now; 404 tstamp diff = self->next - now;
374 405
375 if (wait < diff) 406 if (wait < diff)
376 wait = diff; 407 wait = diff; // global rate limit overrides
377 else if (range) 408 else
378 next = range->next; 409 self->next = range->next; // fast forward
379 } 410 }
380 411
381 if (wait > 0.) 412 if (wait > 0.)
382 ssleep (wait); 413 ssleep (wait);
383 414
384 now = NOW (); 415 now = NOW ();
385 416
386 ts_to_pkt (&pkt, now); 417 ts_to_pkt (&pkt, now);
387 418
388 send_ping (&pkt, &range->lo, range->addrlen); 419 if (!range_send_ping (range, &pkt))
389
390 if (!memcmp (&range->lo, &range->hi, sizeof (addr_tt)))
391 { 420 {
392 self->ranges [0] = self->ranges [--self->rangecnt]; 421 self->ranges [0] = self->ranges [--self->rangecnt];
393 range_free (range); 422 range_free (range);
394 } 423 }
395 else 424 else
396 {
397 inc_addr (&range->lo);
398
399 range->next = next;
400 range->next += range->interval; 425 range->next = self->next + range->interval;
401 426
402 downheap (self); 427 downheap (self);
403 }
404 428
405 next += self->interval; 429 self->next += self->interval;
430 now = NOW ();
406 } 431 }
407 432
408 ssleep (self->maxrtt); 433 ssleep (self->maxrtt);
409 434
410 { 435 {
415 440
416 return 0; 441 return 0;
417} 442}
418 443
419/*****************************************************************************/ 444/*****************************************************************************/
445
446/* NetBSD, Solaris... */
447#ifndef PTHREAD_STACK_MIN
448# define PTHREAD_STACK_MIN 0
449#endif
420 450
421static void 451static void
422pinger_start (PINGER *self) 452pinger_start (PINGER *self)
423{ 453{
424 sigset_t fullsigset, oldsigset; 454 sigset_t fullsigset, oldsigset;
478 } 508 }
479 509
480 pingers [self->id] = self; 510 pingers [self->id] = self;
481 511
482 self->recvcb = &PL_sv_undef; 512 self->recvcb = &PL_sv_undef;
513 self->next = 0.;
483 self->interval = MIN_INTERVAL; 514 self->interval = MIN_INTERVAL;
484 self->maxrtt = 0.5; 515 self->maxrtt = 0.5;
485 self->rangemax = 16; 516 self->rangemax = 16;
486 self->ranges = malloc (sizeof (self->ranges [0]) * self->rangemax); 517 self->ranges = malloc (sizeof (self->ranges [0]) * self->rangemax);
487} 518}
503 range_free (self->ranges [--self->rangecnt]); 534 range_free (self->ranges [--self->rangecnt]);
504 535
505 free (self->ranges); 536 free (self->ranges);
506} 537}
507 538
539static void
540pinger_add_range (PINGER *self, RANGE *range)
541{
542 if (self->rangecnt == self->rangemax)
543 self->ranges = realloc (self->ranges, sizeof (self->ranges [0]) * (self->rangemax <<= 1));
544
545 self->ranges [self->rangecnt] = range;
546 upheap (self, self->rangecnt);
547 ++self->rangecnt;
548}
549
508/*****************************************************************************/ 550/*****************************************************************************/
509 551
510static void 552static void
511recv_feed (PINGER *self, void *addr, int addrlen, tstamp rtt) 553recv_feed (PINGER *self, void *addr, int addrlen, tstamp rtt)
512{ 554{
542 return; 584 return;
543 585
544 ENTER; 586 ENTER;
545 SAVETMPS; 587 SAVETMPS;
546 588
547 while (firstrecv >= 0) 589 do
548 { 590 {
549 dSP; 591 dSP;
550 PINGER *self = pingers [firstrecv]; 592 PINGER *self = pingers [firstrecv];
551 firstrecv = self->nextrecv; 593 firstrecv = self->nextrecv;
552 594
556 XPUSHs (sv_2mortal (newRV_noinc ((SV *)self->recvq))); 598 XPUSHs (sv_2mortal (newRV_noinc ((SV *)self->recvq)));
557 self->recvq = 0; 599 self->recvq = 0;
558 PUTBACK; 600 PUTBACK;
559 call_sv (self->recvcb, G_DISCARD | G_VOID); 601 call_sv (self->recvcb, G_DISCARD | G_VOID);
560 } 602 }
603 while (firstrecv >= 0);
561 604
562 FREETMPS; 605 FREETMPS;
563 LEAVE; 606 LEAVE;
564} 607}
565 608
627 670
628 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP4_FD", 1), icmp4_fd); 671 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP4_FD", 1), icmp4_fd);
629 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP6_FD", 1), icmp6_fd); 672 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP6_FD", 1), icmp6_fd);
630} 673}
631 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
632MODULE = AnyEvent::FastPing PACKAGE = AnyEvent::FastPing PREFIX = pinger_ 679MODULE = AnyEvent::FastPing PACKAGE = AnyEvent::FastPing PREFIX = pinger_
633 680
634BOOT: 681BOOT:
635{ 682{
636 HV *stash = gv_stashpv ("AnyEvent::FastPing", 1); 683 HV *stash = gv_stashpv ("AnyEvent::FastPing", 1);
646 newCONSTSUB (stash, "icmp4_pktsize", newSViv (HDR_SIZE_IP4 + sizeof (PKT))); 693 newCONSTSUB (stash, "icmp4_pktsize", newSViv (HDR_SIZE_IP4 + sizeof (PKT)));
647 newCONSTSUB (stash, "icmp6_pktsize", newSViv (HDR_SIZE_IP6 + sizeof (PKT))); 694 newCONSTSUB (stash, "icmp6_pktsize", newSViv (HDR_SIZE_IP6 + sizeof (PKT)));
648} 695}
649 696
650PROTOTYPES: DISABLE 697PROTOTYPES: DISABLE
651
652void
653_new (SV *klass, UV magic1, UV magic2, UV magic3)
654 PPCODE:
655{
656 SV *pv = NEWSV (0, sizeof (PINGER));
657 PINGER *self = (PINGER *)SvPVX (pv);
658 SvPOK_only (pv);
659 XPUSHs (sv_2mortal (sv_bless (newRV_noinc (pv), gv_stashpv (SvPV_nolen (klass), 1))));
660 pinger_init (self);
661 self->magic1 = magic1;
662 self->magic2 = magic2;
663 self->magic3 = magic3;
664}
665
666void
667_free (PINGER *self)
668 CODE:
669 pinger_free (self);
670
671IV
672id (PINGER *self, ...)
673 CODE:
674 RETVAL = self->id;
675 OUTPUT:
676 RETVAL
677
678void pinger_start (PINGER *self)
679
680void pinger_stop (PINGER *self)
681
682void _stop_id (UV id)
683 CODE:
684 if (id < pingercnt && pingers [id])
685 pinger_stop (pingers [id]);
686
687void interval (PINGER *self, NV interval)
688 CODE:
689 self->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL;
690
691void add_range (PINGER *self, SV *lo_, SV *hi_, NV interval)
692 CODE:
693{
694 STRLEN lo_len, hi_len;
695 char *lo = SvPVbyte (lo_, lo_len);
696 char *hi = SvPVbyte (hi_, hi_len);
697 RANGE *range;
698
699 if (lo_len != hi_len || (lo_len != 4 && lo_len != 16))
700 croak ("AnyEvent::FastPing::add_range address range must be specified as two binary IPv4 or IPv6 addresses");
701
702 if (lo_len == 4 && icmp4_fd < 0) croak ("IPv4 support unavailable");
703 if (lo_len == 16 && icmp6_fd < 0) croak ("IPv6 support unavailable");
704
705 range = calloc (1, sizeof (RANGE));
706
707 range->next = 0;
708 range->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL;
709 range->addrlen = lo_len;
710
711 memcpy (sizeof (addr_tt) - lo_len + (char *)&range->lo, lo, lo_len);
712 memcpy (sizeof (addr_tt) - lo_len + (char *)&range->hi, hi, lo_len);
713
714 if (self->rangecnt == self->rangemax)
715 self->ranges = realloc (self->ranges, sizeof (self->ranges [0]) * (self->rangemax <<= 1));
716
717 self->ranges [self->rangecnt] = range;
718 upheap (self, self->rangecnt);
719 ++self->rangecnt;
720}
721
722void
723on_recv (PINGER *self, SV *cb)
724 CODE:
725 SvREFCNT_dec (self->recvcb);
726 self->recvcb = newSVsv (cb);
727
728void
729max_rtt (PINGER *self, NV maxrtt)
730 CODE:
731 self->maxrtt = maxrtt;
732 698
733void 699void
734_recv_icmp4 (...) 700_recv_icmp4 (...)
735 CODE: 701 CODE:
736{ 702{
737 char buf [512]; 703 char buf [512];
738 struct sockaddr_in sa; 704 struct sockaddr_in sa;
705 int maxrecv;
739 706
740 for (;;) 707 for (maxrecv = 256+1; --maxrecv; )
741 { 708 {
742 PINGER *pinger; 709 PINGER *pinger;
743 IP4HDR *iphdr = (IP4HDR *)buf; 710 IP4HDR *iphdr = (IP4HDR *)buf;
744 socklen_t sl = sizeof (sa); 711 socklen_t sl = sizeof (sa);
745 int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_TRUNC, (struct sockaddr *)&sa, &sl); 712 int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_TRUNC, (struct sockaddr *)&sa, &sl);
750 break; 717 break;
751 718
752 hdrlen = (iphdr->version_ihl & 15) * 4; 719 hdrlen = (iphdr->version_ihl & 15) * 4;
753 totlen = ntohs (iphdr->tot_len); 720 totlen = ntohs (iphdr->tot_len);
754 721
755 // packet corrupt?
756 if (totlen > len 722 if (totlen > len
757 || iphdr->protocol != IPPROTO_ICMP 723 || iphdr->protocol != IPPROTO_ICMP
758 || hdrlen < HDR_SIZE_IP4 || hdrlen + sizeof (PKT) != totlen) 724 || hdrlen < HDR_SIZE_IP4 || hdrlen + sizeof (PKT) != totlen)
759 continue; 725 continue;
760 726
780_recv_icmp6 (...) 746_recv_icmp6 (...)
781 CODE: 747 CODE:
782{ 748{
783 struct sockaddr_in6 sa; 749 struct sockaddr_in6 sa;
784 PKT pkt; 750 PKT pkt;
751 int maxrecv;
785 752
786 for (;;) 753 for (maxrecv = 256+1; --maxrecv; )
787 { 754 {
788 PINGER *pinger; 755 PINGER *pinger;
789 socklen_t sl = sizeof (sa); 756 socklen_t sl = sizeof (sa);
790 int len = recvfrom (icmp6_fd, &pkt, sizeof (pkt), MSG_TRUNC, (struct sockaddr *)&sa, &sl); 757 int len = recvfrom (icmp6_fd, &pkt, sizeof (pkt), MSG_TRUNC, (struct sockaddr *)&sa, &sl);
791 758
806 } 773 }
807 774
808 recv_flush (); 775 recv_flush ();
809} 776}
810 777
778void
779_new (SV *klass, UV magic1, UV magic2, UV magic3)
780 PPCODE:
781{
782 SV *pv = NEWSV (0, sizeof (PINGER));
783 PINGER *self = (PINGER *)SvPVX (pv);
784
785 SvPOK_only (pv);
786 XPUSHs (sv_2mortal (sv_bless (newRV_noinc (pv), gv_stashpv (SvPVutf8_nolen (klass), 1))));
787 pinger_init (self);
788 self->magic1 = magic1;
789 self->magic2 = magic2;
790 self->magic3 = magic3;
791}
792
793void
794_free (PINGER *self)
795 CODE:
796 pinger_free (self);
797
798IV
799id (PINGER *self, ...)
800 CODE:
801 RETVAL = self->id;
802 OUTPUT:
803 RETVAL
804
805void pinger_start (PINGER *self)
806
807void pinger_stop (PINGER *self)
808
809void
810_stop_id (UV id)
811 CODE:
812 if (id < pingercnt && pingers [id])
813 pinger_stop (pingers [id]);
814
815void
816interval (PINGER *self, NV interval)
817 CODE:
818 NOT_RUNNING;
819 self->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL;
820
821void
822max_rtt (PINGER *self, NV maxrtt)
823 CODE:
824 NOT_RUNNING;
825 self->maxrtt = maxrtt;
826
827void
828on_recv (PINGER *self, SV *cb)
829 CODE:
830 SvREFCNT_dec (self->recvcb);
831 self->recvcb = newSVsv (cb);
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