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.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
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
203} 197}
204 198
205static void 199static void
206pkt_cksum (PKT *pkt) 200pkt_cksum (PKT *pkt)
207{ 201{
208 uint_fast32_t sum = -pkt->cksum; 202 uint_fast32_t sum = 0;
209 uint32_t *wp = (uint32_t *)pkt; 203 uint32_t *wp = (uint32_t *)pkt;
210 int len = sizeof (*pkt) / 4; 204 int len = sizeof (*pkt) / 4;
211 205
212 do 206 do
213 { 207 {
214 uint_fast32_t w = *(volatile uint32_t *)wp++; 208 uint_fast32_t w = *(volatile uint32_t *)wp++;
215 sum += (w & 0xffff) + (w >> 16); 209 sum += (w & 0xffff) + (w >> 16);
216 } 210 }
217 while (len--); 211 while (--len);
218 212
219 sum = (sum >> 16) + (sum & 0xffff); /* add high 16 to low 16 */ 213 sum = (sum >> 16) + (sum & 0xffff); /* add high 16 to low 16 */
220 sum += sum >> 16; /* add carry */ 214 sum += sum >> 16; /* add carry */
221 215
222 pkt->cksum = ~sum; 216 pkt->cksum = ~sum;
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
593 LEAVE; 636 LEAVE;
594} 637}
595#endif 638#endif
596 639
597static void 640static void
598boot () 641boot_protocols (void)
599{ 642{
600 if (pipe (thr_res) < 0)
601 croak ("AnyEvent::FastPing: unable to create receive pipe");
602
603 sv_setiv (get_sv ("AnyEvent::FastPing::THR_RES_FD", 1), thr_res [0]);
604
605 icmp4_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); 643 icmp4_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
606 fcntl (icmp4_fd, F_SETFL, O_NONBLOCK); 644 fcntl (icmp4_fd, F_SETFL, O_NONBLOCK);
607#ifdef ICMP_FILTER 645#ifdef ICMP_FILTER
608 { 646 {
609 struct icmp_filter oval; 647 struct icmp_filter oval;
622 ICMP6_FILTER_SETPASS (ICMP6_ECHO_REPLY, &oval); 660 ICMP6_FILTER_SETPASS (ICMP6_ECHO_REPLY, &oval);
623 setsockopt (icmp6_fd, IPPROTO_ICMPV6, ICMP6_FILTER, &oval, sizeof oval); 661 setsockopt (icmp6_fd, IPPROTO_ICMPV6, ICMP6_FILTER, &oval, sizeof oval);
624 } 662 }
625# endif 663# endif
626#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 ();
627 676
628 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP4_FD", 1), icmp4_fd); 677 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP4_FD", 1), icmp4_fd);
629 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP6_FD", 1), icmp6_fd); 678 sv_setiv (get_sv ("AnyEvent::FastPing::ICMP6_FD", 1), icmp6_fd);
630} 679}
631 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
632MODULE = AnyEvent::FastPing PACKAGE = AnyEvent::FastPing PREFIX = pinger_ 685MODULE = AnyEvent::FastPing PACKAGE = AnyEvent::FastPing PREFIX = pinger_
633 686
687PROTOTYPES: DISABLE
688
634BOOT: 689BOOT:
635{ 690{
636 HV *stash = gv_stashpv ("AnyEvent::FastPing", 1); 691 HV *stash = gv_stashpv ("AnyEvent::FastPing", 1);
637 692
638 if (sizeof (PKT) & 3) 693 if (sizeof (PKT) & 3)
639 croak ("size of PKT structure is not a multiple of 4"); 694 croak ("size of PKT structure is not a multiple of 4");
640 695
641 boot ();
642
643 newCONSTSUB (stash, "ipv4_supported", newSViv (icmp4_fd >= 0));
644 newCONSTSUB (stash, "ipv6_supported", newSViv (icmp6_fd >= 0));
645
646 newCONSTSUB (stash, "icmp4_pktsize", newSViv (HDR_SIZE_IP4 + sizeof (PKT))); 696 newCONSTSUB (stash, "icmp4_pktsize", newSViv (HDR_SIZE_IP4 + sizeof (PKT)));
647 newCONSTSUB (stash, "icmp6_pktsize", newSViv (HDR_SIZE_IP6 + sizeof (PKT))); 697 newCONSTSUB (stash, "icmp6_pktsize", newSViv (HDR_SIZE_IP6 + sizeof (PKT)));
648}
649 698
650PROTOTYPES: 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}
651 707
652void 708void
653_new (SV *klass, UV magic1, UV magic2, UV magic3) 709_boot ()
654 PPCODE: 710 CODE:
655{ 711 boot ();
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 712
733void 713void
734_recv_icmp4 (...) 714_recv_icmp4 (...)
735 CODE: 715 CODE:
736{ 716{
737 char buf [512]; 717 char buf [512];
738 struct sockaddr_in sa; 718 struct sockaddr_in sa;
719 int maxrecv;
739 720
740 for (;;) 721 for (maxrecv = 256+1; --maxrecv; )
741 { 722 {
742 PINGER *pinger; 723 PINGER *pinger;
743 IP4HDR *iphdr = (IP4HDR *)buf; 724 IP4HDR *iphdr = (IP4HDR *)buf;
744 socklen_t sl = sizeof (sa); 725 socklen_t sl = sizeof (sa);
745 int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_TRUNC, (struct sockaddr *)&sa, &sl); 726 int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_TRUNC, (struct sockaddr *)&sa, &sl);
750 break; 731 break;
751 732
752 hdrlen = (iphdr->version_ihl & 15) * 4; 733 hdrlen = (iphdr->version_ihl & 15) * 4;
753 totlen = ntohs (iphdr->tot_len); 734 totlen = ntohs (iphdr->tot_len);
754 735
755 // packet corrupt?
756 if (totlen > len 736 if (totlen > len
757 || iphdr->protocol != IPPROTO_ICMP 737 || iphdr->protocol != IPPROTO_ICMP
758 || hdrlen < HDR_SIZE_IP4 || hdrlen + sizeof (PKT) != totlen) 738 || hdrlen < HDR_SIZE_IP4 || hdrlen + sizeof (PKT) != totlen)
759 continue; 739 continue;
760 740
780_recv_icmp6 (...) 760_recv_icmp6 (...)
781 CODE: 761 CODE:
782{ 762{
783 struct sockaddr_in6 sa; 763 struct sockaddr_in6 sa;
784 PKT pkt; 764 PKT pkt;
765 int maxrecv;
785 766
786 for (;;) 767 for (maxrecv = 256+1; --maxrecv; )
787 { 768 {
788 PINGER *pinger; 769 PINGER *pinger;
789 socklen_t sl = sizeof (sa); 770 socklen_t sl = sizeof (sa);
790 int len = recvfrom (icmp6_fd, &pkt, sizeof (pkt), MSG_TRUNC, (struct sockaddr *)&sa, &sl); 771 int len = recvfrom (icmp6_fd, &pkt, sizeof (pkt), MSG_TRUNC, (struct sockaddr *)&sa, &sl);
791 772
806 } 787 }
807 788
808 recv_flush (); 789 recv_flush ();
809} 790}
810 791
792void
793_new (SV *klass, UV magic1, UV magic2, UV magic3)
794 PPCODE:
795{
796 SV *pv = NEWSV (0, sizeof (PINGER));
797 PINGER *self = (PINGER *)SvPVX (pv);
798
799 SvPOK_only (pv);
800 XPUSHs (sv_2mortal (sv_bless (newRV_noinc (pv), gv_stashpv (SvPVutf8_nolen (klass), 1))));
801 pinger_init (self);
802 self->magic1 = magic1;
803 self->magic2 = magic2;
804 self->magic3 = magic3;
805}
806
807void
808_free (PINGER *self)
809 CODE:
810 pinger_free (self);
811
812IV
813id (PINGER *self, ...)
814 CODE:
815 RETVAL = self->id;
816 OUTPUT:
817 RETVAL
818
819void pinger_start (PINGER *self)
820
821void pinger_stop (PINGER *self)
822
823void
824_stop_id (UV id)
825 CODE:
826 if (id < pingercnt && pingers [id])
827 pinger_stop (pingers [id]);
828
829void
830interval (PINGER *self, NV interval)
831 CODE:
832 NOT_RUNNING;
833 self->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL;
834
835void
836max_rtt (PINGER *self, NV maxrtt)
837 CODE:
838 NOT_RUNNING;
839 self->maxrtt = maxrtt;
840
841void
842on_recv (PINGER *self, SV *cb)
843 CODE:
844 SvREFCNT_dec (self->recvcb);
845 self->recvcb = newSVsv (cb);
846
847void
848add_range (PINGER *self, SV *lo_, SV *hi_, NV interval = 0)
849 CODE:
850{
851 STRLEN lo_len, hi_len;
852 char *lo = SvPVbyte (lo_, lo_len);
853 char *hi = SvPVbyte (hi_, hi_len);
854 RANGE *range;
855 NOT_RUNNING;
856
857 if (lo_len != hi_len || (lo_len != 4 && lo_len != 16))
858 croak ("AnyEvent::FastPing::add_range address range must be specified as two binary IPv4 or IPv6 addresses");
859
860 if (lo_len == 4 && icmp4_fd < 0) croak ("IPv4 support unavailable");
861 if (lo_len == 16 && icmp6_fd < 0) croak ("IPv6 support unavailable");
862
863 if (memcmp (lo, hi, lo_len) > 0)
864 croak ("AnyEvent::FastPing::add_range called with lo > hi");
865
866 range = calloc (1, sizeof (RANGE));
867
868 range->next = 0;
869 range->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL;
870 range->addrlen = lo_len;
871
872 memcpy (sizeof (addr_tt) - lo_len + (char *)&range->lo, lo, lo_len);
873 memcpy (sizeof (addr_tt) - lo_len + (char *)&range->hi, hi, lo_len);
874
875 pinger_add_range (self, range);
876}
877
878void
879add_hosts (PINGER *self, SV *addrs, NV interval = 0, UV interleave = 1)
880 CODE:
881{
882 AV *av;
883 int i, j, k;
884 int cnt;
885 int addrlen = 0;
886 RANGE *range;
887 NOT_RUNNING;
888
889 if (!SvROK (addrs) || SvTYPE (SvRV (addrs)) != SVt_PVAV)
890 croak ("AnyEvent::FastPing::add_hosts expects an arrayref with binary IPv4 or IPv6 addresses");
891
892 av = (AV *)SvRV (addrs);
893 cnt = av_len (av) + 1;
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
909 if (!cnt)
910 XSRETURN_EMPTY;
911
912 range = calloc (1, sizeof (RANGE) + cnt * addrlen);
913
914 range->next = 0;
915 range->interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL;
916 range->addrlen = addrlen;
917 range->addrcnt = cnt;
918
919 if (interleave == 0)
920 interleave = cnt <= 256 * 256 ? 256 : (int)sqrtf (cnt);
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
942 memcpy (dst, pv, addrlen);
943 }
944
945 pinger_add_range (self, range);
946}
947

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines