… | |
… | |
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 | |
61 | typedef struct { |
70 | typedef 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 | |
|
|
108 | static uint16_t magic; |
99 | |
109 | |
100 | typedef struct { |
110 | typedef 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 | |
108 | pthread_t pthrid; |
118 | static pthread_t pthrid; |
109 | int thr_send[2]; // send to worker |
119 | static int thr_send[2]; // send to worker |
110 | int thr_recv[2]; // receive from worker |
120 | static int thr_recv[2]; // receive from worker |
111 | |
121 | |
112 | int icmp4_fd, icmp6_fd; |
122 | static int icmp4_fd, icmp6_fd; |
|
|
123 | |
|
|
124 | static AV *cbs; |
113 | |
125 | |
114 | static uint16_t |
126 | static uint16_t |
115 | icmp_cksum (void *data, unsigned int len) |
127 | icmp_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 | |
304 | static void |
323 | static void |
|
|
324 | feed_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 | |
|
|
350 | static void |
305 | boot () |
351 | boot () |
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 | |
354 | BOOT: |
407 | BOOT: |
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 | |
367 | PROTOTYPES: DISABLE |
423 | PROTOTYPES: DISABLE |
368 | |
424 | |
369 | SV * |
425 | SV * |
… | |
… | |
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 | |
529 | void |
590 | void |
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 | |