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

Comparing AnyEvent-FastPing/FPing.xs (file contents):
Revision 1.2 by root, Fri May 4 07:36:45 2007 UTC vs.
Revision 1.7 by root, Fri May 4 16:17:25 2007 UTC

26#include <sys/socket.h> 26#include <sys/socket.h>
27 27
28#include <netinet/in.h> 28#include <netinet/in.h>
29#include <arpa/inet.h> 29#include <arpa/inet.h>
30 30
31#ifdef __linux
32# include <linux/icmp.h>
33#endif
34#if IPV6
35# include <netinet/icmp6.h>
36#endif
37
31#define ICMP4_ECHO 8 38#define ICMP4_ECHO 8
39#define ICMP4_ECHO_REPLY 0
32#define ICMP6_ECHO 128 40#define ICMP6_ECHO 128
41#define ICMP6_ECHO_REPLY 129
33 42
34#define DRAIN_INTERVAL .000001 // how long to wait when sendto returns ENOBUFS, in seconds 43#define DRAIN_INTERVAL .000001 // how long to wait when sendto returns ENOBUFS, in seconds
35#define MIN_INTERVAL .000001 // minimum packet send interval, in seconds 44#define MIN_INTERVAL .000001 // minimum packet send interval, in seconds
36 45
37#define HDR_SIZE_IP4 20 46#define HDR_SIZE_IP4 20
57 double interval; 66 double interval;
58 tstamp next; 67 tstamp next;
59} RANGE; 68} RANGE;
60 69
61typedef struct { 70typedef struct {
62 int send_fd;
63 SV *id; 71 SV *id;
64 double interval; 72 double interval;
65 int nranges; 73 int nranges;
66 RANGE *ranges; 74 RANGE *ranges;
67 uint32_t payload; 75 uint32_t payload;
94 uint8_t src[16]; 102 uint8_t src[16];
95 uint8_t dst[16]; 103 uint8_t dst[16];
96} IP6HDR; 104} IP6HDR;
97 105
98#define MAGIC 0xca4c 106#define MAGIC 0xca4c
107
108static uint16_t magic;
99 109
100typedef struct { 110typedef struct {
101 uint8_t type, code; 111 uint8_t type, code;
102 uint16_t cksum; 112 uint16_t cksum;
103 uint16_t id, seq; 113 uint16_t id, seq;
104 uint32_t payload; 114 uint32_t payload;
105 tstamp stamp; // be careful when accessing this 115 tstamp stamp; // be careful when accessing this
106} PKT; 116} PKT;
107 117
108pthread_t pthrid; 118static pthread_t pthrid;
109int thr_send[2]; // send to worker 119static int thr_send[2]; // send to worker
110int thr_recv[2]; // receive from worker 120static int thr_recv[2]; // receive from worker
111 121
112int icmp4_fd, icmp6_fd; 122static int icmp4_fd, icmp6_fd;
123
124static AV *cbs;
113 125
114static uint16_t 126static uint16_t
115icmp_cksum (void *data, unsigned int len) 127icmp_cksum (void *data, unsigned int len)
116{ 128{
117 register int sum = 0; 129 register int sum = 0;
157 169
158 memset (&pkt, 0, sizeof (pkt)); 170 memset (&pkt, 0, sizeof (pkt));
159 171
160 memset (&sa4, 0, sizeof (sa4)); 172 memset (&sa4, 0, sizeof (sa4));
161 sa4.sin_family = AF_INET; 173 sa4.sin_family = AF_INET;
162 sa4.sin_port = IPPROTO_ICMP; 174 sa4.sin_port = 0;
163#if IPV6 175#if IPV6
164 memset (&sa6, 0, sizeof (sa6)); 176 memset (&sa6, 0, sizeof (sa6));
165 sa6.sin6_family = AF_INET6; 177 sa6.sin6_family = AF_INET6;
166 sa6.sin6_port = IPPROTO_ICMPV6; 178 sa6.sin6_port = 0;
167#endif 179#endif
168 180
169 for (;;) 181 for (;;)
170 { 182 {
171 REQ *req; 183 REQ *req;
180 } 192 }
181 193
182 //TODO: bind to source address 194 //TODO: bind to source address
183 195
184 pkt.code = 0; 196 pkt.code = 0;
185 pkt.id = (uint16_t)MAGIC; 197 pkt.id = (uint16_t)magic;
186 pkt.seq = (uint16_t)~MAGIC; 198 pkt.seq = (uint16_t)~magic;
187 pkt.payload = req->payload; 199 pkt.payload = req->payload;
188 200
189 tstamp next = NOW (); 201 tstamp now = NOW ();
202 tstamp next = now;
203
204 {
205 int r;
206 for (r = req->nranges; r--; )
207 inc_addr (&req->ranges [r].hi);
208 }
190 209
191 while (req->nranges) 210 while (req->nranges)
192 { 211 {
193 RANGE *range = req->ranges; 212 RANGE *range = req->ranges;
194 213
195 if (!memcmp (&range->lo, &range->hi, sizeof (addr_t))) 214 if (!memcmp (&range->lo, &range->hi, sizeof (addr_t)))
196 req->ranges [0] = req->ranges [--req->nranges]; 215 req->ranges [0] = req->ranges [--req->nranges];
197 else 216 else
198 { 217 {
199 tstamp now = NOW ();
200
201 // ranges [0] is always the next range to ping 218 // ranges [0] is always the next range to ping
202 tstamp wait = range->next - now; 219 tstamp wait = range->next - now;
203 220
204 // compare with the global frequency limit 221 // compare with the global frequency limit
205 { 222 {
219 ts.tv_nsec = (wait - ts.tv_sec) * 1000000000.; 236 ts.tv_nsec = (wait - ts.tv_sec) * 1000000000.;
220 237
221 nanosleep (&ts, 0); 238 nanosleep (&ts, 0);
222 } 239 }
223 240
241 now = NOW ();
242
224 pkt.stamp = now; 243 pkt.stamp = now;
225 pkt.cksum = 0; 244 pkt.cksum = 0;
226 245
227 if (range->family == AF_INET) 246 if (range->family == AF_INET)
228 { 247 {
229 pkt.type = ICMP4_ECHO; 248 pkt.type = ICMP4_ECHO;
230 pkt.cksum = icmp_cksum (&pkt, sizeof (pkt));//D 249 pkt.cksum = icmp_cksum (&pkt, sizeof (pkt));
231 250
232 memcpy (&sa4.sin_addr, 251 memcpy (&sa4.sin_addr,
233 sizeof (addr_t) - sizeof (sa4.sin_addr) + (char *)&range->lo, 252 sizeof (addr_t) - sizeof (sa4.sin_addr) + (char *)&range->lo,
234 sizeof (sa4.sin_addr)); 253 sizeof (sa4.sin_addr));
235 254
236 if (sendto (req->send_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa4, sizeof (sa4)) > 0) 255 if (sendto (icmp4_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa4, sizeof (sa4)) > 0)
237 errno = 0; 256 errno = 0;
238 } 257 }
239 else 258 else
240 { 259 {
241#if IPV6 260#if IPV6
243 262
244 memcpy (&sa6.sin6_addr, 263 memcpy (&sa6.sin6_addr,
245 sizeof (addr_t) - sizeof (sa6.sin6_addr) + (char *)&range->lo, 264 sizeof (addr_t) - sizeof (sa6.sin6_addr) + (char *)&range->lo,
246 sizeof (sa6.sin6_addr)); 265 sizeof (sa6.sin6_addr));
247 266
248 if (sendto (req->send_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa6, sizeof (sa6)) > 0) 267 if (sendto (icmp6_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa6, sizeof (sa6)) > 0)
249 errno = 0; 268 errno = 0;
250#endif 269#endif
251 } 270 }
252 271
253 if (errno == ENOBUFS) 272 if (errno == ENOBUFS)
300 319
301 return 0; 320 return 0;
302} 321}
303 322
304static void 323static void
324feed_reply (AV *res_av)
325{
326 if (av_len (res_av) < 0)
327 return;
328
329 dSP;
330 SV *res = sv_2mortal (newRV_inc ((SV *)res_av));
331 int i;
332
333 ENTER;
334 SAVETMPS;
335
336 for (i = av_len (cbs) + 1; i--; )
337 {
338 SV *cb = *av_fetch (cbs, i, 1);
339
340 PUSHMARK (SP);
341 XPUSHs (res);
342 PUTBACK;
343 call_sv (cb, G_DISCARD | G_VOID);
344 }
345
346 FREETMPS;
347 LEAVE;
348}
349
350static void
305boot () 351boot ()
306{ 352{
307 sigset_t fullsigset, oldsigset; 353 sigset_t fullsigset, oldsigset;
308 pthread_attr_t attr; 354 pthread_attr_t attr;
309 355
312 358
313 if (pipe (thr_recv) < 0) 359 if (pipe (thr_recv) < 0)
314 croak ("Net::FPing: unable to create receive pipe"); 360 croak ("Net::FPing: unable to create receive pipe");
315 361
316 icmp4_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); 362 icmp4_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
317
318#ifdef ICMP_FILTER 363#ifdef ICMP_FILTER
319 { 364 {
320 icmp_filter oval; 365 struct icmp_filter oval;
321 oval.data = 0xffffffff & ~(1 << ICMP4_ECHO); 366 oval.data = 0xffffffff & ~(1 << ICMP4_ECHO_REPLY);
322 setsockopt (icmpv4_fd, SOL_RAW, ICMP_FILTER, &oval, sizeof oval); 367 setsockopt (icmp4_fd, SOL_RAW, ICMP_FILTER, &oval, sizeof oval);
323 } 368 }
324#endif 369#endif
325 370
326#if IPV6 371#if IPV6
327 icmp6_fd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 372 icmp6_fd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
373# ifdef ICMP6_FILTER
374 {
375 struct icmp6_filter oval;
376 ICMP6_FILTER_SETBLOCKALL (&oval);
377 ICMP6_FILTER_SETPASS (ICMP6_ECHO_REPLY, &oval);
378 setsockopt (icmp6_fd, IPPROTO_ICMPV6, ICMP6_FILTER, &oval, sizeof oval);
379 }
380# endif
328#endif 381#endif
329 382
330 pthread_attr_init (&attr); 383 pthread_attr_init (&attr);
331 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); 384 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
332#ifdef PTHREAD_SCOPE_PROCESS 385#ifdef PTHREAD_SCOPE_PROCESS
353 406
354BOOT: 407BOOT:
355{ 408{
356 HV *stash = gv_stashpv ("Net::FPing", 1); 409 HV *stash = gv_stashpv ("Net::FPing", 1);
357 410
411 cbs = get_av ("Net::FPing::CB", 1);
412 magic = getpid () ^ MAGIC;
413
358 boot (); 414 boot ();
359 415
360 newCONSTSUB (stash, "ipv4_supported", newSViv (icmp4_fd >= 0)); 416 newCONSTSUB (stash, "ipv4_supported", newSViv (icmp4_fd >= 0));
361 newCONSTSUB (stash, "ipv6_supported", newSViv (icmp6_fd >= 0)); 417 newCONSTSUB (stash, "ipv6_supported", newSViv (icmp6_fd >= 0));
362 418
363 newCONSTSUB (stash, "ipv4_pktsize", newSViv (HDR_SIZE_IP4 + sizeof (PKT))); 419 newCONSTSUB (stash, "icmp4_pktsize", newSViv (HDR_SIZE_IP4 + sizeof (PKT)));
364 newCONSTSUB (stash, "ipv6_pktsize", newSViv (HDR_SIZE_IP6 + sizeof (PKT))); 420 newCONSTSUB (stash, "icmp6_pktsize", newSViv (HDR_SIZE_IP6 + sizeof (PKT)));
365} 421}
366 422
367PROTOTYPES: DISABLE 423PROTOTYPES: DISABLE
368 424
369SV * 425SV *
380 int i; 436 int i;
381 437
382 if (interval < MIN_INTERVAL) 438 if (interval < MIN_INTERVAL)
383 interval = MIN_INTERVAL; 439 interval = MIN_INTERVAL;
384 440
385 req->send_fd = icmp4_fd;
386 req->id = newSVsv (id); 441 req->id = newSVsv (id);
387 req->interval = interval; 442 req->interval = interval;
388 req->payload = payload; 443 req->payload = payload;
389 req->nranges = nranges; 444 req->nranges = nranges;
390 req->ranges = (RANGE *)malloc (nranges * sizeof (RANGE)); 445 req->ranges = (RANGE *)malloc (nranges * sizeof (RANGE));
441 addr = htonl (SvUV (hi)); memcpy (sizeof (addr_t) - 4 + (char *)&r->hi, &addr, 4); 496 addr = htonl (SvUV (hi)); memcpy (sizeof (addr_t) - 4 + (char *)&r->hi, &addr, 4);
442 } 497 }
443 else 498 else
444 croak ("addresses in range must be strings with either 4 (IPv4) or 16 (IPv6) octets"); 499 croak ("addresses in range must be strings with either 4 (IPv4) or 16 (IPv6) octets");
445 500
446 if (r->family = AF_INET) 501 if (r->family == AF_INET)
447 { 502 {
448 if (icmp4_fd < 0) 503 if (icmp4_fd < 0)
449 croak ("Net::FPing: IPv4 ping support not available on this system"); 504 croak ("Net::FPing: IPv4 ping support not available on this system");
450 } 505 }
451 else 506 else
488 CODE: 543 CODE:
489{ 544{
490 char buf [512]; 545 char buf [512];
491 struct sockaddr_in sa; 546 struct sockaddr_in sa;
492 socklen_t sl = sizeof (sa); 547 socklen_t sl = sizeof (sa);
493 AV *res_av = newAV (); 548 AV *res_av = av_len (cbs) < 0 ? 0 : (AV *)sv_2mortal ((SV *)newAV ());
494 SV *res_rv = sv_2mortal (newRV_noinc ((SV *)res_av)); 549 tstamp now = NOW ();
495 550
496 for (;;) 551 for (;;)
497 { 552 {
498 int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl); 553 int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl);
499 554
504 559
505 int hdrlen = (iphdr->version_ihl & 15) * 4; 560 int hdrlen = (iphdr->version_ihl & 15) * 4;
506 int totlen = ntohs (iphdr->tot_len); 561 int totlen = ntohs (iphdr->tot_len);
507 562
508 // packet corrupt? 563 // packet corrupt?
564 if (!res_av
509 if (totlen > len 565 || totlen > len
510 || iphdr->protocol != IPPROTO_ICMP 566 || iphdr->protocol != IPPROTO_ICMP
511 || hdrlen < HDR_SIZE_IP4 || hdrlen + sizeof (PKT) != totlen) 567 || hdrlen < HDR_SIZE_IP4 || hdrlen + sizeof (PKT) != totlen)
512 continue; 568 continue;
513 569
514 PKT *pkt = (PKT *)(buf + hdrlen); 570 PKT *pkt = (PKT *)(buf + hdrlen);
515 571
516 if (pkt->id != (uint16_t)MAGIC 572 if (pkt->type != ICMP4_ECHO_REPLY
573 || pkt->id != (uint16_t) magic
517 || pkt->seq != (uint16_t)~MAGIC 574 || pkt->seq != (uint16_t)~magic
518 || pkt->type != ICMP4_ECHO
519 || !isnormal (pkt->stamp)) 575 || !isnormal (pkt->stamp))
520 continue; 576 continue;
521 577
522 // just drain for now 578 AV *av = newAV ();
523 //av_push 579 av_push (av, newSVpvn ((char *)&sa.sin_addr, 4));
580 av_push (av, newSVnv (now - pkt->stamp));
581 av_push (av, newSVuv (pkt->payload));
582
583 av_push (res_av, newRV_noinc ((SV *)av));
524 } 584 }
525 585
586 if (res_av)
526 // feed (res_av); 587 feed_reply (res_av);
527} 588}
528 589
529void 590void
530_recv_icmp6 (...) 591_recv_icmp6 (...)
531 CODE: 592 CODE:
532{ 593{
533 char buf [512];
534 struct sockaddr_in sa; 594 struct sockaddr_in6 sa;
535 socklen_t sl = sizeof (sa); 595 socklen_t sl = sizeof (sa);
536 AV *res_av = (AV *)sv_2mortal ((SV *)newAV ()); 596 AV *res_av = av_len (cbs) < 0 ? 0 : (AV *)sv_2mortal ((SV *)newAV ());
597 PKT pkt;
598 tstamp now = NOW ();
537 599
538 for (;;) 600 for (;;)
539 { 601 {
540 int len = recvfrom (icmp6_fd, buf, sizeof (buf), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl); 602 int len = recvfrom (icmp6_fd, &pkt, sizeof (pkt), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl);
541 603
542 if (len <= HDR_SIZE_IP6) 604 if (len != sizeof (PKT))
543 break; 605 break;
544 606
545 IP6HDR *iphdr = (IP6HDR *)buf; 607 if (!res_av
546 608 || pkt.type != ICMP6_ECHO_REPLY
547 int datalen = ntohs (iphdr->payload_len); 609 || pkt.id != (uint16_t) magic
548 610 || pkt.seq != (uint16_t)~magic
549 // packet corrupt? 611 || !isnormal (pkt.stamp))
550 if (HDR_SIZE_IP6 + datalen > len
551 || iphdr->nxt_hdr != IPPROTO_ICMPV6
552 || HDR_SIZE_IP6 + sizeof (PKT) != datalen)
553 continue; 612 continue;
554 613
555 PKT *pkt = (PKT *)(buf + HDR_SIZE_IP6); 614 AV *av = newAV ();
615 av_push (av, newSVpvn ((char *)&sa.sin6_addr, 16));
616 av_push (av, newSVnv (now - pkt.stamp));
617 av_push (av, newSVuv (pkt.payload));
556 618
557 if (pkt->id != (uint16_t)MAGIC 619 av_push (res_av, newRV_noinc ((SV *)av));
558 || pkt->seq != (uint16_t)~MAGIC
559 || pkt->type != ICMP6_ECHO
560 || !isnormal (pkt->stamp))
561 continue;
562
563 //fprintf (stderr, "ip6 echo received\n");
564 // just drain for now
565 //av_push
566 } 620 }
567 621
622 if (res_av)
568 // feed (res_av); 623 feed_reply (res_av);
569} 624}
570 625

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines