]> The Tcpdump Group git mirrors - libpcap/blob - pcap-haiku.c
Prefix routines declared in pcap-int.h with pcap_.
[libpcap] / pcap-haiku.c
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 static int
39 prepare_request(struct ifreq *request, const char* name)
40 {
41 if (strlen(name) >= IF_NAMESIZE)
42 return 0;
43
44 strcpy(request->ifr_name, name);
45 return 1;
46 }
47
48
49 static int
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 = 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_t 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 struct 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
110 pcap_inject_haiku(pcap_t *handle, const void *buffer _U_, int size _U_)
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
122 pcap_stats_haiku(pcap_t *handle, struct pcap_stat *stats)
123 {
124 struct pcap_haiku* handlep = (struct pcap_haiku*)handle->priv;
125 struct ifreq request;
126 int pcapSocket = socket(AF_INET, SOCK_DGRAM, 0);
127 if (pcapSocket < 0) {
128 return -1;
129 }
130 prepare_request(&request, handlep->device);
131 if (ioctl(pcapSocket, SIOCGIFSTATS, &request, sizeof(struct ifreq)) < 0) {
132 snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "pcap_stats: %s",
133 strerror(errno));
134 close(pcapSocket);
135 return -1;
136 }
137
138 close(pcapSocket);
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
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 = pcap_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 pcap_t *
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 pcapSocket = socket(AF_INET, SOCK_DGRAM, 0);
209 if (pcapSocket < 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(pcapSocket);
220 return NULL;
221 }
222
223 // check if the interface exist
224 if (ioctl(pcapSocket, SIOCGIFINDEX, &request, sizeof(request)) < 0) {
225 snprintf(errorBuffer, PCAP_ERRBUF_SIZE,
226 "Interface \"%s\" does not exist.\n", device);
227 close(pcapSocket);
228 return NULL;
229 }
230
231 close(pcapSocket);
232 // no longer needed after this point
233
234 // get link level interface for this interface
235
236 pcapSocket = socket(AF_LINK, SOCK_DGRAM, 0);
237 if (pcapSocket < 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(pcapSocket, SIOCSPACKETCAP, &request, sizeof(struct ifreq)) < 0) {
245 snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "Cannot start monitoring: %s\n",
246 strerror(errno));
247 close(pcapSocket);
248 return NULL;
249 }
250
251 pcap_t* handle = PCAP_CREATE_COMMON(errorBuffer, struct pcap_haiku);
252 if (handle == NULL) {
253 snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "malloc: %s", strerror(errno));
254 close(pcapSocket);
255 return NULL;
256 }
257
258 handle->selectable_fd = pcapSocket;
259 handle->fd = pcapSocket;
260
261 handle->activate_op = pcap_activate_haiku;
262
263 return handle;
264 }
265
266 static int
267 can_be_bound(const char *name _U_)
268 {
269 return 1;
270 }
271
272 static int
273 get_if_flags(const char *name _U_, bpf_u_int32 *flags, char *errbuf _U_)
274 {
275 /* TODO */
276 if (*flags & PCAP_IF_LOOPBACK) {
277 /*
278 * Loopback devices aren't wireless, and "connected"/
279 * "disconnected" doesn't apply to them.
280 */
281 *flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE;
282 return (0);
283 }
284 return (0);
285 }
286
287 int
288 pcap_platform_finddevs(pcap_if_list_t* _allDevices, char* errorBuffer)
289 {
290 return pcap_findalldevs_interfaces(_allDevices, errorBuffer, can_be_bound,
291 get_if_flags);
292 }
293
294 /*
295 * Libpcap version string.
296 */
297 const char *
298 pcap_lib_version(void)
299 {
300 return (PCAP_VERSION_STRING);
301 }