/* * 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 #include #include #include #include #include #include #include #include #include /* * 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 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 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; ifreq request; int socket = ::socket(AF_INET, SOCK_DGRAM, 0); if (socket < 0) { return -1; } prepare_request(request, handlep->device); if (ioctl(socket, SIOCGIFSTATS, &request, sizeof(struct ifreq)) < 0) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "pcap_stats: %s", strerror(errno)); close(socket); return -1; } close(socket); 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 extern "C" 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 socket = ::socket(AF_INET, SOCK_DGRAM, 0); if (socket < 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(socket); return NULL; } // check if the interface exist if (ioctl(socket, SIOCGIFINDEX, &request, sizeof(request)) < 0) { snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "Interface \"%s\" does not exist.\n", device); close(socket); return NULL; } close(socket); // no longer needed after this point // get link level interface for this interface socket = ::socket(AF_LINK, SOCK_DGRAM, 0); if (socket < 0) { snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "No link level: %s\n", strerror(errno)); return NULL; } // start monitoring if (ioctl(socket, SIOCSPACKETCAP, &request, sizeof(struct ifreq)) < 0) { snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "Cannot start monitoring: %s\n", strerror(errno)); close(socket); return NULL; } struct wrapper_struct { pcap_t __common; struct pcap_haiku __private; }; pcap_t* handle = pcap_create_common(errorBuffer, sizeof (struct wrapper_struct), offsetof (struct wrapper_struct, __private)); if (handle == NULL) { snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "malloc: %s", strerror(errno)); close(socket); return NULL; } handle->selectable_fd = socket; handle->fd = socket; 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); } extern "C" 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. */ extern "C" const char * pcap_lib_version(void) { return (PCAP_VERSION_STRING); }