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.1 by root, Fri May 4 07:02:19 2007 UTC vs.
Revision 1.6 by root, Fri May 4 15:44:06 2007 UTC

1#define _POSIX_C_SOURCE 199309 1#define _POSIX_C_SOURCE 199309
2#define _GNU_SOURCE 1 2#define _GNU_SOURCE 1
3 3
4#define IPV6 1 4#define IPV6 1 // if you get compilation problems try to disable IPv6
5 5
6#include "EXTERN.h" 6#include "EXTERN.h"
7#include "perl.h" 7#include "perl.h"
8#include "XSUB.h" 8#include "XSUB.h"
9 9
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;
99 107
100typedef struct { 108typedef struct {
101 uint8_t type, code; 109 uint8_t type, code;
102 uint16_t cksum; 110 uint16_t cksum;
103 uint16_t id, seq; 111 uint16_t id, seq;
112 uint32_t payload;
104 tstamp stamp; // be careful when accessing this 113 tstamp stamp; // be careful when accessing this
105 uint32_t payload;
106} PKT; 114} PKT;
107 115
108pthread_t pthrid; 116static pthread_t pthrid;
109int thr_send[2]; // send to worker 117static int thr_send[2]; // send to worker
110int thr_recv[2]; // receive from worker 118static int thr_recv[2]; // receive from worker
111 119
112int icmp4_fd, icmp6_fd; 120static int icmp4_fd, icmp6_fd;
113 121
114uint16_t 122static AV *cbs;
123
124static uint16_t
115icmp_cksum (void *data, unsigned int len) 125icmp_cksum (void *data, unsigned int len)
116{ 126{
117 register int sum = 0; 127 register int sum = 0;
118 uint16_t *wp; 128 uint16_t *wp;
119 129
157 167
158 memset (&pkt, 0, sizeof (pkt)); 168 memset (&pkt, 0, sizeof (pkt));
159 169
160 memset (&sa4, 0, sizeof (sa4)); 170 memset (&sa4, 0, sizeof (sa4));
161 sa4.sin_family = AF_INET; 171 sa4.sin_family = AF_INET;
162 sa4.sin_port = 0; // unused 172 sa4.sin_port = 0;
163#if IPV6 173#if IPV6
164 memset (&sa6, 0, sizeof (sa6)); 174 memset (&sa6, 0, sizeof (sa6));
165 sa6.sin6_family = AF_INET6; 175 sa6.sin6_family = AF_INET6;
166 sa6.sin6_port = 0; // unused 176 sa6.sin6_port = 0;
167#endif 177#endif
168 178
169 for (;;) 179 for (;;)
170 { 180 {
171 REQ *req; 181 REQ *req;
184 pkt.code = 0; 194 pkt.code = 0;
185 pkt.id = (uint16_t)MAGIC; 195 pkt.id = (uint16_t)MAGIC;
186 pkt.seq = (uint16_t)~MAGIC; 196 pkt.seq = (uint16_t)~MAGIC;
187 pkt.payload = req->payload; 197 pkt.payload = req->payload;
188 198
189 tstamp next = NOW (); 199 tstamp now = NOW ();
200 tstamp next = now;
201
202 {
203 int r;
204 for (r = req->nranges; r--; )
205 inc_addr (&req->ranges [r].hi);
206 }
190 207
191 while (req->nranges) 208 while (req->nranges)
192 { 209 {
193 RANGE *range = req->ranges; 210 RANGE *range = req->ranges;
194 211
195 if (!memcmp (&range->lo, &range->hi, sizeof (addr_t))) 212 if (!memcmp (&range->lo, &range->hi, sizeof (addr_t)))
196 req->ranges [0] = req->ranges [--req->nranges]; 213 req->ranges [0] = req->ranges [--req->nranges];
197 else 214 else
198 { 215 {
199 tstamp now = NOW ();
200
201 // ranges [0] is always the next range to ping 216 // ranges [0] is always the next range to ping
202 tstamp wait = range->next - now; 217 tstamp wait = range->next - now;
203 218
204 // compare with the global frequency limit 219 // compare with the global frequency limit
205 { 220 {
219 ts.tv_nsec = (wait - ts.tv_sec) * 1000000000.; 234 ts.tv_nsec = (wait - ts.tv_sec) * 1000000000.;
220 235
221 nanosleep (&ts, 0); 236 nanosleep (&ts, 0);
222 } 237 }
223 238
239 now = NOW ();
240
224 pkt.stamp = now; 241 pkt.stamp = now;
225 pkt.cksum = 0; 242 pkt.cksum = 0;
226 243
227 if (range->family == AF_INET) 244 if (range->family == AF_INET)
228 { 245 {
231 248
232 memcpy (&sa4.sin_addr, 249 memcpy (&sa4.sin_addr,
233 sizeof (addr_t) - sizeof (sa4.sin_addr) + (char *)&range->lo, 250 sizeof (addr_t) - sizeof (sa4.sin_addr) + (char *)&range->lo,
234 sizeof (sa4.sin_addr)); 251 sizeof (sa4.sin_addr));
235 252
236 if (sendto (req->send_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa4, sizeof (sa4)) > 0) 253 if (sendto (icmp4_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa4, sizeof (sa4)) > 0)
237 errno = 0; 254 errno = 0;
238 } 255 }
239 else 256 else
240 { 257 {
241#if IPV6 258#if IPV6
243 260
244 memcpy (&sa6.sin6_addr, 261 memcpy (&sa6.sin6_addr,
245 sizeof (addr_t) - sizeof (sa6.sin6_addr) + (char *)&range->lo, 262 sizeof (addr_t) - sizeof (sa6.sin6_addr) + (char *)&range->lo,
246 sizeof (sa6.sin6_addr)); 263 sizeof (sa6.sin6_addr));
247 264
248 if (sendto (req->send_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa6, sizeof (sa6)) > 0) 265 if (sendto (icmp6_fd, &pkt, sizeof (pkt), 0, (struct sockaddr *)&sa6, sizeof (sa6)) > 0)
249 errno = 0; 266 errno = 0;
250#endif 267#endif
251 } 268 }
252 269
253 if (errno == ENOBUFS) 270 if (errno == ENOBUFS)
300 317
301 return 0; 318 return 0;
302} 319}
303 320
304static void 321static void
322feed_reply (AV *res_av)
323{
324 if (av_len (res_av) < 0)
325 return;
326
327 dSP;
328 SV *res = sv_2mortal (newRV_inc ((SV *)res_av));
329 int i;
330
331 ENTER;
332 SAVETMPS;
333
334 for (i = av_len (cbs) + 1; i--; )
335 {
336 SV *cb = *av_fetch (cbs, i, 1);
337
338 PUSHMARK (SP);
339 XPUSHs (res);
340 PUTBACK;
341 call_sv (cb, G_DISCARD | G_VOID);
342 }
343
344 FREETMPS;
345 LEAVE;
346}
347
348static void
305boot () 349boot ()
306{ 350{
307 sigset_t fullsigset, oldsigset; 351 sigset_t fullsigset, oldsigset;
308 pthread_attr_t attr; 352 pthread_attr_t attr;
309 353
312 356
313 if (pipe (thr_recv) < 0) 357 if (pipe (thr_recv) < 0)
314 croak ("Net::FPing: unable to create receive pipe"); 358 croak ("Net::FPing: unable to create receive pipe");
315 359
316 icmp4_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); 360 icmp4_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
361#ifdef ICMP_FILTER
362 {
363 struct icmp_filter oval;
364 oval.data = 0xffffffff & ~(1 << ICMP4_ECHO_REPLY);
365 setsockopt (icmp4_fd, SOL_RAW, ICMP_FILTER, &oval, sizeof oval);
366 }
367#endif
368
317#if IPV6 369#if IPV6
318 icmp6_fd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 370 icmp6_fd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
371# ifdef ICMP6_FILTER
372 {
373 struct icmp6_filter oval;
374 ICMP6_FILTER_SETBLOCKALL (&oval);
375 ICMP6_FILTER_SETPASS (ICMP6_ECHO_REPLY, &oval);
376 setsockopt (icmp6_fd, IPPROTO_ICMPV6, ICMP6_FILTER, &oval, sizeof oval);
377 }
378# endif
319#endif 379#endif
320 380
321 pthread_attr_init (&attr); 381 pthread_attr_init (&attr);
322 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); 382 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
323#ifdef PTHREAD_SCOPE_PROCESS 383#ifdef PTHREAD_SCOPE_PROCESS
341} 401}
342 402
343MODULE = Net::FPing PACKAGE = Net::FPing 403MODULE = Net::FPing PACKAGE = Net::FPing
344 404
345BOOT: 405BOOT:
406{
407 HV *stash = gv_stashpv ("Net::FPing", 1);
408
409 cbs = get_av ("Net::FPing::CB", 1);
410
346 boot (); 411 boot ();
347 412
413 newCONSTSUB (stash, "ipv4_supported", newSViv (icmp4_fd >= 0));
414 newCONSTSUB (stash, "ipv6_supported", newSViv (icmp6_fd >= 0));
415
416 newCONSTSUB (stash, "icmp4_pktsize", newSViv (HDR_SIZE_IP4 + sizeof (PKT)));
417 newCONSTSUB (stash, "icmp6_pktsize", newSViv (HDR_SIZE_IP6 + sizeof (PKT)));
418}
419
348PROTOTYPES: DISABLE 420PROTOTYPES: DISABLE
349 421
350SV * 422SV *
351_req_ranges4 (SV *ranges, NV interval, U32 payload, SV *id) 423_req_icmp_ping (SV *ranges, NV interval, U32 payload, SV *id)
352 CODE: 424 CODE:
353{ 425{
354 if (!SvROK (ranges) || SvTYPE (SvRV (ranges)) != SVt_PVAV) 426 if (!SvROK (ranges) || SvTYPE (SvRV (ranges)) != SVt_PVAV)
355 croak ("address ranges must be given as arrayref with lo, hi pairs"); 427 croak ("address ranges must be given as arrayref with lo, hi pairs");
356 428
361 int i; 433 int i;
362 434
363 if (interval < MIN_INTERVAL) 435 if (interval < MIN_INTERVAL)
364 interval = MIN_INTERVAL; 436 interval = MIN_INTERVAL;
365 437
366 req->send_fd = icmp4_fd;
367 req->id = newSVsv (id); 438 req->id = newSVsv (id);
368 req->interval = interval; 439 req->interval = interval;
369 req->payload = payload; 440 req->payload = payload;
370 req->nranges = nranges; 441 req->nranges = nranges;
371 req->ranges = (RANGE *)malloc (nranges * sizeof (RANGE)); 442 req->ranges = (RANGE *)malloc (nranges * sizeof (RANGE));
422 addr = htonl (SvUV (hi)); memcpy (sizeof (addr_t) - 4 + (char *)&r->hi, &addr, 4); 493 addr = htonl (SvUV (hi)); memcpy (sizeof (addr_t) - 4 + (char *)&r->hi, &addr, 4);
423 } 494 }
424 else 495 else
425 croak ("addresses in range must be strings with either 4 (IPv4) or 16 (IPv6) octets"); 496 croak ("addresses in range must be strings with either 4 (IPv4) or 16 (IPv6) octets");
426 497
427 if (r->family = AF_INET) 498 if (r->family == AF_INET)
428 { 499 {
429 if (icmp4_fd < 0) 500 if (icmp4_fd < 0)
430 croak ("Net::FPing: IPv4 ping support not available on this system"); 501 croak ("Net::FPing: IPv4 ping support not available on this system");
431 } 502 }
432 else 503 else
469 CODE: 540 CODE:
470{ 541{
471 char buf [512]; 542 char buf [512];
472 struct sockaddr_in sa; 543 struct sockaddr_in sa;
473 socklen_t sl = sizeof (sa); 544 socklen_t sl = sizeof (sa);
474 AV *res_av = newAV (); 545 AV *res_av = av_len (cbs) < 0 ? 0 : (AV *)sv_2mortal ((SV *)newAV ());
475 SV *res_rv = sv_2mortal (newRV_noinc ((SV *)res_av)); 546 tstamp now = NOW ();
476 547
477 for (;;) 548 for (;;)
478 { 549 {
479 int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl); 550 int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl);
480 551
485 556
486 int hdrlen = (iphdr->version_ihl & 15) * 4; 557 int hdrlen = (iphdr->version_ihl & 15) * 4;
487 int totlen = ntohs (iphdr->tot_len); 558 int totlen = ntohs (iphdr->tot_len);
488 559
489 // packet corrupt? 560 // packet corrupt?
561 if (!res_av
490 if (totlen > len 562 || totlen > len
491 || iphdr->protocol != IPPROTO_ICMP 563 || iphdr->protocol != IPPROTO_ICMP
492 || hdrlen < HDR_SIZE_IP4 || hdrlen + sizeof (PKT) != totlen) 564 || hdrlen < HDR_SIZE_IP4 || hdrlen + sizeof (PKT) != totlen)
493 continue; 565 continue;
494 566
495 PKT *pkt = (PKT *)(buf + hdrlen); 567 PKT *pkt = (PKT *)(buf + hdrlen);
496 568
569 if (pkt->type != ICMP4_ECHO_REPLY
497 if (pkt->id != (uint16_t)MAGIC 570 || pkt->id != (uint16_t) MAGIC
498 || pkt->seq != (uint16_t)~MAGIC 571 || pkt->seq != (uint16_t)~MAGIC
499 || pkt->type != ICMP4_ECHO
500 || !isnormal (pkt->stamp)) 572 || !isnormal (pkt->stamp))
501 continue; 573 continue;
502 574
503 // just drain for now 575 AV *av = newAV ();
504 //av_push 576 av_push (av, newSVpvn ((char *)&sa.sin_addr, 4));
577 av_push (av, newSVnv (now - pkt->stamp));
578 av_push (av, newSVuv (pkt->payload));
579
580 av_push (res_av, newRV_noinc ((SV *)av));
505 } 581 }
506 582
583 if (res_av)
507 // feed (res_av); 584 feed_reply (res_av);
508} 585}
509 586
510void 587void
511_recv_icmp6 (...) 588_recv_icmp6 (...)
512 CODE: 589 CODE:
513{ 590{
514 char buf [512];
515 struct sockaddr_in sa; 591 struct sockaddr_in6 sa;
516 socklen_t sl = sizeof (sa); 592 socklen_t sl = sizeof (sa);
517 AV *res_av = (AV *)sv_2mortal ((SV *)newAV ()); 593 AV *res_av = av_len (cbs) < 0 ? 0 : (AV *)sv_2mortal ((SV *)newAV ());
594 PKT pkt;
595 tstamp now = NOW ();
518 596
519 for (;;) 597 for (;;)
520 { 598 {
521 int len = recvfrom (icmp6_fd, buf, sizeof (buf), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl); 599 int len = recvfrom (icmp6_fd, &pkt, sizeof (pkt), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl);
522 600
523 if (len <= HDR_SIZE_IP6) 601 if (len != sizeof (PKT))
524 break; 602 break;
525 603
526 IP6HDR *iphdr = (IP6HDR *)buf; 604 if (!res_av
527 605 || pkt.type != ICMP6_ECHO_REPLY
528 int datalen = ntohs (iphdr->payload_len); 606 || pkt.id != (uint16_t) MAGIC
529 607 || pkt.seq != (uint16_t)~MAGIC
530 // packet corrupt? 608 || !isnormal (pkt.stamp))
531 if (HDR_SIZE_IP6 + datalen > len
532 || iphdr->nxt_hdr != IPPROTO_ICMPV6
533 || HDR_SIZE_IP6 + sizeof (PKT) != datalen)
534 continue; 609 continue;
535 610
536 PKT *pkt = (PKT *)(buf + HDR_SIZE_IP6); 611 AV *av = newAV ();
612 av_push (av, newSVpvn ((char *)&sa.sin6_addr, 16));
613 av_push (av, newSVnv (now - pkt.stamp));
614 av_push (av, newSVuv (pkt.payload));
537 615
538 if (pkt->id != (uint16_t)MAGIC 616 av_push (res_av, newRV_noinc ((SV *)av));
539 || pkt->seq != (uint16_t)~MAGIC
540 || pkt->type != ICMP6_ECHO
541 || !isnormal (pkt->stamp))
542 continue;
543
544 //fprintf (stderr, "ip6 echo received\n");
545 // just drain for now
546 //av_push
547 } 617 }
548 618
619 if (res_av)
549 // feed (res_av); 620 feed_reply (res_av);
550} 621}
551 622

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines