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