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 * SocketCan 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 "pcap-int.h"
40#include "pcap-can-linux.h"
41
42#ifdef NEED_STRERROR_H
43#include "strerror.h"
44#endif
45
46#include <errno.h>
47#include <stdlib.h>
48#include <unistd.h>
49#include <fcntl.h>
50#include <string.h>
51#include <sys/ioctl.h>
52#include <sys/socket.h>
53#include <net/if.h>
54#include <arpa/inet.h>
55
56#include <linux/can.h>
57#include <linux/can/raw.h>
58
59/* not yet defined anywhere */
60#ifndef PF_CAN
61#define PF_CAN 29
62#endif
63#ifndef AF_CAN
64#define AF_CAN PF_CAN
65#endif
66
67/* forward declaration */
68static int can_activate(pcap_t *);
69static int can_read_linux(pcap_t *, int , pcap_handler , u_char *);
70static int can_inject_linux(pcap_t *, const void *, size_t);
71static int can_setfilter_linux(pcap_t *, struct bpf_program *);
72static int can_setdirection_linux(pcap_t *, pcap_direction_t);
73static int can_stats_linux(pcap_t *, struct pcap_stat *);
74
75pcap_t *
76can_create(const char *device, char *ebuf)
77{
78	pcap_t* p;
79
80	p = pcap_create_common(device, ebuf);
81	if (p == NULL)
82		return (NULL);
83
84	p->activate_op = can_activate;
85	return (p);
86}
87
88
89static int
90can_activate(pcap_t* handle)
91{
92	struct sockaddr_can addr;
93	struct ifreq ifr;
94
95	/* Initialize some components of the pcap structure. */
96	handle->bufsize = 24;
97	handle->offset = 8;
98	handle->linktype = DLT_CAN_SOCKETCAN;
99	handle->read_op = can_read_linux;
100	handle->inject_op = can_inject_linux;
101	handle->setfilter_op = can_setfilter_linux;
102	handle->setdirection_op = can_setdirection_linux;
103	handle->set_datalink_op = NULL;
104	handle->getnonblock_op = pcap_getnonblock_fd;
105	handle->setnonblock_op = pcap_setnonblock_fd;
106	handle->stats_op = can_stats_linux;
107
108	/* Create socket */
109	handle->fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
110	if (handle->fd < 0)
111	{
112		snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't create raw socket %d:%s",
113			errno, strerror(errno));
114		return PCAP_ERROR;
115	}
116
117	/* get interface index */
118	memset(&ifr, 0, sizeof(ifr));
119	strncpy(ifr.ifr_name, handle->opt.source, sizeof(ifr.ifr_name));
120	if (ioctl(handle->fd, SIOCGIFINDEX, &ifr) < 0)
121	{
122		snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
123				"Unable to get interface index: %s",
124			pcap_strerror(errno));
125		pcap_cleanup_live_common(handle);
126		return PCAP_ERROR;
127	}
128	handle->md.ifindex = ifr.ifr_ifindex;
129
130	/* allocate butter */
131	handle->buffer = malloc(handle->bufsize);
132	if (!handle->buffer)
133	{
134		snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't allocate dump buffer: %s",
135			pcap_strerror(errno));
136		pcap_cleanup_live_common(handle);
137		return PCAP_ERROR;
138	}
139
140	/* Bind to the socket */
141	addr.can_family = AF_CAN;
142	addr.can_ifindex = handle->md.ifindex;
143	if( bind( handle->fd, (struct sockaddr*)&addr, sizeof(addr) ) < 0  )
144	{
145		snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't attach to device %d %d:%s",
146			handle->md.ifindex, errno, strerror(errno));
147		pcap_cleanup_live_common(handle);
148		return PCAP_ERROR;
149	}
150
151	if (handle->opt.rfmon)
152	{
153		/* Monitor mode doesn't apply to CAN devices. */
154		pcap_cleanup_live_common(handle);
155		return PCAP_ERROR;
156	}
157
158	handle->selectable_fd = handle->fd;
159	return 0;
160
161}
162
163
164static int
165can_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user)
166{
167	struct msghdr msg;
168	struct pcap_pkthdr pkth;
169	struct iovec iv;
170	struct can_frame* cf;
171
172	iv.iov_base = &handle->buffer[handle->offset];
173	iv.iov_len = handle->snapshot;
174
175	memset(&msg, 0, sizeof(msg));
176	msg.msg_iov = &iv;
177	msg.msg_iovlen = 1;
178	msg.msg_control = handle->buffer;
179	msg.msg_controllen = handle->offset;
180
181	do
182	{
183		pkth.caplen = recvmsg(handle->fd, &msg, 0);
184		if (handle->break_loop)
185		{
186			handle->break_loop = 0;
187			return -2;
188		}
189	} while ((pkth.caplen == -1) && (errno == EINTR));
190
191	if (pkth.caplen < 0)
192	{
193		snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't receive packet %d:%s",
194			errno, strerror(errno));
195		return -1;
196	}
197
198	/* adjust capture len according to frame len */
199	cf = (struct can_frame*)&handle->buffer[8];
200	pkth.caplen -= 8 - cf->can_dlc;
201	pkth.len = pkth.caplen;
202
203	cf->can_id = htonl( cf->can_id );
204
205	if( -1 == gettimeofday(&pkth.ts, NULL) )
206	{
207		snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't get time of day %d:%s",
208			errno, strerror(errno));
209		return -1;
210	}
211
212	callback(user, &pkth, &handle->buffer[8]);
213
214	return 1;
215}
216
217
218static int
219can_inject_linux(pcap_t *handle, const void *buf, size_t size)
220{
221	/* not yet implemented */
222	snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "inject not supported on "
223		"can devices");
224	return (-1);
225}
226
227
228static int
229can_stats_linux(pcap_t *handle, struct pcap_stat *stats)
230{
231	/* not yet implemented */
232	stats->ps_recv = 0;			 /* number of packets received */
233	stats->ps_drop = 0;			 /* number of packets dropped */
234	stats->ps_ifdrop = 0;		 /* drops by interface -- only supported on some platforms */
235	return 0;
236}
237
238
239static int
240can_setfilter_linux(pcap_t *p, struct bpf_program *fp)
241{
242	/* not yet implemented */
243	return 0;
244}
245
246
247static int
248can_setdirection_linux(pcap_t *p, pcap_direction_t d)
249{
250	/* no support for PCAP_D_OUT */
251	if (d == PCAP_D_OUT)
252	{
253		snprintf(p->errbuf, sizeof(p->errbuf),
254			"Setting direction to PCAP_D_OUT is not supported on can");
255		return -1;
256	}
257
258	p->direction = d;
259
260	return 0;
261}
262
263
264/* eof */
265