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.5 by root, Fri Jul 3 09:26:19 2009 UTC vs.
Revision 1.8 by root, Sat Jan 29 23:36:49 2011 UTC

1#if defined(__linux) 1#if defined(__linux) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__CYGWIN__)
2# define ENABLE_IPV6 1 // if you get compilation problems try to disable IPv6 2# define ENABLE_IPV6 1 // if you get compilation problems try to disable IPv6
3#else 3#else
4# define ENABLE_IPV6 0 4# define ENABLE_IPV6 0
5#endif 5#endif
6 6
30#include <arpa/inet.h> 30#include <arpa/inet.h>
31 31
32#ifdef __linux 32#ifdef __linux
33# include <linux/icmp.h> 33# include <linux/icmp.h>
34#endif 34#endif
35#if ENABLE_IPV6 35#if ENABLE_IPV6 && !defined (__CYGWIN__)
36# include <netinet/icmp6.h> 36# include <netinet/icmp6.h>
37#endif 37#endif
38 38
39#define ICMP4_ECHO 8 39#define ICMP4_ECHO 8
40#define ICMP4_ECHO_REPLY 0 40#define ICMP4_ECHO_REPLY 0
47#define HDR_SIZE_IP4 20 47#define HDR_SIZE_IP4 20
48#define HDR_SIZE_IP6 48 48#define HDR_SIZE_IP6 48
49 49
50//TODO: xread/xwrite for atomicity? we currently rely on the fact that the pip buffersize divides exactly by pointer sizes 50//TODO: xread/xwrite for atomicity? we currently rely on the fact that the pip buffersize divides exactly by pointer sizes
51 51
52typedef uint8_t addr_t[16]; 52typedef uint8_t addr_tt[16];
53 53
54typedef double tstamp; 54typedef double tstamp;
55 55
56static tstamp 56static tstamp
57NOW (void) 57NOW (void)
59 struct timeval tv; 59 struct timeval tv;
60 gettimeofday (&tv, 0); 60 gettimeofday (&tv, 0);
61 return tv.tv_sec + tv.tv_usec * 0.000001; 61 return tv.tv_sec + tv.tv_usec * 0.000001;
62} 62}
63 63
64typedef struct { 64typedef struct
65{
65 int family; 66 int family;
66 addr_t lo, hi; 67 addr_tt lo, hi;
67 double interval; 68 double interval;
68 tstamp next; 69 tstamp next;
69} RANGE; 70} RANGE;
70 71
71typedef struct { 72typedef struct
73{
72 SV *id; 74 SV *id;
73 double interval; 75 double interval;
74 int nranges; 76 int nranges;
75 RANGE *ranges; 77 RANGE *ranges;
76 uint32_t payload; 78 uint32_t payload;
77} REQ; 79} REQ;
78 80
79typedef struct { 81typedef struct
82{
80 uint8_t version_ihl; 83 uint8_t version_ihl;
81 uint8_t tos; 84 uint8_t tos;
82 uint16_t tot_len; 85 uint16_t tot_len;
83 86
84 uint16_t id; 87 uint16_t id;
90 93
91 uint32_t src; 94 uint32_t src;
92 uint32_t dst; 95 uint32_t dst;
93} IP4HDR; 96} IP4HDR;
94 97
95typedef struct { 98typedef struct
99{
96 uint8_t version; 100 uint8_t version;
97 uint8_t x1, x2, x3; 101 uint8_t x1, x2, x3;
98 102
99 uint16_t payload_len; 103 uint16_t payload_len;
100 uint8_t nxt_hdr; 104 uint8_t nxt_hdr;
106 110
107#define MAGIC 0xca4c 111#define MAGIC 0xca4c
108 112
109static uint16_t magic; 113static uint16_t magic;
110 114
111typedef struct { 115typedef struct
116{
112 uint8_t type, code; 117 uint8_t type, code;
113 uint16_t cksum; 118 uint16_t cksum;
114 uint16_t id, seq; 119 uint16_t id, seq;
115 uint32_t payload; 120 uint32_t payload;
116 tstamp stamp; // be careful when accessing this 121 tstamp stamp; // be careful when accessing this
140 145
141 return ~sum; 146 return ~sum;
142} 147}
143 148
144static void 149static void
145inc_addr (addr_t *addr) 150inc_addr (addr_tt *addr)
146{ 151{
147 int len = sizeof (addr_t) - 1; 152 int len = sizeof (addr_tt) - 1;
148 153
149 do 154 do
150 { 155 {
151 if ((*addr)[len] != 0xff) 156 if ((*addr)[len] != 0xff)
152 { 157 {
182 for (;;) 187 for (;;)
183 { 188 {
184 REQ *req; 189 REQ *req;
185 int len = read (thr_send [0], &req, sizeof (req)); 190 int len = read (thr_send [0], &req, sizeof (req));
186 191
192 tstamp now = NOW ();
193 tstamp next = now;
194
187 if (!len) 195 if (!len)
188 pthread_exit (0); 196 pthread_exit (0);
189 else if (len != sizeof (req)) 197 else if (len != sizeof (req))
190 { 198 {
191 perror ("AnyEvent::FastPing: short reead or read error"); 199 perror ("AnyEvent::FastPing: short read or read error");
192 pthread_exit ((void *)-1); 200 pthread_exit ((void *)-1);
193 } 201 }
194 202
195 //TODO: bind to source address 203 //TODO: bind to source address
196 204
197 pkt.code = 0; 205 pkt.code = 0;
198 pkt.id = (uint16_t)magic; 206 pkt.id = (uint16_t)magic;
199 pkt.seq = (uint16_t)~magic; 207 pkt.seq = (uint16_t)~magic;
200 pkt.payload = req->payload; 208 pkt.payload = req->payload;
201
202 tstamp now = NOW ();
203 tstamp next = now;
204 209
205 { 210 {
206 int r; 211 int r;
207 for (r = req->nranges; r--; ) 212 for (r = req->nranges; r--; )
208 inc_addr (&req->ranges [r].hi); 213 inc_addr (&req->ranges [r].hi);
209 } 214 }
210 215
211 while (req->nranges) 216 while (req->nranges)
212 { 217 {
213 RANGE *range = req->ranges; 218 RANGE *range = req->ranges;
219 int n, k;
214 220
215 if (!memcmp (&range->lo, &range->hi, sizeof (addr_t))) 221 if (!memcmp (&range->lo, &range->hi, sizeof (addr_tt)))
216 req->ranges [0] = req->ranges [--req->nranges]; 222 req->ranges [0] = req->ranges [--req->nranges];
217 else 223 else
218 { 224 {
219 // ranges [0] is always the next range to ping 225 // ranges [0] is always the next range to ping
220 tstamp wait = range->next - now; 226 tstamp wait = range->next - now;
248 { 254 {
249 pkt.type = ICMP4_ECHO; 255 pkt.type = ICMP4_ECHO;
250 pkt.cksum = icmp_cksum (&pkt, sizeof (pkt)); 256 pkt.cksum = icmp_cksum (&pkt, sizeof (pkt));
251 257
252 memcpy (&sa4.sin_addr, 258 memcpy (&sa4.sin_addr,
253 sizeof (addr_t) - sizeof (sa4.sin_addr) + (char *)&range->lo, 259 sizeof (addr_tt) - sizeof (sa4.sin_addr) + (char *)&range->lo,
254 sizeof (sa4.sin_addr)); 260 sizeof (sa4.sin_addr));
255 261
256 if (sendto (icmp4_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa4, sizeof (sa4)) > 0) 262 if (sendto (icmp4_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa4, sizeof (sa4)) > 0)
257 errno = 0; 263 errno = 0;
258 } 264 }
260 { 266 {
261#if ENABLE_IPV6 267#if ENABLE_IPV6
262 pkt.type = ICMP6_ECHO; 268 pkt.type = ICMP6_ECHO;
263 269
264 memcpy (&sa6.sin6_addr, 270 memcpy (&sa6.sin6_addr,
265 sizeof (addr_t) - sizeof (sa6.sin6_addr) + (char *)&range->lo, 271 sizeof (addr_tt) - sizeof (sa6.sin6_addr) + (char *)&range->lo,
266 sizeof (sa6.sin6_addr)); 272 sizeof (sa6.sin6_addr));
267 273
268 if (sendto (icmp6_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa6, sizeof (sa6)) > 0) 274 if (sendto (icmp6_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa6, sizeof (sa6)) > 0)
269 errno = 0; 275 errno = 0;
270#endif 276#endif
289 295
290 next += req->interval; 296 next += req->interval;
291 } 297 }
292 298
293 // make a downheap operation 299 // make a downheap operation
294 int k = 0;
295 int n = 0;
296 for (;;) 300 for (n = k = 0; ; )
297 { 301 {
302 int j = k * 2 + 1;
303
298 ++n; 304 ++n;
299 int j = k * 2 + 1;
300 305
301 if (j >= req->nranges) 306 if (j >= req->nranges)
302 break; 307 break;
303 else if (j < req->nranges - 1) 308 else if (j < req->nranges - 1)
304 if (req->ranges [j].next > req->ranges [j + 1].next) 309 if (req->ranges [j].next > req->ranges [j + 1].next)
305 ++j; 310 ++j;
306 311
307 if (req->ranges [j].next >= req->ranges [k].next) 312 if (req->ranges [j].next >= req->ranges [k].next)
308 break; 313 break;
309 314
315 {
310 RANGE temp = req->ranges [k]; 316 RANGE temp = req->ranges [k];
311 req->ranges [k] = req->ranges [j]; 317 req->ranges [k] = req->ranges [j];
312 req->ranges [j] = temp; 318 req->ranges [j] = temp;
319 }
313 320
314 k = j; 321 k = j;
315 } 322 }
316 } 323 }
317 324
322} 329}
323 330
324static void 331static void
325feed_reply (AV *res_av) 332feed_reply (AV *res_av)
326{ 333{
327 if (av_len (res_av) < 0)
328 return;
329
330 dSP; 334 dSP;
331 SV *res = sv_2mortal (newRV_inc ((SV *)res_av)); 335 SV *res = sv_2mortal (newRV_inc ((SV *)res_av));
332 int i; 336 int i;
337
338 if (av_len (res_av) < 0)
339 return;
333 340
334 ENTER; 341 ENTER;
335 SAVETMPS; 342 SAVETMPS;
336 343
337 for (i = av_len (cbs) + 1; i--; ) 344 for (i = av_len (cbs) + 1; i--; )
427 434
428SV * 435SV *
429_req_icmp_ping (SV *ranges, NV interval, U32 payload, SV *id) 436_req_icmp_ping (SV *ranges, NV interval, U32 payload, SV *id)
430 CODE: 437 CODE:
431{ 438{
439 AV *rav;
440 int nranges, i;
441 REQ *req;
442
432 if (!SvROK (ranges) || SvTYPE (SvRV (ranges)) != SVt_PVAV) 443 if (!SvROK (ranges) || SvTYPE (SvRV (ranges)) != SVt_PVAV)
433 croak ("address ranges must be given as arrayref with lo, hi pairs"); 444 croak ("address ranges must be given as arrayref with lo, hi pairs");
434 445
435 AV *rav = (AV *)SvRV (ranges); 446 rav = (AV *)SvRV (ranges);
436 int nranges = av_len (rav) + 1; 447 nranges = av_len (rav) + 1;
437 448
438 REQ *req = malloc (sizeof (REQ)); 449 req = malloc (sizeof (REQ));
439 int i;
440 450
441 if (interval < MIN_INTERVAL) 451 if (interval < MIN_INTERVAL)
442 interval = MIN_INTERVAL; 452 interval = MIN_INTERVAL;
443 453
444 req->id = newSVsv (id); 454 req->id = newSVsv (id);
448 req->ranges = (RANGE *)malloc (nranges * sizeof (RANGE)); 458 req->ranges = (RANGE *)malloc (nranges * sizeof (RANGE));
449 459
450 while (nranges--) 460 while (nranges--)
451 { 461 {
452 SV *sv = *av_fetch (rav, nranges, 1); 462 SV *sv = *av_fetch (rav, nranges, 1);
463 SV *lo, *hi;
464 AV *av;
465 RANGE *r;
453 466
454 if (!SvROK (sv) || SvTYPE (SvRV (sv)) != SVt_PVAV) 467 if (!SvROK (sv) || SvTYPE (SvRV (sv)) != SVt_PVAV)
455 croak ("address range must be given as arrayref with lo, hi, interval arrayrefs"); 468 croak ("address range must be given as arrayref with lo, hi, interval arrayrefs");
456 469
457 AV *av = (AV *)SvRV (sv); 470 av = (AV *)SvRV (sv);
458 RANGE *r = req->ranges + nranges; 471 r = req->ranges + nranges;
459 472
460 SV *lo = *av_fetch (av, 0, 1); 473 lo = *av_fetch (av, 0, 1);
461 SV *hi = *av_fetch (av, 1, 1); 474 hi = *av_fetch (av, 1, 1);
462 475
463 sv_utf8_downgrade (lo, 0); 476 sv_utf8_downgrade (lo, 0);
464 sv_utf8_downgrade (hi, 0); 477 sv_utf8_downgrade (hi, 0);
465 478
466 memset (&r->lo, 0, sizeof (addr_t)); 479 memset (&r->lo, 0, sizeof (addr_tt));
467 memset (&r->hi, 0, sizeof (addr_t)); 480 memset (&r->hi, 0, sizeof (addr_tt));
468 481
469 if (SvPOKp (lo) && SvPOKp (hi)) 482 if (SvPOKp (lo) && SvPOKp (hi))
470 { 483 {
471 if (SvCUR (lo) != SvCUR (hi)) 484 if (SvCUR (lo) != SvCUR (hi))
472 croak ("all addresses in range must be of the same size (either 4 or 16 bytes)"); 485 croak ("all addresses in range must be of the same size (either 4 or 16 bytes)");
473 486
474 if (SvCUR (lo) == 4) 487 if (SvCUR (lo) == 4)
475 { 488 {
476 r->family = AF_INET; 489 r->family = AF_INET;
477 memcpy (sizeof (addr_t) - 4 + (char *)&r->lo, SvPVX (lo), 4); 490 memcpy (sizeof (addr_tt) - 4 + (char *)&r->lo, SvPVX (lo), 4);
478 memcpy (sizeof (addr_t) - 4 + (char *)&r->hi, SvPVX (hi), 4); 491 memcpy (sizeof (addr_tt) - 4 + (char *)&r->hi, SvPVX (hi), 4);
479 } 492 }
480 else if (SvCUR (lo) == 16) 493 else if (SvCUR (lo) == 16)
481 { 494 {
482#if ENABLE_IPV6 495#if ENABLE_IPV6
483 r->family = AF_INET6; 496 r->family = AF_INET6;
484 memcpy (&r->lo, SvPVX (lo), sizeof (addr_t)); 497 memcpy (&r->lo, SvPVX (lo), sizeof (addr_tt));
485 memcpy (&r->hi, SvPVX (hi), sizeof (addr_t)); 498 memcpy (&r->hi, SvPVX (hi), sizeof (addr_tt));
486#else 499#else
487 croak ("IPv6 not supported in this configuration"); 500 croak ("IPv6 not supported in this configuration");
488#endif 501#endif
489 } 502 }
490 else 503 else
491 croak ("addresses in range must be either 4 (IPv4) or 16 (IPv6) bytes in length"); 504 croak ("addresses in range must be either 4 (IPv4) or 16 (IPv6) bytes in length");
492 } 505 }
493 else if (SvIOK (lo) && SvIOK (hi)) 506 else if (SvIOK (lo) && SvIOK (hi))
494 { 507 {
508 uint32_t addr;
509
495 r->family = AF_INET; 510 r->family = AF_INET;
496 511
497 uint32_t addr;
498 addr = htonl (SvUV (lo)); memcpy (sizeof (addr_t) - 4 + (char *)&r->lo, &addr, 4); 512 addr = htonl (SvUV (lo)); memcpy (sizeof (addr_tt) - 4 + (char *)&r->lo, &addr, 4);
499 addr = htonl (SvUV (hi)); memcpy (sizeof (addr_t) - 4 + (char *)&r->hi, &addr, 4); 513 addr = htonl (SvUV (hi)); memcpy (sizeof (addr_tt) - 4 + (char *)&r->hi, &addr, 4);
500 } 514 }
501 else 515 else
502 croak ("addresses in range must be strings with either 4 (IPv4) or 16 (IPv6) octets"); 516 croak ("addresses in range must be strings with either 4 (IPv4) or 16 (IPv6) octets");
503 517
504 if (r->family == AF_INET) 518 if (r->family == AF_INET)
551 AV *res_av = av_len (cbs) < 0 ? 0 : (AV *)sv_2mortal ((SV *)newAV ()); 565 AV *res_av = av_len (cbs) < 0 ? 0 : (AV *)sv_2mortal ((SV *)newAV ());
552 tstamp now = NOW (); 566 tstamp now = NOW ();
553 567
554 for (;;) 568 for (;;)
555 { 569 {
570 IP4HDR *iphdr = (IP4HDR *)buf;
556 int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_TRUNC, (struct sockaddr *)&sa, &sl); 571 int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_TRUNC, (struct sockaddr *)&sa, &sl);
572 int hdrlen, totlen;
573 PKT *pkt;
557 574
558 if (len <= HDR_SIZE_IP4) 575 if (len <= HDR_SIZE_IP4)
559 break; 576 break;
560 577
561 IP4HDR *iphdr = (IP4HDR *)buf;
562
563 int hdrlen = (iphdr->version_ihl & 15) * 4; 578 hdrlen = (iphdr->version_ihl & 15) * 4;
564 int totlen = ntohs (iphdr->tot_len); 579 totlen = ntohs (iphdr->tot_len);
565 580
566 // packet corrupt? 581 // packet corrupt?
567 if (!res_av 582 if (!res_av
568 || totlen > len 583 || totlen > len
569 || iphdr->protocol != IPPROTO_ICMP 584 || iphdr->protocol != IPPROTO_ICMP
570 || hdrlen < HDR_SIZE_IP4 || hdrlen + sizeof (PKT) != totlen) 585 || hdrlen < HDR_SIZE_IP4 || hdrlen + sizeof (PKT) != totlen)
571 continue; 586 continue;
572 587
573 PKT *pkt = (PKT *)(buf + hdrlen); 588 pkt = (PKT *)(buf + hdrlen);
574 589
575 if (pkt->type != ICMP4_ECHO_REPLY 590 if (pkt->type != ICMP4_ECHO_REPLY
576 || pkt->id != (uint16_t) magic 591 || pkt->id != (uint16_t) magic
577 || pkt->seq != (uint16_t)~magic 592 || pkt->seq != (uint16_t)~magic
578 || !isnormal (pkt->stamp)) 593 || !isnormal (pkt->stamp))
579 continue; 594 continue;
580 595
596 {
581 AV *av = newAV (); 597 AV *av = newAV ();
582 av_push (av, newSVpvn ((char *)&sa.sin_addr, 4)); 598 av_push (av, newSVpvn ((char *)&sa.sin_addr, 4));
583 av_push (av, newSVnv (now - pkt->stamp)); 599 av_push (av, newSVnv (now - pkt->stamp));
584 av_push (av, newSVuv (pkt->payload)); 600 av_push (av, newSVuv (pkt->payload));
585 601
586 av_push (res_av, newRV_noinc ((SV *)av)); 602 av_push (res_av, newRV_noinc ((SV *)av));
603 }
587 } 604 }
588 605
589 if (res_av) 606 if (res_av)
590 feed_reply (res_av); 607 feed_reply (res_av);
591} 608}
612 || pkt.id != (uint16_t) magic 629 || pkt.id != (uint16_t) magic
613 || pkt.seq != (uint16_t)~magic 630 || pkt.seq != (uint16_t)~magic
614 || !isnormal (pkt.stamp)) 631 || !isnormal (pkt.stamp))
615 continue; 632 continue;
616 633
634 {
617 AV *av = newAV (); 635 AV *av = newAV ();
618 av_push (av, newSVpvn ((char *)&sa.sin6_addr, 16)); 636 av_push (av, newSVpvn ((char *)&sa.sin6_addr, 16));
619 av_push (av, newSVnv (now - pkt.stamp)); 637 av_push (av, newSVnv (now - pkt.stamp));
620 av_push (av, newSVuv (pkt.payload)); 638 av_push (av, newSVuv (pkt.payload));
621 639
622 av_push (res_av, newRV_noinc ((SV *)av)); 640 av_push (res_av, newRV_noinc ((SV *)av));
641 }
623 } 642 }
624 643
625 if (res_av) 644 if (res_av)
626 feed_reply (res_av); 645 feed_reply (res_av);
627} 646}

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines