xref: /freebsd/contrib/libpcap/pcap-haiku.cpp (revision 6f9cba8f)
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  */
32 struct pcap_haiku {
33 	struct pcap_stat	stat;
34 	char	*device;	/* device name */
35 };
36 
37 
38 bool
prepare_request(struct ifreq & request,const char * name)39 prepare_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 
49 static int
pcap_read_haiku(pcap_t * handle,int maxPackets _U_,pcap_handler callback,u_char * userdata)50 pcap_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 
109 static int
pcap_inject_haiku(pcap_t * handle,const void * buffer,int size)110 pcap_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 
121 static int
pcap_stats_haiku(pcap_t * handle,struct pcap_stat * stats)122 pcap_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 
146 static int
pcap_activate_haiku(pcap_t * handle)147 pcap_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 
202 extern "C" pcap_t *
pcap_create_interface(const char * device,char * errorBuffer)203 pcap_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 
270 static int
can_be_bound(const char * name _U_)271 can_be_bound(const char *name _U_)
272 {
273 	return 1;
274 }
275 
276 static int
get_if_flags(const char * name,bpf_u_int32 * flags,char * errbuf)277 get_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 
291 extern "C" int
pcap_platform_finddevs(pcap_if_list_t * _allDevices,char * errorBuffer)292 pcap_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  */
301 extern "C" const char *
pcap_lib_version(void)302 pcap_lib_version(void)
303 {
304 	return (PCAP_VERSION_STRING);
305 }
306