File Coverage

lib/Socket/Packet.xs
Criterion Covered Total %
statement 75 122 61.4
branch 29 96 30.2
condition n/a
subroutine n/a
pod n/a
total 104 218 47.7


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