diff options
Diffstat (limited to 'pcap-can-linux.c')
-rw-r--r-- | pcap-can-linux.c | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/pcap-can-linux.c b/pcap-can-linux.c new file mode 100644 index 0000000..fb8e232 --- /dev/null +++ b/pcap-can-linux.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2009 Felix Obenhuber + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * SocketCan sniffing API implementation for Linux platform + * By Felix Obenhuber <felix@obenhuber.de> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcap-int.h" +#include "pcap-can-linux.h" + +#ifdef NEED_STRERROR_H +#include "strerror.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> +#include <arpa/inet.h> + +#include <linux/can.h> +#include <linux/can/raw.h> + +/* not yet defined anywhere */ +#ifndef PF_CAN +#define PF_CAN 29 +#endif +#ifndef AF_CAN +#define AF_CAN PF_CAN +#endif + +/* forward declaration */ +static int can_activate(pcap_t *); +static int can_read_linux(pcap_t *, int , pcap_handler , u_char *); +static int can_inject_linux(pcap_t *, const void *, size_t); +static int can_setfilter_linux(pcap_t *, struct bpf_program *); +static int can_setdirection_linux(pcap_t *, pcap_direction_t); +static int can_stats_linux(pcap_t *, struct pcap_stat *); + +pcap_t * +can_create(const char *device, char *ebuf) +{ + pcap_t* p; + + p = pcap_create_common(device, ebuf); + if (p == NULL) + return (NULL); + + p->activate_op = can_activate; + return (p); +} + + +static int +can_activate(pcap_t* handle) +{ + struct sockaddr_can addr; + struct ifreq ifr; + + /* Initialize some components of the pcap structure. */ + handle->bufsize = 24; + handle->offset = 8; + handle->linktype = DLT_CAN_SOCKETCAN; + handle->read_op = can_read_linux; + handle->inject_op = can_inject_linux; + handle->setfilter_op = can_setfilter_linux; + handle->setdirection_op = can_setdirection_linux; + handle->set_datalink_op = NULL; + handle->getnonblock_op = pcap_getnonblock_fd; + handle->setnonblock_op = pcap_setnonblock_fd; + handle->stats_op = can_stats_linux; + + /* Create socket */ + handle->fd = socket(PF_CAN, SOCK_RAW, CAN_RAW); + if (handle->fd < 0) + { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't create raw socket %d:%s", + errno, strerror(errno)); + return PCAP_ERROR; + } + + /* get interface index */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, handle->opt.source, sizeof(ifr.ifr_name)); + if (ioctl(handle->fd, SIOCGIFINDEX, &ifr) < 0) + { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "Unable to get interface index: %s", + pcap_strerror(errno)); + pcap_cleanup_live_common(handle); + return PCAP_ERROR; + } + handle->md.ifindex = ifr.ifr_ifindex; + + /* allocate butter */ + handle->buffer = malloc(handle->bufsize); + if (!handle->buffer) + { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't allocate dump buffer: %s", + pcap_strerror(errno)); + pcap_cleanup_live_common(handle); + return PCAP_ERROR; + } + + /* Bind to the socket */ + addr.can_family = AF_CAN; + addr.can_ifindex = handle->md.ifindex; + if( bind( handle->fd, (struct sockaddr*)&addr, sizeof(addr) ) < 0 ) + { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't attach to device %d %d:%s", + handle->md.ifindex, errno, strerror(errno)); + pcap_cleanup_live_common(handle); + return PCAP_ERROR; + } + + if (handle->opt.rfmon) + { + /* Monitor mode doesn't apply to CAN devices. */ + pcap_cleanup_live_common(handle); + return PCAP_ERROR; + } + + handle->selectable_fd = handle->fd; + return 0; + +} + + +static int +can_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) +{ + struct msghdr msg; + struct pcap_pkthdr pkth; + struct iovec iv; + struct can_frame* cf; + + iv.iov_base = &handle->buffer[handle->offset]; + iv.iov_len = handle->snapshot; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iv; + msg.msg_iovlen = 1; + msg.msg_control = handle->buffer; + msg.msg_controllen = handle->offset; + + do + { + pkth.caplen = recvmsg(handle->fd, &msg, 0); + if (handle->break_loop) + { + handle->break_loop = 0; + return -2; + } + } while ((pkth.caplen == -1) && (errno == EINTR)); + + if (pkth.caplen < 0) + { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't receive packet %d:%s", + errno, strerror(errno)); + return -1; + } + + /* adjust capture len according to frame len */ + cf = (struct can_frame*)&handle->buffer[8]; + pkth.caplen -= 8 - cf->can_dlc; + pkth.len = pkth.caplen; + + cf->can_id = htonl( cf->can_id ); + + if( -1 == gettimeofday(&pkth.ts, NULL) ) + { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't get time of day %d:%s", + errno, strerror(errno)); + return -1; + } + + callback(user, &pkth, &handle->buffer[8]); + + return 1; +} + + +static int +can_inject_linux(pcap_t *handle, const void *buf, size_t size) +{ + /* not yet implemented */ + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "inject not supported on " + "can devices"); + return (-1); +} + + +static int +can_stats_linux(pcap_t *handle, struct pcap_stat *stats) +{ + /* not yet implemented */ + stats->ps_recv = 0; /* number of packets received */ + stats->ps_drop = 0; /* number of packets dropped */ + stats->ps_ifdrop = 0; /* drops by interface -- only supported on some platforms */ + return 0; +} + + +static int +can_setfilter_linux(pcap_t *p, struct bpf_program *fp) +{ + /* not yet implemented */ + return 0; +} + + +static int +can_setdirection_linux(pcap_t *p, pcap_direction_t d) +{ + /* no support for PCAP_D_OUT */ + if (d == PCAP_D_OUT) + { + snprintf(p->errbuf, sizeof(p->errbuf), + "Setting direction to PCAP_D_OUT is not supported on can"); + return -1; + } + + p->direction = d; + + return 0; +} + + +/* eof */ |