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.1 by root, Sun Apr 27 15:43:51 2008 UTC vs.
Revision 1.8 by root, Sat Jan 29 23:36:49 2011 UTC

1#define _POSIX_C_SOURCE 199309 1#if defined(__linux) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__CYGWIN__)
2#define _GNU_SOURCE 1
3
4#define 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
4# define ENABLE_IPV6 0
5#endif
5 6
6#include "EXTERN.h" 7#include "EXTERN.h"
7#include "perl.h" 8#include "perl.h"
8#include "XSUB.h" 9#include "XSUB.h"
9 10
29#include <arpa/inet.h> 30#include <arpa/inet.h>
30 31
31#ifdef __linux 32#ifdef __linux
32# include <linux/icmp.h> 33# include <linux/icmp.h>
33#endif 34#endif
34#if IPV6 35#if ENABLE_IPV6 && !defined (__CYGWIN__)
35# include <netinet/icmp6.h> 36# include <netinet/icmp6.h>
36#endif 37#endif
37 38
38#define ICMP4_ECHO 8 39#define ICMP4_ECHO 8
39#define ICMP4_ECHO_REPLY 0 40#define ICMP4_ECHO_REPLY 0
44#define MIN_INTERVAL .000001 // minimum packet send interval, in seconds 45#define MIN_INTERVAL .000001 // 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 biffersize 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
50 51
51typedef uint8_t addr_t[16]; 52typedef uint8_t addr_tt[16];
52 53
53typedef double tstamp; 54typedef double tstamp;
54 55
55tstamp 56static tstamp
56NOW () 57NOW (void)
57{ 58{
58 struct timeval tv; 59 struct timeval tv;
59 gettimeofday (&tv, 0); 60 gettimeofday (&tv, 0);
60 return tv.tv_sec + tv.tv_usec * 0.000001; 61 return tv.tv_sec + tv.tv_usec * 0.000001;
61} 62}
62 63
63typedef struct { 64typedef struct
65{
64 int family; 66 int family;
65 addr_t lo, hi; 67 addr_tt lo, hi;
66 double interval; 68 double interval;
67 tstamp next; 69 tstamp next;
68} RANGE; 70} RANGE;
69 71
70typedef struct { 72typedef struct
73{
71 SV *id; 74 SV *id;
72 double interval; 75 double interval;
73 int nranges; 76 int nranges;
74 RANGE *ranges; 77 RANGE *ranges;
75 uint32_t payload; 78 uint32_t payload;
76} REQ; 79} REQ;
77 80
78typedef struct { 81typedef struct
82{
79 uint8_t version_ihl; 83 uint8_t version_ihl;
80 uint8_t tos; 84 uint8_t tos;
81 uint16_t tot_len; 85 uint16_t tot_len;
82 86
83 uint16_t id; 87 uint16_t id;
89 93
90 uint32_t src; 94 uint32_t src;
91 uint32_t dst; 95 uint32_t dst;
92} IP4HDR; 96} IP4HDR;
93 97
94typedef struct { 98typedef struct
99{
95 uint8_t version; 100 uint8_t version;
96 uint8_t x1, x2, x3; 101 uint8_t x1, x2, x3;
97 102
98 uint16_t payload_len; 103 uint16_t payload_len;
99 uint8_t nxt_hdr; 104 uint8_t nxt_hdr;
105 110
106#define MAGIC 0xca4c 111#define MAGIC 0xca4c
107 112
108static uint16_t magic; 113static uint16_t magic;
109 114
110typedef struct { 115typedef struct
116{
111 uint8_t type, code; 117 uint8_t type, code;
112 uint16_t cksum; 118 uint16_t cksum;
113 uint16_t id, seq; 119 uint16_t id, seq;
114 uint32_t payload; 120 uint32_t payload;
115 tstamp stamp; // be careful when accessing this 121 tstamp stamp; // be careful when accessing this
139 145
140 return ~sum; 146 return ~sum;
141} 147}
142 148
143static void 149static void
144inc_addr (addr_t *addr) 150inc_addr (addr_tt *addr)
145{ 151{
146 int len = sizeof (addr_t) - 1; 152 int len = sizeof (addr_tt) - 1;
147 153
148 do 154 do
149 { 155 {
150 if ((*addr)[len] != 0xff) 156 if ((*addr)[len] != 0xff)
151 { 157 {
161static void * 167static void *
162ping_proc (void *unused) 168ping_proc (void *unused)
163{ 169{
164 PKT pkt; 170 PKT pkt;
165 struct sockaddr_in sa4; 171 struct sockaddr_in sa4;
166#if IPV6 172#if ENABLE_IPV6
167 struct sockaddr_in6 sa6; 173 struct sockaddr_in6 sa6;
168#endif 174#endif
169 175
170 memset (&pkt, 0, sizeof (pkt)); 176 memset (&pkt, 0, sizeof (pkt));
171 177
172 memset (&sa4, 0, sizeof (sa4)); 178 memset (&sa4, 0, sizeof (sa4));
173 sa4.sin_family = AF_INET; 179 sa4.sin_family = AF_INET;
174 sa4.sin_port = 0; 180 sa4.sin_port = 0;
175#if IPV6 181#if ENABLE_IPV6
176 memset (&sa6, 0, sizeof (sa6)); 182 memset (&sa6, 0, sizeof (sa6));
177 sa6.sin6_family = AF_INET6; 183 sa6.sin6_family = AF_INET6;
178 sa6.sin6_port = 0; 184 sa6.sin6_port = 0;
179#endif 185#endif
180 186
181 for (;;) 187 for (;;)
182 { 188 {
183 REQ *req; 189 REQ *req;
184 int len = read (thr_send [0], &req, sizeof (req)); 190 int len = read (thr_send [0], &req, sizeof (req));
185 191
192 tstamp now = NOW ();
193 tstamp next = now;
194
186 if (!len) 195 if (!len)
187 pthread_exit (0); 196 pthread_exit (0);
188 else if (len != sizeof (req)) 197 else if (len != sizeof (req))
189 { 198 {
190 perror ("AnyEvent::FastPing: short reead or read error"); 199 perror ("AnyEvent::FastPing: short read or read error");
191 pthread_exit ((void *)-1); 200 pthread_exit ((void *)-1);
192 } 201 }
193 202
194 //TODO: bind to source address 203 //TODO: bind to source address
195 204
196 pkt.code = 0; 205 pkt.code = 0;
197 pkt.id = (uint16_t)magic; 206 pkt.id = (uint16_t)magic;
198 pkt.seq = (uint16_t)~magic; 207 pkt.seq = (uint16_t)~magic;
199 pkt.payload = req->payload; 208 pkt.payload = req->payload;
200
201 tstamp now = NOW ();
202 tstamp next = now;
203 209
204 { 210 {
205 int r; 211 int r;
206 for (r = req->nranges; r--; ) 212 for (r = req->nranges; r--; )
207 inc_addr (&req->ranges [r].hi); 213 inc_addr (&req->ranges [r].hi);
208 } 214 }
209 215
210 while (req->nranges) 216 while (req->nranges)
211 { 217 {
212 RANGE *range = req->ranges; 218 RANGE *range = req->ranges;
219 int n, k;
213 220
214 if (!memcmp (&range->lo, &range->hi, sizeof (addr_t))) 221 if (!memcmp (&range->lo, &range->hi, sizeof (addr_tt)))
215 req->ranges [0] = req->ranges [--req->nranges]; 222 req->ranges [0] = req->ranges [--req->nranges];
216 else 223 else
217 { 224 {
218 // ranges [0] is always the next range to ping 225 // ranges [0] is always the next range to ping
219 tstamp wait = range->next - now; 226 tstamp wait = range->next - now;
247 { 254 {
248 pkt.type = ICMP4_ECHO; 255 pkt.type = ICMP4_ECHO;
249 pkt.cksum = icmp_cksum (&pkt, sizeof (pkt)); 256 pkt.cksum = icmp_cksum (&pkt, sizeof (pkt));
250 257
251 memcpy (&sa4.sin_addr, 258 memcpy (&sa4.sin_addr,
252 sizeof (addr_t) - sizeof (sa4.sin_addr) + (char *)&range->lo, 259 sizeof (addr_tt) - sizeof (sa4.sin_addr) + (char *)&range->lo,
253 sizeof (sa4.sin_addr)); 260 sizeof (sa4.sin_addr));
254 261
255 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)
256 errno = 0; 263 errno = 0;
257 } 264 }
258 else 265 else
259 { 266 {
260#if IPV6 267#if ENABLE_IPV6
261 pkt.type = ICMP6_ECHO; 268 pkt.type = ICMP6_ECHO;
262 269
263 memcpy (&sa6.sin6_addr, 270 memcpy (&sa6.sin6_addr,
264 sizeof (addr_t) - sizeof (sa6.sin6_addr) + (char *)&range->lo, 271 sizeof (addr_tt) - sizeof (sa6.sin6_addr) + (char *)&range->lo,
265 sizeof (sa6.sin6_addr)); 272 sizeof (sa6.sin6_addr));
266 273
267 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)
268 errno = 0; 275 errno = 0;
269#endif 276#endif
288 295
289 next += req->interval; 296 next += req->interval;
290 } 297 }
291 298
292 // make a downheap operation 299 // make a downheap operation
293 int k = 0;
294 int n = 0;
295 for (;;) 300 for (n = k = 0; ; )
296 { 301 {
302 int j = k * 2 + 1;
303
297 ++n; 304 ++n;
298 int j = k * 2 + 1;
299 305
300 if (j >= req->nranges) 306 if (j >= req->nranges)
301 break; 307 break;
302 else if (j < req->nranges - 1) 308 else if (j < req->nranges - 1)
303 if (req->ranges [j].next > req->ranges [j + 1].next) 309 if (req->ranges [j].next > req->ranges [j + 1].next)
304 ++j; 310 ++j;
305 311
306 if (req->ranges [j].next >= req->ranges [k].next) 312 if (req->ranges [j].next >= req->ranges [k].next)
307 break; 313 break;
308 314
315 {
309 RANGE temp = req->ranges [k]; 316 RANGE temp = req->ranges [k];
310 req->ranges [k] = req->ranges [j]; 317 req->ranges [k] = req->ranges [j];
311 req->ranges [j] = temp; 318 req->ranges [j] = temp;
319 }
312 320
313 k = j; 321 k = j;
314 } 322 }
315 } 323 }
316 324
321} 329}
322 330
323static void 331static void
324feed_reply (AV *res_av) 332feed_reply (AV *res_av)
325{ 333{
326 if (av_len (res_av) < 0)
327 return;
328
329 dSP; 334 dSP;
330 SV *res = sv_2mortal (newRV_inc ((SV *)res_av)); 335 SV *res = sv_2mortal (newRV_inc ((SV *)res_av));
331 int i; 336 int i;
337
338 if (av_len (res_av) < 0)
339 return;
332 340
333 ENTER; 341 ENTER;
334 SAVETMPS; 342 SAVETMPS;
335 343
336 for (i = av_len (cbs) + 1; i--; ) 344 for (i = av_len (cbs) + 1; i--; )
358 366
359 if (pipe (thr_recv) < 0) 367 if (pipe (thr_recv) < 0)
360 croak ("AnyEvent::FastPing: unable to create receive pipe"); 368 croak ("AnyEvent::FastPing: unable to create receive pipe");
361 369
362 icmp4_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); 370 icmp4_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
371 fcntl (icmp4_fd, F_SETFL, O_NONBLOCK);
363#ifdef ICMP_FILTER 372#ifdef ICMP_FILTER
364 { 373 {
365 struct icmp_filter oval; 374 struct icmp_filter oval;
366 oval.data = 0xffffffff & ~(1 << ICMP4_ECHO_REPLY); 375 oval.data = 0xffffffff & ~(1 << ICMP4_ECHO_REPLY);
367 setsockopt (icmp4_fd, SOL_RAW, ICMP_FILTER, &oval, sizeof oval); 376 setsockopt (icmp4_fd, SOL_RAW, ICMP_FILTER, &oval, sizeof oval);
368 } 377 }
369#endif 378#endif
370 379
371#if IPV6 380#if ENABLE_IPV6
372 icmp6_fd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 381 icmp6_fd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
382 fcntl (icmp6_fd, F_SETFL, O_NONBLOCK);
373# ifdef ICMP6_FILTER 383# ifdef ICMP6_FILTER
374 { 384 {
375 struct icmp6_filter oval; 385 struct icmp6_filter oval;
376 ICMP6_FILTER_SETBLOCKALL (&oval); 386 ICMP6_FILTER_SETBLOCKALL (&oval);
377 ICMP6_FILTER_SETPASS (ICMP6_ECHO_REPLY, &oval); 387 ICMP6_FILTER_SETPASS (ICMP6_ECHO_REPLY, &oval);
424 434
425SV * 435SV *
426_req_icmp_ping (SV *ranges, NV interval, U32 payload, SV *id) 436_req_icmp_ping (SV *ranges, NV interval, U32 payload, SV *id)
427 CODE: 437 CODE:
428{ 438{
439 AV *rav;
440 int nranges, i;
441 REQ *req;
442
429 if (!SvROK (ranges) || SvTYPE (SvRV (ranges)) != SVt_PVAV) 443 if (!SvROK (ranges) || SvTYPE (SvRV (ranges)) != SVt_PVAV)
430 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");
431 445
432 AV *rav = (AV *)SvRV (ranges); 446 rav = (AV *)SvRV (ranges);
433 int nranges = av_len (rav) + 1; 447 nranges = av_len (rav) + 1;
434 448
435 REQ *req = malloc (sizeof (REQ)); 449 req = malloc (sizeof (REQ));
436 int i;
437 450
438 if (interval < MIN_INTERVAL) 451 if (interval < MIN_INTERVAL)
439 interval = MIN_INTERVAL; 452 interval = MIN_INTERVAL;
440 453
441 req->id = newSVsv (id); 454 req->id = newSVsv (id);
445 req->ranges = (RANGE *)malloc (nranges * sizeof (RANGE)); 458 req->ranges = (RANGE *)malloc (nranges * sizeof (RANGE));
446 459
447 while (nranges--) 460 while (nranges--)
448 { 461 {
449 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;
450 466
451 if (!SvROK (sv) || SvTYPE (SvRV (sv)) != SVt_PVAV) 467 if (!SvROK (sv) || SvTYPE (SvRV (sv)) != SVt_PVAV)
452 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");
453 469
454 AV *av = (AV *)SvRV (sv); 470 av = (AV *)SvRV (sv);
455 RANGE *r = req->ranges + nranges; 471 r = req->ranges + nranges;
456 472
457 SV *lo = *av_fetch (av, 0, 1); 473 lo = *av_fetch (av, 0, 1);
458 SV *hi = *av_fetch (av, 1, 1); 474 hi = *av_fetch (av, 1, 1);
459 475
460 sv_utf8_downgrade (lo, 0); 476 sv_utf8_downgrade (lo, 0);
461 sv_utf8_downgrade (hi, 0); 477 sv_utf8_downgrade (hi, 0);
462 478
463 memset (&r->lo, 0, sizeof (addr_t)); 479 memset (&r->lo, 0, sizeof (addr_tt));
464 memset (&r->hi, 0, sizeof (addr_t)); 480 memset (&r->hi, 0, sizeof (addr_tt));
465 481
466 if (SvPOKp (lo) && SvPOKp (hi)) 482 if (SvPOKp (lo) && SvPOKp (hi))
467 { 483 {
468 if (SvCUR (lo) != SvCUR (hi)) 484 if (SvCUR (lo) != SvCUR (hi))
469 croak ("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)");
470 486
471 if (SvCUR (lo) == 4) 487 if (SvCUR (lo) == 4)
472 { 488 {
473 r->family = AF_INET; 489 r->family = AF_INET;
474 memcpy (sizeof (addr_t) - 4 + (char *)&r->lo, SvPVX (lo), 4); 490 memcpy (sizeof (addr_tt) - 4 + (char *)&r->lo, SvPVX (lo), 4);
475 memcpy (sizeof (addr_t) - 4 + (char *)&r->hi, SvPVX (hi), 4); 491 memcpy (sizeof (addr_tt) - 4 + (char *)&r->hi, SvPVX (hi), 4);
476 } 492 }
477 else if (SvCUR (lo) == 16) 493 else if (SvCUR (lo) == 16)
478 { 494 {
479#if IPV6 495#if ENABLE_IPV6
480 r->family = AF_INET6; 496 r->family = AF_INET6;
481 memcpy (&r->lo, SvPVX (lo), sizeof (addr_t)); 497 memcpy (&r->lo, SvPVX (lo), sizeof (addr_tt));
482 memcpy (&r->hi, SvPVX (hi), sizeof (addr_t)); 498 memcpy (&r->hi, SvPVX (hi), sizeof (addr_tt));
483#else 499#else
484 croak ("IPv6 not supported in this configuration"); 500 croak ("IPv6 not supported in this configuration");
485#endif 501#endif
486 } 502 }
487 else 503 else
488 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");
489 } 505 }
490 else if (SvIOK (lo) && SvIOK (hi)) 506 else if (SvIOK (lo) && SvIOK (hi))
491 { 507 {
508 uint32_t addr;
509
492 r->family = AF_INET; 510 r->family = AF_INET;
493 511
494 uint32_t addr;
495 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);
496 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);
497 } 514 }
498 else 515 else
499 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");
500 517
501 if (r->family == AF_INET) 518 if (r->family == AF_INET)
548 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 ());
549 tstamp now = NOW (); 566 tstamp now = NOW ();
550 567
551 for (;;) 568 for (;;)
552 { 569 {
570 IP4HDR *iphdr = (IP4HDR *)buf;
553 int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl); 571 int len = recvfrom (icmp4_fd, buf, sizeof (buf), MSG_TRUNC, (struct sockaddr *)&sa, &sl);
572 int hdrlen, totlen;
573 PKT *pkt;
554 574
555 if (len <= HDR_SIZE_IP4) 575 if (len <= HDR_SIZE_IP4)
556 break; 576 break;
557 577
558 IP4HDR *iphdr = (IP4HDR *)buf;
559
560 int hdrlen = (iphdr->version_ihl & 15) * 4; 578 hdrlen = (iphdr->version_ihl & 15) * 4;
561 int totlen = ntohs (iphdr->tot_len); 579 totlen = ntohs (iphdr->tot_len);
562 580
563 // packet corrupt? 581 // packet corrupt?
564 if (!res_av 582 if (!res_av
565 || totlen > len 583 || totlen > len
566 || iphdr->protocol != IPPROTO_ICMP 584 || iphdr->protocol != IPPROTO_ICMP
567 || hdrlen < HDR_SIZE_IP4 || hdrlen + sizeof (PKT) != totlen) 585 || hdrlen < HDR_SIZE_IP4 || hdrlen + sizeof (PKT) != totlen)
568 continue; 586 continue;
569 587
570 PKT *pkt = (PKT *)(buf + hdrlen); 588 pkt = (PKT *)(buf + hdrlen);
571 589
572 if (pkt->type != ICMP4_ECHO_REPLY 590 if (pkt->type != ICMP4_ECHO_REPLY
573 || pkt->id != (uint16_t) magic 591 || pkt->id != (uint16_t) magic
574 || pkt->seq != (uint16_t)~magic 592 || pkt->seq != (uint16_t)~magic
575 || !isnormal (pkt->stamp)) 593 || !isnormal (pkt->stamp))
576 continue; 594 continue;
577 595
596 {
578 AV *av = newAV (); 597 AV *av = newAV ();
579 av_push (av, newSVpvn ((char *)&sa.sin_addr, 4)); 598 av_push (av, newSVpvn ((char *)&sa.sin_addr, 4));
580 av_push (av, newSVnv (now - pkt->stamp)); 599 av_push (av, newSVnv (now - pkt->stamp));
581 av_push (av, newSVuv (pkt->payload)); 600 av_push (av, newSVuv (pkt->payload));
582 601
583 av_push (res_av, newRV_noinc ((SV *)av)); 602 av_push (res_av, newRV_noinc ((SV *)av));
603 }
584 } 604 }
585 605
586 if (res_av) 606 if (res_av)
587 feed_reply (res_av); 607 feed_reply (res_av);
588} 608}
597 PKT pkt; 617 PKT pkt;
598 tstamp now = NOW (); 618 tstamp now = NOW ();
599 619
600 for (;;) 620 for (;;)
601 { 621 {
602 int len = recvfrom (icmp6_fd, &pkt, sizeof (pkt), MSG_DONTWAIT | MSG_TRUNC, &sa, &sl); 622 int len = recvfrom (icmp6_fd, &pkt, sizeof (pkt), MSG_TRUNC, (struct sockaddr *)&sa, &sl);
603 623
604 if (len != sizeof (PKT)) 624 if (len != sizeof (PKT))
605 break; 625 break;
606 626
607 if (!res_av 627 if (!res_av
609 || pkt.id != (uint16_t) magic 629 || pkt.id != (uint16_t) magic
610 || pkt.seq != (uint16_t)~magic 630 || pkt.seq != (uint16_t)~magic
611 || !isnormal (pkt.stamp)) 631 || !isnormal (pkt.stamp))
612 continue; 632 continue;
613 633
634 {
614 AV *av = newAV (); 635 AV *av = newAV ();
615 av_push (av, newSVpvn ((char *)&sa.sin6_addr, 16)); 636 av_push (av, newSVpvn ((char *)&sa.sin6_addr, 16));
616 av_push (av, newSVnv (now - pkt.stamp)); 637 av_push (av, newSVnv (now - pkt.stamp));
617 av_push (av, newSVuv (pkt.payload)); 638 av_push (av, newSVuv (pkt.payload));
618 639
619 av_push (res_av, newRV_noinc ((SV *)av)); 640 av_push (res_av, newRV_noinc ((SV *)av));
641 }
620 } 642 }
621 643
622 if (res_av) 644 if (res_av)
623 feed_reply (res_av); 645 feed_reply (res_av);
624} 646}

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines