File Coverage

lib/Socket/Packet.xs
Criterion Covered Total %
statement 81 189 42.8
branch 23 90 25.5
condition n/a
subroutine n/a
pod n/a
total 104 279 37.2


line stmt bran cond sub pod time code
1             /* You may distribute under the terms of either the GNU General Public License
2             * or the Artistic License (the same terms as Perl itself)
3             *
4             * (C) Paul Evans, 2009,2010 -- leonerd@leonerd.org.uk
5             */
6              
7             #include "EXTERN.h"
8             #include "perl.h"
9             #include "XSUB.h"
10              
11             #include
12             #include
13             #include
14             #include
15             #include
16             #include
17              
18             /* Borrowed from IO/Sockatmark.xs */
19              
20             #ifdef PerlIO
21             typedef PerlIO * InputStream;
22             #else
23             #define PERLIO_IS_STDIO 1
24             typedef FILE * InputStream;
25             #define PerlIO_fileno(f) fileno(f)
26             #endif
27              
28             /* Lower and upper bounds of a valid struct sockaddr_ll */
29             static int sll_max;
30             static int sll_min;
31             /* Maximum number of address bytes in a struct sockaddr_ll */
32             static int sll_maxaddr;
33              
34             #ifndef PUSHmortal
35             # define PUSHmortal PUSHs(sv_newmortal())
36             #endif
37              
38             #ifndef mPUSHi
39             # define mPUSHi(iv) sv_setiv(PUSHmortal, iv)
40             #endif
41              
42             #ifndef mPUSHn
43             # define mPUSHn(nv) sv_setnv(PUSHmortal, nv)
44             #endif
45              
46             #ifndef mPUSHp
47             # define mPUSHp(p,l) sv_setpvn(PUSHmortal, p, l)
48             #endif
49              
50             #define HVSTOREi(hv,name,iv) sv_setiv(*hv_fetch(hv, ""name"", sizeof(""name"")-1, 1), iv)
51             #define HVSTOREp(hv,name,pv,len) sv_setpvn(*hv_fetch(hv, ""name"", sizeof(""name"")-1, 1), pv, len)
52              
53             #if defined(HAVE_TPACKET) || defined(HAVE_TPACKET2)
54             # define HAVE_RX_RING
55              
56             struct packet_rxring_state
57             {
58             char *buffer;
59             unsigned int frame_size;
60             unsigned int frame_nr;
61             unsigned int frame_idx;
62             };
63              
64 0           static int free_rxring_state(pTHX_ SV *sv, MAGIC *mg)
65             {
66 0           Safefree(mg->mg_ptr);
67 0           return 0;
68             }
69              
70             static const MGVTBL vtbl = {
71             NULL, /* get */
72             NULL, /* set */
73             NULL, /* len */
74             NULL, /* clear */
75             &free_rxring_state, /* free */
76             };
77              
78 0           static void S_apply_rxring_state(pTHX_ SV *sv, struct packet_rxring_state *state)
79             {
80 0           sv_magicext(sv, NULL, PERL_MAGIC_ext, &vtbl, (char *)state, 0);
81 0           }
82              
83 0           static struct packet_rxring_state *S_get_rxring_state(pTHX_ SV *sv)
84             {
85 0           MAGIC *mg = mg_findext(sv, PERL_MAGIC_ext, &vtbl);
86 0 0         if(mg)
87 0           return (struct packet_rxring_state *)mg->mg_ptr;
88              
89 0           croak("Cannot find rxring state - call setup_rx_ring() first");
90             }
91              
92             #define apply_rxring_state(sv, state) S_apply_rxring_state(aTHX_ sv, state)
93             #define get_rxring_state(sv) S_get_rxring_state(aTHX_ sv)
94              
95             static void *frame_ptr(struct packet_rxring_state *state)
96             {
97 0           return state->buffer + (state->frame_size * state->frame_idx);
98             }
99             #endif
100              
101 8           static void setup_constants(void)
102             {
103 8           sll_max = sizeof(struct sockaddr_ll);
104 8           sll_maxaddr = sizeof(((struct sockaddr_ll*)NULL)->sll_addr);
105 8           sll_min = sll_max - sll_maxaddr;
106              
107             HV *stash;
108             AV *export;
109              
110 8           stash = gv_stashpvn("Socket::Packet", 14, TRUE);
111 8           export = get_av("Socket::Packet::EXPORT", TRUE);
112              
113             #define DO_CONSTANT(c) \
114             newCONSTSUB(stash, #c, newSViv(c)); \
115             av_push(export, newSVpv(#c, 0));
116              
117              
118 8           DO_CONSTANT(PF_PACKET)
119 8           DO_CONSTANT(AF_PACKET)
120              
121 8           DO_CONSTANT(PACKET_HOST)
122 8           DO_CONSTANT(PACKET_BROADCAST)
123 8           DO_CONSTANT(PACKET_MULTICAST)
124 8           DO_CONSTANT(PACKET_OTHERHOST)
125 8           DO_CONSTANT(PACKET_OUTGOING)
126              
127 8           DO_CONSTANT(ETH_P_ALL)
128              
129 8           DO_CONSTANT(SOL_PACKET)
130              
131 8           DO_CONSTANT(PACKET_ADD_MEMBERSHIP)
132 8           DO_CONSTANT(PACKET_DROP_MEMBERSHIP)
133 8           DO_CONSTANT(PACKET_STATISTICS)
134             #ifdef HAVE_ORIGDEV
135 8           DO_CONSTANT(PACKET_ORIGDEV)
136             #endif
137              
138 8           DO_CONSTANT(PACKET_MR_MULTICAST)
139 8           DO_CONSTANT(PACKET_MR_PROMISC)
140 8           DO_CONSTANT(PACKET_MR_ALLMULTI)
141              
142             #ifdef HAVE_RX_RING
143 8           DO_CONSTANT(TP_STATUS_KERNEL)
144 8           DO_CONSTANT(TP_STATUS_USER)
145 8           DO_CONSTANT(TP_STATUS_COPY)
146 8           DO_CONSTANT(TP_STATUS_LOSING)
147 8           DO_CONSTANT(TP_STATUS_CSUMNOTREADY)
148             #endif
149 8           }
150              
151             MODULE = Socket::Packet PACKAGE = Socket::Packet
152              
153             BOOT:
154 8           setup_constants();
155              
156             void
157             pack_sockaddr_ll(protocol, ifindex, hatype, pkttype, addr)
158             unsigned short protocol
159             int ifindex
160             unsigned short hatype
161             unsigned char pkttype
162             SV *addr
163              
164             PREINIT:
165             struct sockaddr_ll sll;
166             char *addrbytes;
167             STRLEN addrlen;
168              
169             PPCODE:
170 4 50         if (DO_UTF8(addr) && !sv_utf8_downgrade(addr, 1))
    0          
    0          
171 0           croak("Wide character in Socket::Packet::pack_sockaddr_ll");
172              
173 4           addrbytes = SvPVbyte(addr, addrlen);
174              
175 4 50         if(addrlen > sll_maxaddr)
176 0           croak("addr too long; should be no more than %d bytes, found %d", sll_maxaddr, addrlen);
177              
178 4 50         sll.sll_family = AF_PACKET;
179 4           sll.sll_protocol = htons(protocol);
180 4           sll.sll_ifindex = ifindex;
181 4           sll.sll_hatype = hatype;
182 4           sll.sll_pkttype = pkttype;
183              
184 4 50         sll.sll_halen = addrlen;
185             Zero(&sll.sll_addr, sll_maxaddr, char);
186             Copy(addrbytes, &sll.sll_addr, addrlen, char);
187              
188 4 50         EXTEND(SP, 1);
189 4           mPUSHp((char *)&sll, sizeof sll);
190              
191             void
192             unpack_sockaddr_ll(sa)
193             SV * sa
194              
195             PREINIT:
196             STRLEN sa_len;
197             char *sa_bytes;
198             struct sockaddr_ll sll;
199              
200             PPCODE:
201             /* variable size of structure, because of variable length of addr bytes */
202 10           sa_bytes = SvPVbyte(sa, sa_len);
203 10 50         if(sa_len < sll_min)
204 0           croak("Socket address too small; found %d bytes, expected at least %d", sa_len, sll_min);
205 10 50         if(sa_len > sll_max)
206 0           croak("Socket address too big; found %d bytes, expected at most %d", sa_len, sll_max);
207              
208             Copy(sa_bytes, &sll, sizeof sll, char);
209              
210 10 50         if(sa_len < sll_min + sll.sll_halen)
211 0           croak("Socket address too small; it did not provide enough bytes for sll_halen of %d", sll.sll_halen);
212              
213 10 50         if(sll.sll_family != AF_PACKET)
214 0           croak("Bad address family for unpack_sockaddr_ll: got %d, expected %d", sll.sll_family, AF_PACKET);
215              
216 10 50         EXTEND(SP, 5);
217 10           mPUSHi(ntohs(sll.sll_protocol));
218 10           mPUSHi(sll.sll_ifindex);
219 10           mPUSHi(sll.sll_hatype);
220 10           mPUSHi(sll.sll_pkttype);
221 10           mPUSHp((char *)sll.sll_addr, sll.sll_halen);
222              
223             void
224             pack_packet_mreq(ifindex, type, addr)
225             int ifindex
226             unsigned short type
227             SV * addr
228              
229             PREINIT:
230             struct packet_mreq mreq;
231             char *addr_bytes;
232             STRLEN addr_len;
233              
234             PPCODE:
235 0 0         if (DO_UTF8(addr) && !sv_utf8_downgrade(addr, 1))
    0          
    0          
236 0           croak("Wide character in Socket::Packet::pack_sockaddr_ll");
237              
238 0           addr_bytes = SvPVbyte(addr, addr_len);
239              
240 0 0         if(addr_len > sizeof(mreq.mr_address))
241 0           croak("addr too long; should be no more than %d bytes, found %d", sizeof(mreq.mr_address), addr_len);
242              
243 0           mreq.mr_ifindex = ifindex;
244 0           mreq.mr_type = type;
245              
246 0 0         mreq.mr_alen = addr_len;
247             Zero(&mreq.mr_address, sizeof(mreq.mr_address), char);
248             Copy(addr_bytes, &mreq.mr_address, addr_len, char);
249              
250 0 0         EXTEND(SP, 1);
251 0           mPUSHp((char *)&mreq, sizeof mreq);
252              
253             void
254             unpack_packet_mreq(data)
255             SV * data
256              
257             PREINIT:
258             STRLEN data_len;
259             char *data_bytes;
260             struct packet_mreq mreq;
261              
262             PPCODE:
263 0           data_bytes = SvPVbyte(data, data_len);
264 0 0         if(data_len != sizeof(mreq))
265 0           croak("packet_mreq buffer incorrect size; found %d bytes, expected %d", data_len, sizeof(mreq));
266              
267             Copy(data_bytes, &mreq, data_len, char);
268              
269 0 0         if(mreq.mr_alen > sizeof(mreq.mr_address))
270 0           croak("packet_mreq claims to have a larger address than it has space for");
271              
272 0 0         EXTEND(SP, 3);
273 0           mPUSHi(mreq.mr_ifindex);
274 0           mPUSHi(mreq.mr_type);
275 0           mPUSHp(mreq.mr_address, mreq.mr_alen);
276              
277             void
278             unpack_tpacket_stats(stats)
279             SV * stats
280              
281             PREINIT:
282             STRLEN stats_len;
283             char *stats_bytes;
284             struct tpacket_stats statsbuf;
285              
286             PPCODE:
287 1           stats_bytes = SvPVbyte(stats, stats_len);
288 1 50         if(stats_len != sizeof(statsbuf))
289 0           croak("tpacket_stats buffer incorrect size; found %d bytes, expected %d", stats_len, sizeof(statsbuf));
290              
291             Copy(stats_bytes, &statsbuf, stats_len, char);
292              
293 1 50         EXTEND(SP, 5);
294 1           mPUSHi(statsbuf.tp_packets);
295 1           mPUSHi(statsbuf.tp_drops);
296              
297             void
298             siocgstamp(sock)
299             InputStream sock
300             PROTOTYPE: $
301              
302             PREINIT:
303             int fd;
304             int result;
305             struct timeval tv;
306              
307             PPCODE:
308 3           fd = PerlIO_fileno(sock);
309 3 50         if(ioctl(fd, SIOCGSTAMP, &tv) == -1) {
310 3 50         if(GIMME_V == G_ARRAY)
311             return;
312             else
313 3           XSRETURN_UNDEF;
314             }
315              
316 0 0         if(GIMME_V == G_ARRAY) {
317 0 0         EXTEND(SP, 2);
318 0           mPUSHi(tv.tv_sec);
319 0           mPUSHi(tv.tv_usec);
320             }
321             else {
322 0           mPUSHn((double)tv.tv_sec + (tv.tv_usec / 1000000.0));
323             }
324              
325             void
326             siocgstampns(sock)
327             InputStream sock
328             PROTOTYPE: $
329              
330             PREINIT:
331             int fd;
332             int result;
333             struct timespec ts;
334              
335             PPCODE:
336             #ifdef SIOCGSTAMPNS
337 0           fd = PerlIO_fileno(sock);
338 0 0         if(ioctl(fd, SIOCGSTAMPNS, &ts) == -1) {
339 0 0         if(GIMME_V == G_ARRAY)
340             return;
341             else
342 0           XSRETURN_UNDEF;
343             }
344              
345 0 0         if(GIMME_V == G_ARRAY) {
346 0 0         EXTEND(SP, 2);
347 0           mPUSHi(ts.tv_sec);
348 0           mPUSHi(ts.tv_nsec);
349             }
350             else {
351 0           mPUSHn((double)ts.tv_sec + (ts.tv_nsec / 1000000000.0));
352             }
353             #else
354             croak("SIOCGSTAMPNS not implemented");
355             #endif
356              
357             void
358             siocgifindex(sock, ifname)
359             InputStream sock
360             char *ifname
361             PROTOTYPE: $$
362              
363             PREINIT:
364             int fd;
365             struct ifreq req;
366              
367             PPCODE:
368             #ifdef SIOCGIFINDEX
369 3           fd = PerlIO_fileno(sock);
370             strncpy(req.ifr_name, ifname, IFNAMSIZ);
371 3 50         if(ioctl(fd, SIOCGIFINDEX, &req) == -1)
372 0           XSRETURN_UNDEF;
373 3           mPUSHi(req.ifr_ifindex);
374             #else
375             croak("SIOCGIFINDEX not implemented");
376             #endif
377              
378             void
379             siocgifname(sock, ifindex)
380             InputStream sock
381             int ifindex
382             PROTOTYPE: $$
383              
384             PREINIT:
385             int fd;
386             struct ifreq req;
387              
388             PPCODE:
389             #ifdef SIOCGIFNAME
390 7           fd = PerlIO_fileno(sock);
391 7           req.ifr_ifindex = ifindex;
392 7 100         if(ioctl(fd, SIOCGIFNAME, &req) == -1)
393 2           XSRETURN_UNDEF;
394 5           PUSHs(sv_2mortal(newSVpv(req.ifr_name, 0)));
395             #else
396             croak("SIOCGIFNAME not implemented");
397             #endif
398              
399             void
400             recv_len(sock, buffer, maxlen, flags)
401             InputStream sock
402             SV *buffer
403             int maxlen
404             int flags
405              
406             PREINIT:
407             int fd;
408             char *bufferp;
409             struct sockaddr_storage addr;
410             socklen_t addrlen;
411             int len;
412              
413             PPCODE:
414 1           fd = PerlIO_fileno(sock);
415              
416 1 50         if(!SvOK(buffer))
417 1           sv_setpvn(buffer, "", 0);
418              
419 1 50         bufferp = SvGROW(buffer, (STRLEN)(maxlen+1));
    50          
420              
421 1           addrlen = sizeof(addr);
422              
423 1 50         len = recvfrom(fd, bufferp, maxlen, flags, (struct sockaddr *)&addr, &addrlen);
424              
425 1 50         if(len < 0)
426 0           XSRETURN(0);
427              
428 1 50         if(len > maxlen)
429 1           SvCUR_set(buffer, maxlen);
430             else
431 0           SvCUR_set(buffer, len);
432              
433 1           *SvEND(buffer) = '\0';
434 1           SvPOK_only(buffer);
435              
436 1           mPUSHp((char *)&addr, addrlen);
437 1           mPUSHi(len);
438              
439             void
440             setup_rx_ring(sock, frame_size, frame_nr, block_size)
441             InputStream sock
442             unsigned int frame_size
443             unsigned int frame_nr
444             unsigned int block_size
445              
446             PREINIT:
447             int fd;
448             int version;
449             struct tpacket_req req;
450             size_t size;
451             char *addr;
452              
453             PPCODE:
454             #ifdef HAVE_RX_RING
455 0           fd = PerlIO_fileno(sock);
456             #ifdef HAVE_TPACKET2
457 0           version = TPACKET_V2;
458 0 0         if(setsockopt(fd, SOL_PACKET, PACKET_VERSION, &version, sizeof version) != 0)
459 0           XSRETURN_UNDEF;
460             #endif
461              
462             {
463             struct tpacket_req req;
464 0           req.tp_frame_size = frame_size;
465 0           req.tp_frame_nr = frame_nr;
466 0           req.tp_block_size = block_size;
467 0           req.tp_block_nr = (frame_size * frame_nr) / block_size;
468 0 0         if(setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &req, sizeof req) != 0)
469 0           XSRETURN_UNDEF;
470              
471 0           size = req.tp_block_size * req.tp_block_nr;
472             }
473              
474 0           addr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
475 0 0         if(addr == MAP_FAILED)
476 0           XSRETURN_UNDEF;
477              
478             {
479             struct packet_rxring_state *state;
480 0           Newx(state, 1, struct packet_rxring_state);
481              
482 0           state->buffer = addr;
483 0           state->frame_size = frame_size;
484 0           state->frame_nr = frame_nr;
485 0           state->frame_idx = 0;
486              
487 0           apply_rxring_state((SV *)sv_2io(ST(0)), state);
488             }
489              
490 0           ST(0) = sv_2mortal(newSViv(size));
491 0           XSRETURN(1);
492             #else
493             croak("setup_rx_ring() not supported on this platform");
494             #endif
495              
496             void
497             get_ring_frame_status(sock)
498             InputStream sock
499             PPCODE:
500             #ifdef HAVE_RX_RING
501             {
502 0           struct packet_rxring_state *state = get_rxring_state((SV*)sv_2io(ST(0)));
503             char *addr = frame_ptr(state);
504             #if defined(HAVE_TPACKET2)
505             struct tpacket2_hdr *hdr = (struct tpacket2_hdr *)addr;
506             #elif defined(HAVE_TPACKET)
507             struct tpacket_hdr *hdr = (struct tpacket_hdr *)addr;
508             #endif
509 0           ST(0) = sv_2mortal(newSViv(hdr->tp_status));
510             }
511              
512 0           XSRETURN(1);
513             #else
514             croak("get_ring_frame_status() not supported on this platform");
515             #endif
516              
517             void
518             get_ring_frame(sock, buffer, info)
519             InputStream sock
520             SV *buffer
521             HV *info
522              
523             PREINIT:
524              
525             PPCODE:
526             #ifdef HAVE_RX_RING
527             {
528 0           struct packet_rxring_state *state = get_rxring_state((SV*)sv_2io(ST(0)));
529             char *addr = frame_ptr(state);
530             unsigned int len;
531             unsigned int snaplen;
532             int mac;
533             struct sockaddr_ll *sll;
534             #if defined(HAVE_TPACKET2)
535             struct tpacket2_hdr *hdr = (struct tpacket2_hdr *)addr;
536 0 0         if((hdr->tp_status & 1) != 1)
537 0           XSRETURN(0);
538              
539 0           len = hdr->tp_len;
540 0           snaplen = hdr->tp_snaplen;
541 0           mac = hdr->tp_mac;
542              
543 0           HVSTOREi(info, "tp_status", hdr->tp_status);
544 0           HVSTOREi(info, "tp_len", hdr->tp_len);
545 0           HVSTOREi(info, "tp_snaplen", hdr->tp_snaplen);
546 0           HVSTOREi(info, "tp_sec", hdr->tp_sec);
547 0           HVSTOREi(info, "tp_nsec", hdr->tp_nsec);
548 0           HVSTOREi(info, "tp_vlan_tci", hdr->tp_vlan_tci);
549              
550             sll = (struct sockaddr_ll *)(addr + TPACKET_ALIGN(sizeof(struct tpacket2_hdr)));
551             #elif defined(HAVE_TPACKET)
552             struct tpacket_hdr *hdr = (struct tpacket_hdr *)addr;
553             if((hdr->tp_status & 1) != 1)
554             XSRETURN(0);
555              
556             len = hdr->tp_len;
557             snaplen = hdr->tp_snaplen;
558             mac = hdr->tp_mac;
559              
560             HVSTOREi(info, "tp_status", hdr->tp_status);
561             HVSTOREi(info, "tp_len", hdr->tp_len);
562             HVSTOREi(info, "tp_snaplen", hdr->tp_snaplen);
563             HVSTOREi(info, "tp_sec", hdr->tp_sec);
564             HVSTOREi(info, "tp_nsec", hdr->tp_usec * 1000);
565              
566             sll = (struct sockaddr_ll *)(addr + TPACKET_ALIGN(sizeof(struct tpacket_hdr)));
567             #endif
568 0           HVSTOREi(info, "sll_protocol", ntohs(sll->sll_protocol));
569 0           HVSTOREi(info, "sll_ifindex", sll->sll_ifindex);
570 0           HVSTOREi(info, "sll_hatype", sll->sll_hatype);
571 0           HVSTOREi(info, "sll_pkttype", sll->sll_pkttype);
572 0           HVSTOREp(info, "sll_addr", sll->sll_addr, sll->sll_halen);
573              
574             /* Alias, don't copy data - we like zero-copy */
575 0 0         SvUPGRADE(buffer, SVt_PV);
576 0           SvPVX(buffer) = addr + mac;
577 0           SvCUR_set(buffer, snaplen);
578 0           SvLEN_set(buffer, 0);
579 0           SvPOK_only(buffer);
580              
581 0           sv_setiv(ST(0), len);
582 0           XSRETURN(1);
583             }
584             #else
585             croak("get_ring_frame() not supported on this platform");
586             #endif
587              
588             void
589             done_ring_frame(sock)
590             InputStream sock
591             PPCODE:
592             #ifdef HAVE_RX_RING
593             {
594 0           struct packet_rxring_state *state = get_rxring_state((SV*)sv_2io(ST(0)));
595             char *addr = frame_ptr(state);
596             #if defined(HAVE_TPACKET2)
597             struct tpacket2_hdr *hdr = (struct tpacket2_hdr *)addr;
598             #elif defined(HAVE_TPACKET)
599             struct tpacket_hdr *hdr = (struct tpacket_hdr *)addr;
600             #endif
601 0           hdr->tp_status = TP_STATUS_KERNEL;
602              
603 0           state->frame_idx = (state->frame_idx + 1) % state->frame_nr;
604             }
605              
606 0           XSRETURN(0);
607             #else
608             croak("done_ring_frame() not supported on this platform");
609             #endif