diff options
Diffstat (limited to 'pcap-haiku.c')
-rw-r--r-- | pcap-haiku.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/pcap-haiku.c b/pcap-haiku.c new file mode 100644 index 00000000..1c509806 --- /dev/null +++ b/pcap-haiku.c @@ -0,0 +1,301 @@ +/* + * Copyright 2006-2010, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + * James Woodcock + */ + + +#include "config.h" +#include "pcap-int.h" + +#include <OS.h> + +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +/* + * Private data for capturing on Haiku sockets. + */ +struct pcap_haiku { + struct pcap_stat stat; + char *device; /* device name */ +}; + + +bool +prepare_request(struct ifreq *request, const char* name) +{ + if (strlen(name) >= IF_NAMESIZE) + return false; + + strcpy(request->ifr_name, name); + return true; +} + + +static int +pcap_read_haiku(pcap_t* handle, int maxPackets _U_, pcap_handler callback, + u_char* userdata) +{ + // Receive a single packet + + u_char* buffer = (u_char*)handle->buffer + handle->offset; + struct sockaddr_dl from; + ssize_t bytesReceived; + do { + if (handle->break_loop) { + // Clear the break loop flag, and return -2 to indicate our + // reasoning + handle->break_loop = 0; + return -2; + } + + socklen_t fromLength = sizeof(from); + bytesReceived = recvfrom(handle->fd, buffer, handle->bufsize, MSG_TRUNC, + (struct sockaddr*)&from, &fromLength); + } while (bytesReceived < 0 && errno == B_INTERRUPTED); + + if (bytesReceived < 0) { + if (errno == B_WOULD_BLOCK) { + // there is no packet for us + return 0; + } + + snprintf(handle->errbuf, sizeof(handle->errbuf), + "recvfrom: %s", strerror(errno)); + return -1; + } + + int32_t captureLength = bytesReceived; + if (captureLength > handle->snapshot) + captureLength = handle->snapshot; + + // run the packet filter + if (handle->fcode.bf_insns) { + if (pcap_filter(handle->fcode.bf_insns, buffer, bytesReceived, + captureLength) == 0) { + // packet got rejected + return 0; + } + } + + // fill in pcap_header + struct pcap_pkthdr header; + header.caplen = captureLength; + header.len = bytesReceived; + header.ts.tv_usec = system_time() % 1000000; + header.ts.tv_sec = system_time() / 1000000; + // TODO: get timing from packet!!! + + /* Call the user supplied callback function */ + callback(userdata, &header, buffer); + return 1; +} + + +static int +pcap_inject_haiku(pcap_t *handle, const void *buffer, int size) +{ + // we don't support injecting packets yet + // TODO: use the AF_LINK protocol (we need another socket for this) to + // inject the packets + strlcpy(handle->errbuf, "Sending packets isn't supported yet", + PCAP_ERRBUF_SIZE); + return -1; +} + + +static int +pcap_stats_haiku(pcap_t *handle, struct pcap_stat *stats) +{ + struct pcap_haiku* handlep = (struct pcap_haiku*)handle->priv; + struct ifreq request; + int pcapSocket = socket(AF_INET, SOCK_DGRAM, 0); + if (pcapSocket < 0) { + return -1; + } + prepare_request(&request, handlep->device); + if (ioctl(pcapSocket, SIOCGIFSTATS, &request, sizeof(struct ifreq)) < 0) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "pcap_stats: %s", + strerror(errno)); + close(pcapSocket); + return -1; + } + + close(pcapSocket); + handlep->stat.ps_recv += request.ifr_stats.receive.packets; + handlep->stat.ps_drop += request.ifr_stats.receive.dropped; + *stats = handlep->stat; + return 0; +} + + +static int +pcap_activate_haiku(pcap_t *handle) +{ + struct pcap_haiku* handlep = (struct pcap_haiku*)handle->priv; + + const char* device = handle->opt.device; + + handle->read_op = pcap_read_haiku; + handle->setfilter_op = install_bpf_program; /* no kernel filtering */ + handle->inject_op = pcap_inject_haiku; + handle->stats_op = pcap_stats_haiku; + + // use default hooks where possible + handle->getnonblock_op = pcap_getnonblock_fd; + handle->setnonblock_op = pcap_setnonblock_fd; + + /* + * Turn a negative snapshot value (invalid), a snapshot value of + * 0 (unspecified), or a value bigger than the normal maximum + * value, into the maximum allowed value. + * + * If some application really *needs* a bigger snapshot + * length, we should just increase MAXIMUM_SNAPLEN. + */ + if (handle->snapshot <= 0 || handle->snapshot > MAXIMUM_SNAPLEN) + handle->snapshot = MAXIMUM_SNAPLEN; + + handlep->device = strdup(device); + if (handlep->device == NULL) { + pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, + errno, "strdup"); + return PCAP_ERROR; + } + + handle->bufsize = 65536; + // TODO: should be determined by interface MTU + + // allocate buffer for monitoring the device + handle->buffer = (u_char*)malloc(handle->bufsize); + if (handle->buffer == NULL) { + pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, + errno, "buffer malloc"); + return PCAP_ERROR; + } + + handle->offset = 0; + handle->linktype = DLT_EN10MB; + // TODO: check interface type! + + return 0; +} + + +// #pragma mark - pcap API + + +pcap_t * +pcap_create_interface(const char *device, char *errorBuffer) +{ + // TODO: handle promiscuous mode! + + // we need a socket to talk to the networking stack + int pcapSocket = socket(AF_INET, SOCK_DGRAM, 0); + if (pcapSocket < 0) { + snprintf(errorBuffer, PCAP_ERRBUF_SIZE, + "The networking stack doesn't seem to be available.\n"); + return NULL; + } + + struct ifreq request; + if (!prepare_request(&request, device)) { + snprintf(errorBuffer, PCAP_ERRBUF_SIZE, + "Interface name \"%s\" is too long.", device); + close(pcapSocket); + return NULL; + } + + // check if the interface exist + if (ioctl(pcapSocket, SIOCGIFINDEX, &request, sizeof(request)) < 0) { + snprintf(errorBuffer, PCAP_ERRBUF_SIZE, + "Interface \"%s\" does not exist.\n", device); + close(pcapSocket); + return NULL; + } + + close(pcapSocket); + // no longer needed after this point + + // get link level interface for this interface + + pcapSocket = socket(AF_LINK, SOCK_DGRAM, 0); + if (pcapSocket < 0) { + snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "No link level: %s\n", + strerror(errno)); + return NULL; + } + + // start monitoring + if (ioctl(pcapSocket, SIOCSPACKETCAP, &request, sizeof(struct ifreq)) < 0) { + snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "Cannot start monitoring: %s\n", + strerror(errno)); + close(pcapSocket); + return NULL; + } + + pcap_t* handle = PCAP_CREATE_COMMON(errorBuffer, struct pcap_haiku); + if (handle == NULL) { + snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "malloc: %s", strerror(errno)); + close(pcapSocket); + return NULL; + } + + handle->selectable_fd = pcapSocket; + handle->fd = pcapSocket; + + handle->activate_op = pcap_activate_haiku; + + return handle; +} + +static int +can_be_bound(const char *name _U_) +{ + return 1; +} + +static int +get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) +{ + /* TODO */ + if (*flags & PCAP_IF_LOOPBACK) { + /* + * Loopback devices aren't wireless, and "connected"/ + * "disconnected" doesn't apply to them. + */ + *flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE; + return (0); + } + return (0); +} + +int +pcap_platform_finddevs(pcap_if_list_t* _allDevices, char* errorBuffer) +{ + return pcap_findalldevs_interfaces(_allDevices, errorBuffer, can_be_bound, + get_if_flags); +} + +/* + * Libpcap version string. + */ +const char * +pcap_lib_version(void) +{ + return (PCAP_VERSION_STRING); +} |