xref: /dragonfly/contrib/libpcap/pcap-haiku.cpp (revision e75ef36f1332e115895388cede9dfd24ca1a806c)
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,pcap_handler callback,u_char * userdata)50 pcap_read_haiku(pcap_t* handle, int maxPackets, pcap_handler callback,
51           u_char* userdata)
52 {
53           // Receive a single packet
54 
55           struct pcap_haiku* handlep = (struct pcap_haiku*)handle->priv;
56           u_char* buffer = (u_char*)handle->buffer + handle->offset;
57           struct sockaddr_dl from;
58           ssize_t bytesReceived;
59           do {
60                     if (handle->break_loop) {
61                               // Clear the break loop flag, and return -2 to indicate our
62                               // reasoning
63                               handle->break_loop = 0;
64                               return -2;
65                     }
66 
67                     socklen_t fromLength = sizeof(from);
68                     bytesReceived = recvfrom(handle->fd, buffer, handle->bufsize, MSG_TRUNC,
69                               (struct sockaddr*)&from, &fromLength);
70           } while (bytesReceived < 0 && errno == B_INTERRUPTED);
71 
72           if (bytesReceived < 0) {
73                     if (errno == B_WOULD_BLOCK) {
74                               // there is no packet for us
75                               return 0;
76                     }
77 
78                     snprintf(handle->errbuf, sizeof(handle->errbuf),
79                               "recvfrom: %s", strerror(errno));
80                     return -1;
81           }
82 
83           int32 captureLength = bytesReceived;
84           if (captureLength > handle->snapshot)
85                     captureLength = handle->snapshot;
86 
87           // run the packet filter
88           if (handle->fcode.bf_insns) {
89                     if (pcap_filter(handle->fcode.bf_insns, buffer, bytesReceived,
90                                         captureLength) == 0) {
91                               // packet got rejected
92                               return 0;
93                     }
94           }
95 
96           // fill in pcap_header
97           pcap_pkthdr header;
98           header.caplen = captureLength;
99           header.len = bytesReceived;
100           header.ts.tv_usec = system_time() % 1000000;
101           header.ts.tv_sec = system_time() / 1000000;
102           // TODO: get timing from packet!!!
103 
104           /* Call the user supplied callback function */
105           callback(userdata, &header, buffer);
106           return 1;
107 }
108 
109 
110 static int
pcap_inject_haiku(pcap_t * handle,const void * buffer,int size)111 pcap_inject_haiku(pcap_t *handle, const void *buffer, int size)
112 {
113           // we don't support injecting packets yet
114           // TODO: use the AF_LINK protocol (we need another socket for this) to
115           // inject the packets
116           strlcpy(handle->errbuf, "Sending packets isn't supported yet",
117                     PCAP_ERRBUF_SIZE);
118           return -1;
119 }
120 
121 
122 static int
pcap_stats_haiku(pcap_t * handle,struct pcap_stat * stats)123 pcap_stats_haiku(pcap_t *handle, struct pcap_stat *stats)
124 {
125           struct pcap_haiku* handlep = (struct pcap_haiku*)handle->priv;
126           ifreq request;
127           int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
128           if (socket < 0) {
129                     return -1;
130           }
131           prepare_request(request, handlep->device);
132           if (ioctl(socket, SIOCGIFSTATS, &request, sizeof(struct ifreq)) < 0) {
133                     snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "pcap_stats: %s",
134                               strerror(errno));
135                     close(socket);
136                     return -1;
137           }
138 
139           close(socket);
140           handlep->stat.ps_recv += request.ifr_stats.receive.packets;
141           handlep->stat.ps_drop += request.ifr_stats.receive.dropped;
142           *stats = handlep->stat;
143           return 0;
144 }
145 
146 
147 static int
pcap_activate_haiku(pcap_t * handle)148 pcap_activate_haiku(pcap_t *handle)
149 {
150           struct pcap_haiku* handlep = (struct pcap_haiku*)handle->priv;
151 
152           const char* device = handle->opt.device;
153 
154           handle->read_op = pcap_read_haiku;
155           handle->setfilter_op = install_bpf_program; /* no kernel filtering */
156           handle->inject_op = pcap_inject_haiku;
157           handle->stats_op = pcap_stats_haiku;
158 
159           // use default hooks where possible
160           handle->getnonblock_op = pcap_getnonblock_fd;
161           handle->setnonblock_op = pcap_setnonblock_fd;
162 
163           handlep->device     = strdup(device);
164           if (handlep->device == NULL) {
165                     pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
166                               errno, "strdup");
167                     return PCAP_ERROR;
168           }
169 
170           handle->bufsize = 65536;
171           // TODO: should be determined by interface MTU
172 
173           // allocate buffer for monitoring the device
174           handle->buffer = (u_char*)malloc(handle->bufsize);
175           if (handle->buffer == NULL) {
176                     pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
177                               errno, "buffer malloc");
178                     return PCAP_ERROR;
179           }
180 
181           handle->offset = 0;
182           handle->linktype = DLT_EN10MB;
183           // TODO: check interface type!
184 
185           return 0;
186 }
187 
188 
189 //        #pragma mark - pcap API
190 
191 
192 extern "C" pcap_t *
pcap_create_interface(const char * device,char * errorBuffer)193 pcap_create_interface(const char *device, char *errorBuffer)
194 {
195           // TODO: handle promiscuous mode!
196 
197           // we need a socket to talk to the networking stack
198           int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
199           if (socket < 0) {
200                     snprintf(errorBuffer, PCAP_ERRBUF_SIZE,
201                               "The networking stack doesn't seem to be available.\n");
202                     return NULL;
203           }
204 
205           struct ifreq request;
206           if (!prepare_request(request, device)) {
207                     snprintf(errorBuffer, PCAP_ERRBUF_SIZE,
208                               "Interface name \"%s\" is too long.", device);
209                     close(socket);
210                     return NULL;
211           }
212 
213           // check if the interface exist
214           if (ioctl(socket, SIOCGIFINDEX, &request, sizeof(request)) < 0) {
215                     snprintf(errorBuffer, PCAP_ERRBUF_SIZE,
216                               "Interface \"%s\" does not exist.\n", device);
217                     close(socket);
218                     return NULL;
219           }
220 
221           close(socket);
222           // no longer needed after this point
223 
224           // get link level interface for this interface
225 
226           socket = ::socket(AF_LINK, SOCK_DGRAM, 0);
227           if (socket < 0) {
228                     snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "No link level: %s\n",
229                               strerror(errno));
230                     return NULL;
231           }
232 
233           // start monitoring
234           if (ioctl(socket, SIOCSPACKETCAP, &request, sizeof(struct ifreq)) < 0) {
235                     snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "Cannot start monitoring: %s\n",
236                               strerror(errno));
237                     close(socket);
238                     return NULL;
239           }
240 
241           pcap_t* handle = PCAP_CREATE_COMMON(errorBuffer, struct pcap_haiku);
242           if (handle == NULL) {
243                     snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "malloc: %s", strerror(errno));
244                     close(socket);
245                     return NULL;
246           }
247 
248           handle->selectable_fd = socket;
249           handle->fd = socket;
250 
251           handle->activate_op = pcap_activate_haiku;
252 
253           return handle;
254 }
255 
256 static int
can_be_bound(const char * name)257 can_be_bound(const char *name)
258 {
259           return 1;
260 }
261 
262 static int
get_if_flags(const char * name,bpf_u_int32 * flags,char * errbuf)263 get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf)
264 {
265           /* TODO */
266           if (*flags & PCAP_IF_LOOPBACK) {
267                     /*
268                      * Loopback devices aren't wireless, and "connected"/
269                      * "disconnected" doesn't apply to them.
270                      */
271                     *flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE;
272                     return (0);
273           }
274           return (0);
275 }
276 
277 extern "C" int
pcap_platform_finddevs(pcap_if_list_t * _allDevices,char * errorBuffer)278 pcap_platform_finddevs(pcap_if_list_t* _allDevices, char* errorBuffer)
279 {
280           return pcap_findalldevs_interfaces(_allDevices, errorBuffer, can_be_bound,
281                     get_if_flags);
282 }
283