1/*
2 * Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel D��rfler, axeld@pinc-software.de
7 *		James Woodcock
8 */
9
10
11#include "config.h"
12#include "pcap-int.h"
13
14#include <OS.h>
15
16#include <sys/socket.h>
17#include <sys/sockio.h>
18
19#include <net/if.h>
20#include <net/if_dl.h>
21#include <net/if_types.h>
22
23#include <errno.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28
29/*
30 * Private data for capturing on Haiku sockets.
31 */
32struct pcap_haiku {
33	struct pcap_stat	stat;
34	char	*device;	/* device name */
35};
36
37
38bool
39prepare_request(struct ifreq& request, const char* name)
40{
41	if (strlen(name) >= IF_NAMESIZE)
42		return false;
43
44	strcpy(request.ifr_name, name);
45	return true;
46}
47
48
49static int
50pcap_read_haiku(pcap_t* handle, int maxPackets _U_, pcap_handler callback,
51	u_char* userdata)
52{
53	// Receive a single packet
54
55	u_char* buffer = (u_char*)handle->buffer + handle->offset;
56	struct sockaddr_dl from;
57	ssize_t bytesReceived;
58	do {
59		if (handle->break_loop) {
60			// Clear the break loop flag, and return -2 to indicate our
61			// reasoning
62			handle->break_loop = 0;
63			return -2;
64		}
65
66		socklen_t fromLength = sizeof(from);
67		bytesReceived = recvfrom(handle->fd, buffer, handle->bufsize, MSG_TRUNC,
68			(struct sockaddr*)&from, &fromLength);
69	} while (bytesReceived < 0 && errno == B_INTERRUPTED);
70
71	if (bytesReceived < 0) {
72		if (errno == B_WOULD_BLOCK) {
73			// there is no packet for us
74			return 0;
75		}
76
77		snprintf(handle->errbuf, sizeof(handle->errbuf),
78			"recvfrom: %s", strerror(errno));
79		return -1;
80	}
81
82	int32 captureLength = bytesReceived;
83	if (captureLength > handle->snapshot)
84		captureLength = handle->snapshot;
85
86	// run the packet filter
87	if (handle->fcode.bf_insns) {
88		if (pcap_filter(handle->fcode.bf_insns, buffer, bytesReceived,
89				captureLength) == 0) {
90			// packet got rejected
91			return 0;
92		}
93	}
94
95	// fill in pcap_header
96	pcap_pkthdr header;
97	header.caplen = captureLength;
98	header.len = bytesReceived;
99	header.ts.tv_usec = system_time() % 1000000;
100	header.ts.tv_sec = system_time() / 1000000;
101	// TODO: get timing from packet!!!
102
103	/* Call the user supplied callback function */
104	callback(userdata, &header, buffer);
105	return 1;
106}
107
108
109static int
110pcap_inject_haiku(pcap_t *handle, const void *buffer, int size)
111{
112	// we don't support injecting packets yet
113	// TODO: use the AF_LINK protocol (we need another socket for this) to
114	// inject the packets
115	strlcpy(handle->errbuf, "Sending packets isn't supported yet",
116		PCAP_ERRBUF_SIZE);
117	return -1;
118}
119
120
121static int
122pcap_stats_haiku(pcap_t *handle, struct pcap_stat *stats)
123{
124	struct pcap_haiku* handlep = (struct pcap_haiku*)handle->priv;
125	ifreq request;
126	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
127	if (socket < 0) {
128		return -1;
129	}
130	prepare_request(request, handlep->device);
131	if (ioctl(socket, SIOCGIFSTATS, &request, sizeof(struct ifreq)) < 0) {
132		snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "pcap_stats: %s",
133			strerror(errno));
134		close(socket);
135		return -1;
136	}
137
138	close(socket);
139	handlep->stat.ps_recv += request.ifr_stats.receive.packets;
140	handlep->stat.ps_drop += request.ifr_stats.receive.dropped;
141	*stats = handlep->stat;
142	return 0;
143}
144
145
146static int
147pcap_activate_haiku(pcap_t *handle)
148{
149	struct pcap_haiku* handlep = (struct pcap_haiku*)handle->priv;
150
151	const char* device = handle->opt.device;
152
153	handle->read_op = pcap_read_haiku;
154	handle->setfilter_op = install_bpf_program; /* no kernel filtering */
155	handle->inject_op = pcap_inject_haiku;
156	handle->stats_op = pcap_stats_haiku;
157
158	// use default hooks where possible
159	handle->getnonblock_op = pcap_getnonblock_fd;
160	handle->setnonblock_op = pcap_setnonblock_fd;
161
162	/*
163	 * Turn a negative snapshot value (invalid), a snapshot value of
164	 * 0 (unspecified), or a value bigger than the normal maximum
165	 * value, into the maximum allowed value.
166	 *
167	 * If some application really *needs* a bigger snapshot
168	 * length, we should just increase MAXIMUM_SNAPLEN.
169	 */
170	if (handle->snapshot <= 0 || handle->snapshot > MAXIMUM_SNAPLEN)
171		handle->snapshot = MAXIMUM_SNAPLEN;
172
173	handlep->device	= strdup(device);
174	if (handlep->device == NULL) {
175		pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
176			errno, "strdup");
177		return PCAP_ERROR;
178	}
179
180	handle->bufsize = 65536;
181	// TODO: should be determined by interface MTU
182
183	// allocate buffer for monitoring the device
184	handle->buffer = (u_char*)malloc(handle->bufsize);
185	if (handle->buffer == NULL) {
186		pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
187			errno, "buffer malloc");
188		return PCAP_ERROR;
189	}
190
191	handle->offset = 0;
192	handle->linktype = DLT_EN10MB;
193	// TODO: check interface type!
194
195	return 0;
196}
197
198
199//	#pragma mark - pcap API
200
201
202extern "C" pcap_t *
203pcap_create_interface(const char *device, char *errorBuffer)
204{
205	// TODO: handle promiscuous mode!
206
207	// we need a socket to talk to the networking stack
208	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
209	if (socket < 0) {
210		snprintf(errorBuffer, PCAP_ERRBUF_SIZE,
211			"The networking stack doesn't seem to be available.\n");
212		return NULL;
213	}
214
215	struct ifreq request;
216	if (!prepare_request(request, device)) {
217		snprintf(errorBuffer, PCAP_ERRBUF_SIZE,
218			"Interface name \"%s\" is too long.", device);
219		close(socket);
220		return NULL;
221	}
222
223	// check if the interface exist
224	if (ioctl(socket, SIOCGIFINDEX, &request, sizeof(request)) < 0) {
225		snprintf(errorBuffer, PCAP_ERRBUF_SIZE,
226			"Interface \"%s\" does not exist.\n", device);
227		close(socket);
228		return NULL;
229	}
230
231	close(socket);
232	// no longer needed after this point
233
234	// get link level interface for this interface
235
236	socket = ::socket(AF_LINK, SOCK_DGRAM, 0);
237	if (socket < 0) {
238		snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "No link level: %s\n",
239			strerror(errno));
240		return NULL;
241	}
242
243	// start monitoring
244	if (ioctl(socket, SIOCSPACKETCAP, &request, sizeof(struct ifreq)) < 0) {
245		snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "Cannot start monitoring: %s\n",
246			strerror(errno));
247		close(socket);
248		return NULL;
249	}
250
251	struct wrapper_struct { pcap_t __common; struct pcap_haiku __private; };
252	pcap_t* handle = pcap_create_common(errorBuffer,
253		sizeof (struct wrapper_struct),
254		offsetof (struct wrapper_struct, __private));
255
256	if (handle == NULL) {
257		snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "malloc: %s", strerror(errno));
258		close(socket);
259		return NULL;
260	}
261
262	handle->selectable_fd = socket;
263	handle->fd = socket;
264
265	handle->activate_op = pcap_activate_haiku;
266
267	return handle;
268}
269
270static int
271can_be_bound(const char *name _U_)
272{
273	return 1;
274}
275
276static int
277get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf)
278{
279	/* TODO */
280	if (*flags & PCAP_IF_LOOPBACK) {
281		/*
282		 * Loopback devices aren't wireless, and "connected"/
283		 * "disconnected" doesn't apply to them.
284		 */
285		*flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE;
286		return (0);
287	}
288	return (0);
289}
290
291extern "C" int
292pcap_platform_finddevs(pcap_if_list_t* _allDevices, char* errorBuffer)
293{
294	return pcap_findalldevs_interfaces(_allDevices, errorBuffer, can_be_bound,
295		get_if_flags);
296}
297
298/*
299 * Libpcap version string.
300 */
301extern "C" const char *
302pcap_lib_version(void)
303{
304	return (PCAP_VERSION_STRING);
305}
306