]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/libpcap/pcap-canusb-linux.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / libpcap / pcap-canusb-linux.c
1 /*
2  * Copyright (c) 2009 Felix Obenhuber
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote
15  * products derived from this software without specific prior written
16  * permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * Sockettrace sniffing API implementation for Linux platform
31  * By Felix Obenhuber <felix@obenhuber.de>
32  *
33  */
34
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38
39 #include <libusb-1.0/libusb.h>
40
41 #include "pcap-int.h"
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #include <string.h>
46
47
48 #define CANUSB_IFACE "canusb"
49
50 #define CANUSB_VID 0x0403
51 #define CANUSB_PID 0x8990
52
53 #define USE_THREAD 1
54
55 #if USE_THREAD == 0
56 #include <signal.h>
57 #endif
58
59
60 /* forward declaration */
61 static int canusb_activate(pcap_t *);
62 static int canusb_read_linux(pcap_t *, int , pcap_handler , u_char *);
63 static int canusb_inject_linux(pcap_t *, const void *, size_t);
64 static int canusb_setfilter_linux(pcap_t *, struct bpf_program *);
65 static int canusb_setdirection_linux(pcap_t *, pcap_direction_t);
66 static int canusb_stats_linux(pcap_t *, struct pcap_stat *);
67
68 struct CAN_Msg
69 {
70     uint32_t timestamp;
71     uint32_t id;
72     uint32_t length;
73     uint8_t data[8];
74 };
75
76 struct canusb_t
77 {
78     libusb_context *ctx;
79     libusb_device_handle *dev;
80     char *serial;
81     pthread_t worker;
82     int rdpipe, wrpipe;
83     volatile int* loop;
84 };
85
86 static struct canusb_t canusb;
87 static volatile int loop;
88
89 int canusb_findalldevs(pcap_if_t **alldevsp, char *err_str)
90 {
91     libusb_context *fdctx;
92     libusb_device** devs;
93     unsigned char sernum[65];
94     unsigned char buf[96];
95     int cnt, i;
96     
97     if (libusb_init(&fdctx) != 0) {
98         /*
99          * XXX - if this doesn't just mean "no USB file system mounted",
100          * perhaps we should report a real error rather than just
101          * saying "no CANUSB devices".
102          */
103         return 0;
104     } 
105
106     cnt = libusb_get_device_list(fdctx,&devs);
107
108     for(i=0;i<cnt;i++)
109     {
110         int ret;
111         // Check if this device is interesting.
112         struct libusb_device_descriptor desc;
113         libusb_get_device_descriptor(devs[i],&desc);
114
115         if ((desc.idVendor != CANUSB_VID) || (desc.idProduct != CANUSB_PID)) 
116             continue; //It is not, check next device
117           
118         //It is!
119         libusb_device_handle *dh = NULL;
120
121         if (ret = libusb_open(devs[i],&dh) == 0)
122         {
123             char dev_name[30];
124             char dev_descr[50]; 
125             int n = libusb_get_string_descriptor_ascii(dh,desc.iSerialNumber,sernum,64);
126             sernum[n] = 0;
127
128             snprintf(dev_name, 30, CANUSB_IFACE"%s", sernum);
129             snprintf(dev_descr, 50, "CanUSB [%s]", sernum);
130             
131             libusb_close(dh);
132             
133             if (pcap_add_if(alldevsp, dev_name, 0, dev_descr, err_str) < 0)
134             {
135                 libusb_free_device_list(devs,1);
136                 return -1;
137             }
138         }
139     }
140
141     libusb_free_device_list(devs,1);
142     libusb_exit(fdctx);
143     return 0;
144 }
145
146 static libusb_device_handle* canusb_opendevice(struct libusb_context *ctx, char* devserial)
147 {
148     libusb_device_handle* dh;
149     libusb_device** devs;
150     unsigned char serial[65];
151     int cnt,i,n;
152     
153     cnt = libusb_get_device_list(ctx,&devs);
154
155     for(i=0;i<cnt;i++)
156     {    
157         // Check if this device is interesting.
158         struct libusb_device_descriptor desc;
159         libusb_get_device_descriptor(devs[i],&desc);
160
161         if ((desc.idVendor != CANUSB_VID) || (desc.idProduct != CANUSB_PID))
162           continue;
163           
164         //Found one!
165         libusb_device_handle *dh = NULL;
166
167         if (libusb_open(devs[i],&dh) != 0) continue;
168
169         n = libusb_get_string_descriptor_ascii(dh,desc.iSerialNumber,serial,64);
170         serial[n] = 0;
171
172         if ((devserial) && (strcmp(serial,devserial) != 0))
173         {
174             libusb_close(dh);
175             continue;
176         }
177
178         if ((libusb_kernel_driver_active(dh,0)) && (libusb_detach_kernel_driver(dh,0) != 0))
179         {
180             libusb_close(dh);
181             continue;
182         }
183
184         if (libusb_set_configuration(dh,1) != 0)
185         {
186             libusb_close(dh);
187             continue;
188         }
189
190         if (libusb_claim_interface(dh,0) != 0)
191         {
192             libusb_close(dh);
193             continue;
194         }
195         
196         //Fount it!
197         libusb_free_device_list(devs,1);        
198         return dh;
199     }
200
201     libusb_free_device_list(devs,1);
202     return NULL;
203 }
204
205
206 pcap_t *
207 canusb_create(const char *device, char *ebuf, int *is_ours)
208
209     const char *cp;
210     char *cpend;
211     long devnum;
212     pcap_t* p;
213
214     libusb_init(&canusb.ctx);
215
216     /* Does this look like a DAG device? */
217     cp = strrchr(device, '/');
218     if (cp == NULL)
219         cp = device;
220     /* Does it begin with "canusb"? */
221     if (strncmp(cp, "canusb", 6) != 0) {
222         /* Nope, doesn't begin with "canusb" */
223         *is_ours = 0;
224         return NULL;
225     }
226     /* Yes - is "canusb" followed by a number? */
227     cp += 6;
228     devnum = strtol(cp, &cpend, 10);
229     if (cpend == cp || *cpend != '\0') {
230         /* Not followed by a number. */
231         *is_ours = 0;
232         return NULL;
233     }
234     if (devnum < 0) {
235         /* Followed by a non-valid number. */
236         *is_ours = 0;
237         return NULL;
238     }
239
240     /* OK, it's probably ours. */
241     *is_ours = 1;
242
243     p = pcap_create_common(device, ebuf);
244     if (p == NULL)
245         return (NULL);
246
247     memset(&canusb, 0x00, sizeof(canusb));
248
249     p->activate_op = canusb_activate;
250
251     return (p);
252 }
253
254
255 static void* canusb_capture_thread(struct canusb_t *canusb)
256 {
257     struct libusb_context *ctx;
258     libusb_device_handle *dev;
259     int i, n;  
260     struct 
261     {
262       uint8_t rxsz, txsz;
263     } status;
264     char *serial;
265   
266     libusb_init(&ctx);
267   
268     serial = canusb->serial;
269     dev = canusb_opendevice(ctx, serial);
270   
271     fcntl(canusb->wrpipe, F_SETFL, O_NONBLOCK);  
272
273     while(*canusb->loop)
274     {
275         int sz, ret;
276         struct CAN_Msg msg;
277     
278         libusb_interrupt_transfer(dev, 0x81, (unsigned char*)&status, sizeof(status), &sz, 100);
279         //HACK!!!!! -> drop buffered data, read new one by reading twice.        
280         ret = libusb_interrupt_transfer(dev, 0x81, (unsigned char*)&status, sizeof(status), &sz, 100);                                   
281
282         for(i = 0; i<status.rxsz; i++)
283         {
284             libusb_bulk_transfer(dev, 0x85, (unsigned char*)&msg, sizeof(msg), &sz, 100);      
285             n = write(canusb->wrpipe, &msg, sizeof(msg));
286         }
287
288     }
289   
290     libusb_close(dev);
291     libusb_exit(ctx);
292   
293     return NULL;
294 }
295
296 static int canusb_startcapture(struct canusb_t* this)
297 {
298     int pipefd[2];
299
300     if (pipe(pipefd) == -1)
301         return -1;
302
303     canusb.rdpipe = pipefd[0];
304     canusb.wrpipe = pipefd[1];
305     canusb.loop = &loop;
306
307     loop = 1;  
308     pthread_create(&this->worker, NULL, canusb_capture_thread, &canusb);
309
310     return canusb.rdpipe;
311 }
312
313 static void canusb_clearbufs(struct canusb_t* this)
314 {
315     unsigned char cmd[16];
316     int al;
317
318     cmd[0] = 1;  //Empty incoming buffer
319     cmd[1] = 1;  //Empty outgoing buffer
320     cmd[3] = 0;  //Not a write to serial number
321     memset(&cmd[4],0,16-4);
322         
323     libusb_interrupt_transfer(this->dev, 0x1,cmd,16,&al,100);
324 }
325
326
327 static void canusb_close(pcap_t* handle)
328 {
329     loop = 0;
330     pthread_join(canusb.worker, NULL);
331
332     if (canusb.dev)
333     {
334         libusb_close(canusb.dev);
335         canusb.dev = NULL;    
336     }    
337 }
338
339
340
341 static int canusb_activate(pcap_t* handle)
342 {
343     char *serial;
344
345     handle->read_op = canusb_read_linux;
346
347     handle->inject_op = canusb_inject_linux;
348     handle->setfilter_op = canusb_setfilter_linux;
349     handle->setdirection_op = canusb_setdirection_linux;
350     handle->getnonblock_op = pcap_getnonblock_fd;
351     handle->setnonblock_op = pcap_setnonblock_fd;
352     handle->stats_op = canusb_stats_linux;
353     handle->cleanup_op = canusb_close;
354
355     /* Initialize some components of the pcap structure. */
356     handle->bufsize = 32;
357     handle->offset = 8;
358     handle->linktype = DLT_CAN_SOCKETCAN;
359     handle->set_datalink_op = NULL;
360
361     serial = handle->opt.source + strlen(CANUSB_IFACE);
362     canusb.serial = strdup(serial);
363
364     canusb.dev = canusb_opendevice(canusb.ctx,serial);
365     if (!canusb.dev)
366     {
367         snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't open USB Device:");  
368         return PCAP_ERROR;
369     }
370
371     canusb_clearbufs(&canusb);
372
373     handle->fd = canusb_startcapture(&canusb);
374     handle->selectable_fd = handle->fd;
375
376     return 0;
377 }
378
379
380
381
382 static int
383 canusb_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user)
384 {
385     static struct timeval firstpacket = { -1, -1};
386   
387     int msgsent = 0;
388     int i = 0;
389     struct CAN_Msg msg;
390     struct pcap_pkthdr pkth;
391   
392     while(i < max_packets)
393     {
394         int n;
395         usleep(10 * 1000);
396         n = read(handle->fd, &msg, sizeof(msg));
397         if (n <= 0)
398             break;
399         pkth.caplen = pkth.len = n;
400         pkth.caplen -= 4;
401         pkth.caplen -= 8 - msg.length;
402     
403         if ((firstpacket.tv_sec == -1) && (firstpacket.tv_usec == -1))
404             gettimeofday(&firstpacket, NULL);
405       
406         pkth.ts.tv_usec = firstpacket.tv_usec + (msg.timestamp % 100) * 10000;
407         pkth.ts.tv_sec = firstpacket.tv_usec + (msg.timestamp / 100);
408         if (pkth.ts.tv_usec > 1000000)
409         {
410             pkth.ts.tv_usec -= 1000000;
411             pkth.ts.tv_sec++;
412         }
413
414         callback(user, &pkth, (void*)&msg.id);
415         i++;
416     }
417   
418     return i;
419 }
420
421
422 static int
423 canusb_inject_linux(pcap_t *handle, const void *buf, size_t size)
424 {
425     /* not yet implemented */
426     snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "inject not supported on canusb devices");
427     return (-1);
428 }
429
430
431 static int
432 canusb_stats_linux(pcap_t *handle, struct pcap_stat *stats)
433 {
434     /* not yet implemented */
435     stats->ps_recv = 0;     /* number of packets received */
436     stats->ps_drop = 0;     /* number of packets dropped */
437     stats->ps_ifdrop = 0;   /* drops by interface -- only supported on some platforms */
438     return 0;
439 }
440
441
442 static int
443 canusb_setfilter_linux(pcap_t *p, struct bpf_program *fp)
444 {
445     /* not yet implemented */
446     return 0;
447 }
448
449
450 static int
451 canusb_setdirection_linux(pcap_t *p, pcap_direction_t d)
452 {
453     /* no support for PCAP_D_OUT */
454     if (d == PCAP_D_OUT)
455     {
456         snprintf(p->errbuf, sizeof(p->errbuf),
457             "Setting direction to PCAP_D_OUT is not supported on this interface");
458         return -1;
459     }
460
461     p->direction = d;
462
463     return 0;
464 }
465
466
467 /* eof */