]> The Tcpdump Group git mirrors - libpcap/blob - pcap-canusb-linux.c
9a176c30f1a84e5c1e835d1768f6e88d658894db
[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 <stdlib.h>
42 #include <unistd.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 #include <string.h>
46 #include <pthread.h>
47
48 #include "pcap-int.h"
49 #include "pcap-canusb-linux.h"
50
51 #define CANUSB_IFACE "canusb"
52
53 #define CANUSB_VID 0x0403
54 #define CANUSB_PID 0x8990
55
56 #define USE_THREAD 1
57
58 #if USE_THREAD == 0
59 #include <signal.h>
60 #endif
61
62
63 /* forward declaration */
64 static int canusb_activate(pcap_t *);
65 static int canusb_read_linux(pcap_t *, int , pcap_handler , u_char *);
66 static int canusb_inject_linux(pcap_t *, const void *, size_t);
67 static int canusb_setfilter_linux(pcap_t *, struct bpf_program *);
68 static int canusb_setdirection_linux(pcap_t *, pcap_direction_t);
69 static int canusb_stats_linux(pcap_t *, struct pcap_stat *);
70
71 struct CAN_Msg
72 {
73 uint32_t timestamp;
74 uint32_t id;
75 uint32_t length;
76 uint8_t data[8];
77 };
78
79 /*
80 * Private data for capturing on Linux CANbus USB devices.
81 */
82 struct pcap_canusb {
83 libusb_context *ctx;
84 libusb_device_handle *dev;
85 pthread_t worker;
86 int rdpipe, wrpipe;
87 volatile int loop;
88 };
89
90 int canusb_findalldevs(pcap_if_t **alldevsp, char *err_str)
91 {
92 libusb_context *fdctx;
93 libusb_device** devs;
94 unsigned char sernum[65];
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_device_handle *dh;
114
115 libusb_get_device_descriptor(devs[i],&desc);
116
117 if ((desc.idVendor != CANUSB_VID) || (desc.idProduct != CANUSB_PID))
118 continue; /* It is not, check next device */
119
120 /* It is! */
121 dh = NULL;
122
123 if ((ret = libusb_open(devs[i],&dh)) == 0)
124 {
125 char dev_name[30];
126 char dev_descr[50];
127 int n = libusb_get_string_descriptor_ascii(dh,desc.iSerialNumber,sernum,64);
128 sernum[n] = 0;
129
130 pcap_snprintf(dev_name, 30, CANUSB_IFACE"%s", sernum);
131 pcap_snprintf(dev_descr, 50, "CanUSB [%s]", sernum);
132
133 libusb_close(dh);
134
135 if (pcap_add_if(alldevsp, dev_name, 0, dev_descr, err_str) < 0)
136 {
137 libusb_free_device_list(devs,1);
138 libusb_exit(fdctx);
139 return -1;
140 }
141 }
142 }
143
144 libusb_free_device_list(devs,1);
145 libusb_exit(fdctx);
146 return 0;
147 }
148
149 static libusb_device_handle* canusb_opendevice(struct libusb_context *ctx, char* devserial)
150 {
151 libusb_device** devs;
152 unsigned char serial[65];
153 int cnt,i,n;
154
155 cnt = libusb_get_device_list(ctx,&devs);
156
157 for(i=0;i<cnt;i++)
158 {
159 /* Check if this device is interesting. */
160 struct libusb_device_descriptor desc;
161 libusb_device_handle *dh;
162
163 libusb_get_device_descriptor(devs[i],&desc);
164
165 if ((desc.idVendor != CANUSB_VID) || (desc.idProduct != CANUSB_PID))
166 continue;
167
168 /* Found one! */
169 dh = NULL;
170
171 if (libusb_open(devs[i],&dh) != 0) continue;
172
173 n = libusb_get_string_descriptor_ascii(dh,desc.iSerialNumber,serial,64);
174 serial[n] = 0;
175
176 if ((devserial) && (strcmp((char *)serial,devserial) != 0))
177 {
178 libusb_close(dh);
179 continue;
180 }
181
182 if ((libusb_kernel_driver_active(dh,0)) && (libusb_detach_kernel_driver(dh,0) != 0))
183 {
184 libusb_close(dh);
185 continue;
186 }
187
188 if (libusb_set_configuration(dh,1) != 0)
189 {
190 libusb_close(dh);
191 continue;
192 }
193
194 if (libusb_claim_interface(dh,0) != 0)
195 {
196 libusb_close(dh);
197 continue;
198 }
199
200 /* Fount it! */
201 libusb_free_device_list(devs,1);
202 return dh;
203 }
204
205 libusb_free_device_list(devs,1);
206 return NULL;
207 }
208
209
210 pcap_t *
211 canusb_create(const char *device, char *ebuf, int *is_ours)
212 {
213 const char *cp;
214 char *cpend;
215 long devnum;
216 pcap_t* p;
217 struct pcap_canusb *canusb;
218
219 /* Does this look like a DAG device? */
220 cp = strrchr(device, '/');
221 if (cp == NULL)
222 cp = device;
223 /* Does it begin with "canusb"? */
224 if (strncmp(cp, "canusb", 6) != 0) {
225 /* Nope, doesn't begin with "canusb" */
226 *is_ours = 0;
227 return NULL;
228 }
229 /* Yes - is "canusb" followed by a number? */
230 cp += 6;
231 devnum = strtol(cp, &cpend, 10);
232 if (cpend == cp || *cpend != '\0') {
233 /* Not followed by a number. */
234 *is_ours = 0;
235 return NULL;
236 }
237 if (devnum < 0) {
238 /* Followed by a non-valid number. */
239 *is_ours = 0;
240 return NULL;
241 }
242
243 /* OK, it's probably ours. */
244 *is_ours = 1;
245
246 p = pcap_create_common(ebuf, sizeof (struct pcap_canusb));
247 if (p == NULL)
248 return (NULL);
249
250 canusb = p->priv;
251 canusb->ctx = NULL;
252 canusb->dev = NULL;
253 canusb->rdpipe = -1;
254 canusb->wrpipe = -1;
255
256 p->activate_op = canusb_activate;
257
258 return (p);
259 }
260
261
262 static void* canusb_capture_thread(void *arg)
263 {
264 struct pcap_canusb *canusb = arg;
265 int i;
266 struct
267 {
268 uint8_t rxsz, txsz;
269 } status;
270
271 fcntl(canusb->wrpipe, F_SETFL, O_NONBLOCK);
272
273 while(canusb->loop)
274 {
275 int sz;
276 struct CAN_Msg msg;
277
278 libusb_interrupt_transfer(canusb->dev, 0x81, (unsigned char*)&status, sizeof(status), &sz, 100);
279 /* HACK!!!!! -> drop buffered data, read new one by reading twice. */
280 libusb_interrupt_transfer(canusb->dev, 0x81, (unsigned char*)&status, sizeof(status), &sz, 100);
281
282 for(i = 0; i<status.rxsz; i++)
283 {
284 libusb_bulk_transfer(canusb->dev, 0x85, (unsigned char*)&msg, sizeof(msg), &sz, 100);
285 if(write(canusb->wrpipe, &msg, sizeof(msg)) < 0)
286 fprintf(stderr,"write() error: %s\n", strerror(errno));
287 }
288
289 }
290
291 return NULL;
292 }
293
294 static int canusb_startcapture(struct pcap_canusb* this)
295 {
296 int pipefd[2];
297
298 if (pipe(pipefd) == -1)
299 return -1;
300
301 this->rdpipe = pipefd[0];
302 this->wrpipe = pipefd[1];
303
304 this->loop = 1;
305 pthread_create(&this->worker, NULL, canusb_capture_thread, this);
306
307 return this->rdpipe;
308 }
309
310 static void canusb_clearbufs(struct pcap_canusb* this)
311 {
312 unsigned char cmd[16];
313 int al;
314
315 cmd[0] = 1; /* Empty incoming buffer */
316 cmd[1] = 1; /* Empty outgoing buffer */
317 cmd[3] = 0; /* Not a write to serial number */
318 memset(&cmd[4],0,16-4);
319
320 libusb_interrupt_transfer(this->dev, 0x1,cmd,16,&al,100);
321 }
322
323
324 static void canusb_close(pcap_t* handle)
325 {
326 struct pcap_canusb *canusb = handle->priv;
327
328 canusb->loop = 0;
329 pthread_join(canusb->worker, NULL);
330
331 if (canusb->dev)
332 {
333 libusb_close(canusb->dev);
334 canusb->dev = NULL;
335 }
336 if (canusb->ctx)
337 {
338 libusb_exit(canusb->ctx);
339 canusb->ctx = NULL;
340 }
341 }
342
343
344
345 static int canusb_activate(pcap_t* handle)
346 {
347 struct pcap_canusb *canusb = handle->priv;
348 char *serial;
349
350 if (libusb_init(&canusb->ctx) != 0) {
351 /*
352 * XXX - what causes this to fail?
353 */
354 pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "libusb_init() failed");
355 return PCAP_ERROR;
356 }
357
358 handle->read_op = canusb_read_linux;
359
360 handle->inject_op = canusb_inject_linux;
361 handle->setfilter_op = canusb_setfilter_linux;
362 handle->setdirection_op = canusb_setdirection_linux;
363 handle->getnonblock_op = pcap_getnonblock_fd;
364 handle->setnonblock_op = pcap_setnonblock_fd;
365 handle->stats_op = canusb_stats_linux;
366 handle->cleanup_op = canusb_close;
367
368 /* Initialize some components of the pcap structure. */
369 handle->bufsize = 32;
370 handle->offset = 8;
371 handle->linktype = DLT_CAN_SOCKETCAN;
372 handle->set_datalink_op = NULL;
373
374 serial = handle->opt.device + strlen(CANUSB_IFACE);
375
376 canusb->dev = canusb_opendevice(canusb->ctx, serial);
377 if (!canusb->dev)
378 {
379 libusb_exit(canusb->ctx);
380 pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't open USB Device");
381 return PCAP_ERROR;
382 }
383
384 canusb_clearbufs(canusb);
385
386 handle->fd = canusb_startcapture(canusb);
387 handle->selectable_fd = handle->fd;
388
389 return 0;
390 }
391
392
393
394
395 static int
396 canusb_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user)
397 {
398 static struct timeval firstpacket = { -1, -1};
399 int i = 0;
400 struct CAN_Msg msg;
401 struct pcap_pkthdr pkth;
402
403 while(i < max_packets)
404 {
405 int n;
406 usleep(10 * 1000);
407 n = read(handle->fd, &msg, sizeof(msg));
408 if (n <= 0)
409 break;
410 pkth.caplen = pkth.len = n;
411 pkth.caplen -= 4;
412 pkth.caplen -= 8 - msg.length;
413
414 if ((firstpacket.tv_sec == -1) && (firstpacket.tv_usec == -1))
415 gettimeofday(&firstpacket, NULL);
416
417 pkth.ts.tv_usec = firstpacket.tv_usec + (msg.timestamp % 100) * 10000;
418 pkth.ts.tv_sec = firstpacket.tv_usec + (msg.timestamp / 100);
419 if (pkth.ts.tv_usec > 1000000)
420 {
421 pkth.ts.tv_usec -= 1000000;
422 pkth.ts.tv_sec++;
423 }
424
425 callback(user, &pkth, (void*)&msg.id);
426 i++;
427 }
428
429 return i;
430 }
431
432
433 static int
434 canusb_inject_linux(pcap_t *handle, const void *buf, size_t size)
435 {
436 /* not yet implemented */
437 pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "inject not supported on canusb devices");
438 return (-1);
439 }
440
441
442 static int
443 canusb_stats_linux(pcap_t *handle, struct pcap_stat *stats)
444 {
445 /* not yet implemented */
446 stats->ps_recv = 0; /* number of packets received */
447 stats->ps_drop = 0; /* number of packets dropped */
448 stats->ps_ifdrop = 0; /* drops by interface -- only supported on some platforms */
449 return 0;
450 }
451
452
453 static int
454 canusb_setfilter_linux(pcap_t *p, struct bpf_program *fp)
455 {
456 /* not yet implemented */
457 return 0;
458 }
459
460
461 static int
462 canusb_setdirection_linux(pcap_t *p, pcap_direction_t d)
463 {
464 /* no support for PCAP_D_OUT */
465 if (d == PCAP_D_OUT)
466 {
467 pcap_snprintf(p->errbuf, sizeof(p->errbuf),
468 "Setting direction to PCAP_D_OUT is not supported on this interface");
469 return -1;
470 }
471
472 p->direction = d;
473
474 return 0;
475 }
476
477
478 /* eof */