/* * Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy) * Copyright (c) 2005 - 2010 CACE Technologies, Davis (California) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the Politecnico di Torino, CACE Technologies * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include #endif #include "pcap-int.h" #include #include "pcap-airpcap.h" /* Default size of the buffer we allocate in userland. */ #define AIRPCAP_DEFAULT_USER_BUFFER_SIZE 256000 /* Default size of the buffer for the AirPcap adapter. */ #define AIRPCAP_DEFAULT_KERNEL_BUFFER_SIZE 1000000 // // We load the AirPcap DLL dynamically, so that the code will // work whether you have it installed or not, and there don't // have to be two different versions of the library, one linked // to the AirPcap library and one not linked to it. // static pcap_code_handle_t airpcap_lib; typedef PCHAR (*AirpcapGetLastErrorHandler)(PAirpcapHandle); typedef BOOL (*AirpcapGetDeviceListHandler)(PAirpcapDeviceDescription *, PCHAR); typedef VOID (*AirpcapFreeDeviceListHandler)(PAirpcapDeviceDescription); typedef PAirpcapHandle (*AirpcapOpenHandler)(PCHAR, PCHAR); typedef VOID (*AirpcapCloseHandler)(PAirpcapHandle); typedef BOOL (*AirpcapSetDeviceMacFlagsHandler)(PAirpcapHandle, UINT); typedef BOOL (*AirpcapSetLinkTypeHandler)(PAirpcapHandle, AirpcapLinkType); typedef BOOL (*AirpcapGetLinkTypeHandler)(PAirpcapHandle, PAirpcapLinkType); typedef BOOL (*AirpcapSetKernelBufferHandler)(PAirpcapHandle, UINT); typedef BOOL (*AirpcapSetFilterHandler)(PAirpcapHandle, PVOID, UINT); typedef BOOL (*AirpcapSetMinToCopyHandler)(PAirpcapHandle, UINT); typedef BOOL (*AirpcapGetReadEventHandler)(PAirpcapHandle, HANDLE *); typedef BOOL (*AirpcapReadHandler)(PAirpcapHandle, PBYTE, UINT, PUINT); typedef BOOL (*AirpcapWriteHandler)(PAirpcapHandle, PCHAR, ULONG); typedef BOOL (*AirpcapGetStatsHandler)(PAirpcapHandle, PAirpcapStats); static AirpcapGetLastErrorHandler p_AirpcapGetLastError; static AirpcapGetDeviceListHandler p_AirpcapGetDeviceList; static AirpcapFreeDeviceListHandler p_AirpcapFreeDeviceList; static AirpcapOpenHandler p_AirpcapOpen; static AirpcapCloseHandler p_AirpcapClose; static AirpcapSetDeviceMacFlagsHandler p_AirpcapSetDeviceMacFlags; static AirpcapSetLinkTypeHandler p_AirpcapSetLinkType; static AirpcapGetLinkTypeHandler p_AirpcapGetLinkType; static AirpcapSetKernelBufferHandler p_AirpcapSetKernelBuffer; static AirpcapSetFilterHandler p_AirpcapSetFilter; static AirpcapSetMinToCopyHandler p_AirpcapSetMinToCopy; static AirpcapGetReadEventHandler p_AirpcapGetReadEvent; static AirpcapReadHandler p_AirpcapRead; static AirpcapWriteHandler p_AirpcapWrite; static AirpcapGetStatsHandler p_AirpcapGetStats; typedef enum LONG { AIRPCAP_API_UNLOADED = 0, AIRPCAP_API_LOADED, AIRPCAP_API_CANNOT_LOAD, AIRPCAP_API_LOADING } AIRPCAP_API_LOAD_STATUS; static AIRPCAP_API_LOAD_STATUS airpcap_load_status; /* * NOTE: this function should be called by the pcap functions that can * theoretically deal with the AirPcap library for the first time, * namely listing the adapters and creating a pcap_t for an adapter. * All the other ones (activate, close, read, write, set parameters) * work on a pcap_t for an AirPcap device, meaning we've already * created the pcap_t and thus have loaded the functions, so we do * not need to call this function. */ static AIRPCAP_API_LOAD_STATUS load_airpcap_functions(void) { AIRPCAP_API_LOAD_STATUS current_status; /* * We don't use a mutex because there's no place that * we can guarantee we'll be called before any threads * other than the main thread exists. (For example, * this might be a static library, so we can't arrange * to be called by DllMain(), and there's no guarantee * that the application called pcap_init() - which is * supposed to be called only from one thread - so * we can't arrange to be called from it.) * * If nobody's tried to load it yet, mark it as * loading; in any case, return the status before * we modified it. */ current_status = InterlockedCompareExchange((LONG *)&airpcap_load_status, AIRPCAP_API_LOADING, AIRPCAP_API_UNLOADED); /* * If the status was AIRPCAP_API_UNLOADED, we've set it * to AIRPCAP_API_LOADING, because we're going to be * the ones to load the library but current_status is * AIRPCAP_API_UNLOADED. * * if it was AIRPCAP_API_LOADING, meaning somebody else * was trying to load it, spin until they finish and * set the status to a value reflecting whether they * succeeded. */ while (current_status == AIRPCAP_API_LOADING) { current_status = InterlockedCompareExchange((LONG*)&airpcap_load_status, AIRPCAP_API_LOADING, AIRPCAP_API_LOADING); Sleep(10); } /* * At this point, current_status is either: * * AIRPCAP_API_LOADED, in which case another thread * loaded the library, so we're done; * * AIRPCAP_API_CANNOT_LOAD, in which another thread * tried and failed to load the library, so we're * done - we won't try it ourselves; * * AIRPCAP_API_LOADING, in which case *we're* the * ones loading it, and should now try to do so. */ if (current_status == AIRPCAP_API_LOADED) return AIRPCAP_API_LOADED; if (current_status == AIRPCAP_API_CANNOT_LOAD) return AIRPCAP_API_CANNOT_LOAD; /* * Start out assuming we can't load it. */ current_status = AIRPCAP_API_CANNOT_LOAD; airpcap_lib = pcap_load_code("airpcap.dll"); if (airpcap_lib != NULL) { /* * OK, we've loaded the library; now try to find the * functions we need in it. */ p_AirpcapGetLastError = (AirpcapGetLastErrorHandler) pcap_find_function(airpcap_lib, "AirpcapGetLastError"); p_AirpcapGetDeviceList = (AirpcapGetDeviceListHandler) pcap_find_function(airpcap_lib, "AirpcapGetDeviceList"); p_AirpcapFreeDeviceList = (AirpcapFreeDeviceListHandler) pcap_find_function(airpcap_lib, "AirpcapFreeDeviceList"); p_AirpcapOpen = (AirpcapOpenHandler) pcap_find_function(airpcap_lib, "AirpcapOpen"); p_AirpcapClose = (AirpcapCloseHandler) pcap_find_function(airpcap_lib, "AirpcapClose"); p_AirpcapSetDeviceMacFlags = (AirpcapSetDeviceMacFlagsHandler) pcap_find_function(airpcap_lib, "AirpcapSetDeviceMacFlags"); p_AirpcapSetLinkType = (AirpcapSetLinkTypeHandler) pcap_find_function(airpcap_lib, "AirpcapSetLinkType"); p_AirpcapGetLinkType = (AirpcapGetLinkTypeHandler) pcap_find_function(airpcap_lib, "AirpcapGetLinkType"); p_AirpcapSetKernelBuffer = (AirpcapSetKernelBufferHandler) pcap_find_function(airpcap_lib, "AirpcapSetKernelBuffer"); p_AirpcapSetFilter = (AirpcapSetFilterHandler) pcap_find_function(airpcap_lib, "AirpcapSetFilter"); p_AirpcapSetMinToCopy = (AirpcapSetMinToCopyHandler) pcap_find_function(airpcap_lib, "AirpcapSetMinToCopy"); p_AirpcapGetReadEvent = (AirpcapGetReadEventHandler) pcap_find_function(airpcap_lib, "AirpcapGetReadEvent"); p_AirpcapRead = (AirpcapReadHandler) pcap_find_function(airpcap_lib, "AirpcapRead"); p_AirpcapWrite = (AirpcapWriteHandler) pcap_find_function(airpcap_lib, "AirpcapWrite"); p_AirpcapGetStats = (AirpcapGetStatsHandler) pcap_find_function(airpcap_lib, "AirpcapGetStats"); // // Make sure that we found everything // if (p_AirpcapGetLastError != NULL && p_AirpcapGetDeviceList != NULL && p_AirpcapFreeDeviceList != NULL && p_AirpcapOpen != NULL && p_AirpcapClose != NULL && p_AirpcapSetDeviceMacFlags != NULL && p_AirpcapSetLinkType != NULL && p_AirpcapGetLinkType != NULL && p_AirpcapSetKernelBuffer != NULL && p_AirpcapSetFilter != NULL && p_AirpcapSetMinToCopy != NULL && p_AirpcapGetReadEvent != NULL && p_AirpcapRead != NULL && p_AirpcapWrite != NULL && p_AirpcapGetStats != NULL) { /* * We have all we need. */ current_status = AIRPCAP_API_LOADED; } } if (current_status != AIRPCAP_API_LOADED) { /* * We failed; if we found the DLL, close the * handle for it. */ if (airpcap_lib != NULL) { FreeLibrary(airpcap_lib); airpcap_lib = NULL; } } /* * Now set the status appropriately - and atomically. */ InterlockedExchange((LONG *)&airpcap_load_status, current_status); return current_status; } /* * Private data for capturing on AirPcap devices. */ struct pcap_airpcap { PAirpcapHandle adapter; int filtering_in_kernel; int nonblock; int read_timeout; HANDLE read_event; struct pcap_stat stat; }; static int airpcap_setfilter(pcap_t *p, struct bpf_program *fp) { struct pcap_airpcap *pa = p->priv; if (!p_AirpcapSetFilter(pa->adapter, fp->bf_insns, fp->bf_len * sizeof(struct bpf_insn))) { /* * Kernel filter not installed. * * XXX - we don't know whether this failed because: * * the kernel rejected the filter program as invalid, * in which case we should fall back on userland * filtering; * * the kernel rejected the filter program as too big, * in which case we should again fall back on * userland filtering; * * there was some other problem, in which case we * should probably report an error; * * So we just fall back on userland filtering in * all cases. */ /* * install_bpf_program() validates the program. * * XXX - what if we already have a filter in the kernel? */ if (install_bpf_program(p, fp) < 0) return (-1); pa->filtering_in_kernel = 0; /* filtering in userland */ return (0); } /* * It worked. */ pa->filtering_in_kernel = 1; /* filtering in the kernel */ /* * Discard any previously-received packets, as they might have * passed whatever filter was formerly in effect, but might * not pass this filter (BIOCSETF discards packets buffered * in the kernel, so you can lose packets in any case). */ p->cc = 0; return (0); } static int airpcap_set_datalink(pcap_t *p, int dlt) { struct pcap_airpcap *pa = p->priv; AirpcapLinkType type; switch (dlt) { case DLT_IEEE802_11_RADIO: type = AIRPCAP_LT_802_11_PLUS_RADIO; break; case DLT_PPI: type = AIRPCAP_LT_802_11_PLUS_PPI; break; case DLT_IEEE802_11: type = AIRPCAP_LT_802_11; break; default: /* This can't happen; just return. */ return (0); } if (!p_AirpcapSetLinkType(pa->adapter, type)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirpcapSetLinkType() failed: %s", p_AirpcapGetLastError(pa->adapter)); return (-1); } p->linktype = dlt; return (0); } static int airpcap_getnonblock(pcap_t *p) { struct pcap_airpcap *pa = p->priv; return (pa->nonblock); } static int airpcap_setnonblock(pcap_t *p, int nonblock) { struct pcap_airpcap *pa = p->priv; int newtimeout; if (nonblock) { /* * Set the packet buffer timeout to -1 for non-blocking * mode. */ newtimeout = -1; } else { /* * Restore the timeout set when the device was opened. * (Note that this may be -1, in which case we're not * really leaving non-blocking mode. However, although * the timeout argument to pcap_set_timeout() and * pcap_open_live() is an int, you're not supposed to * supply a negative value, so that "shouldn't happen".) */ newtimeout = p->opt.timeout; } pa->read_timeout = newtimeout; pa->nonblock = (newtimeout == -1); return (0); } static int airpcap_stats(pcap_t *p, struct pcap_stat *ps) { struct pcap_airpcap *pa = p->priv; AirpcapStats tas; /* * Try to get statistics. */ if (!p_AirpcapGetStats(pa->adapter, &tas)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirpcapGetStats() failed: %s", p_AirpcapGetLastError(pa->adapter)); return (-1); } ps->ps_drop = tas.Drops; ps->ps_recv = tas.Recvs; ps->ps_ifdrop = tas.IfDrops; return (0); } /* * Win32-only routine for getting statistics. * * This way is definitely safer than passing the pcap_stat * from the userland. * In fact, there could happen than the user allocates a variable which is not * big enough for the new structure, and the library will write in a zone * which is not allocated to this variable. * * In this way, we're pretty sure we are writing on memory allocated to this * variable. * * XXX - but this is the wrong way to handle statistics. Instead, we should * have an API that returns data in a form like the Options section of a * pcapng Interface Statistics Block: * * https://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.6 * * which would let us add new statistics straightforwardly and indicate which * statistics we are and are *not* providing, rather than having to provide * possibly-bogus values for statistics we can't provide. */ static struct pcap_stat * airpcap_stats_ex(pcap_t *p, int *pcap_stat_size) { struct pcap_airpcap *pa = p->priv; AirpcapStats tas; *pcap_stat_size = sizeof (p->stat); /* * Try to get statistics. */ if (!p_AirpcapGetStats(pa->adapter, &tas)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirpcapGetStats() failed: %s", p_AirpcapGetLastError(pa->adapter)); return (NULL); } p->stat.ps_recv = tas.Recvs; p->stat.ps_drop = tas.Drops; p->stat.ps_ifdrop = tas.IfDrops; /* * Just in case this is ever compiled for a target other than * Windows, which is extremely unlikely at best. */ #ifdef _WIN32 p->stat.ps_capt = tas.Capt; #endif return (&p->stat); } /* Set the dimension of the kernel-level capture buffer */ static int airpcap_setbuff(pcap_t *p, int dim) { struct pcap_airpcap *pa = p->priv; if (!p_AirpcapSetKernelBuffer(pa->adapter, dim)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirpcapSetKernelBuffer() failed: %s", p_AirpcapGetLastError(pa->adapter)); return (-1); } return (0); } /* Set the driver working mode */ static int airpcap_setmode(pcap_t *p, int mode) { if (mode != MODE_CAPT) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Only MODE_CAPT is supported on an AirPcap adapter"); return (-1); } return (0); } /*set the minimum amount of data that will release a read call*/ static int airpcap_setmintocopy(pcap_t *p, int size) { struct pcap_airpcap *pa = p->priv; if (!p_AirpcapSetMinToCopy(pa->adapter, size)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirpcapSetMinToCopy() failed: %s", p_AirpcapGetLastError(pa->adapter)); return (-1); } return (0); } static HANDLE airpcap_getevent(pcap_t *p) { struct pcap_airpcap *pa = p->priv; return (pa->read_event); } static int airpcap_oid_get_request(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_, size_t *lenp _U_) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Getting OID values is not supported on an AirPcap adapter"); return (PCAP_ERROR); } static int airpcap_oid_set_request(pcap_t *p, bpf_u_int32 oid _U_, const void *data _U_, size_t *lenp _U_) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Setting OID values is not supported on an AirPcap adapter"); return (PCAP_ERROR); } static u_int airpcap_sendqueue_transmit(pcap_t *p, pcap_send_queue *queue _U_, int sync _U_) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Cannot queue packets for transmission on an AirPcap adapter"); return (0); } static int airpcap_setuserbuffer(pcap_t *p, int size) { unsigned char *new_buff; if (size <= 0) { /* Bogus parameter */ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error: invalid size %d",size); return (-1); } /* Allocate the buffer */ new_buff = (unsigned char *)malloc(sizeof(char)*size); if (!new_buff) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error: not enough memory"); return (-1); } free(p->buffer); p->buffer = new_buff; p->bufsize = size; return (0); } static int airpcap_live_dump(pcap_t *p, char *filename _U_, int maxsize _U_, int maxpacks _U_) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirPcap adapters don't support live dump"); return (-1); } static int airpcap_live_dump_ended(pcap_t *p, int sync _U_) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirPcap adapters don't support live dump"); return (-1); } static PAirpcapHandle airpcap_get_airpcap_handle(pcap_t *p) { struct pcap_airpcap *pa = p->priv; return (pa->adapter); } static int airpcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) { struct pcap_airpcap *pa = p->priv; int cc; int n; register u_char *bp, *ep; UINT bytes_read; u_char *datap; cc = p->cc; if (cc == 0) { /* * Has "pcap_breakloop()" been called? */ if (p->break_loop) { /* * Yes - clear the flag that indicates that it * has, and return PCAP_ERROR_BREAK to indicate * that we were told to break out of the loop. */ p->break_loop = 0; return (PCAP_ERROR_BREAK); } // // If we're not in non-blocking mode, wait for data to // arrive. // if (pa->read_timeout != -1) { WaitForSingleObject(pa->read_event, (pa->read_timeout ==0 )? INFINITE: pa->read_timeout); } // // Read the data. // p_AirpcapRead doesn't block. // if (!p_AirpcapRead(pa->adapter, (PBYTE)p->buffer, p->bufsize, &bytes_read)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirpcapRead() failed: %s", p_AirpcapGetLastError(pa->adapter)); return (-1); } cc = bytes_read; bp = (u_char *)p->buffer; } else bp = p->bp; /* * Loop through each packet. */ #define bhp ((AirpcapBpfHeader *)bp) n = 0; ep = bp + cc; for (;;) { register u_int caplen, hdrlen; /* * Has "pcap_breakloop()" been called? * If so, return immediately - if we haven't read any * packets, clear the flag and return PCAP_ERROR_BREAK * to indicate that we were told to break out of the loop, * otherwise leave the flag set, so that the *next* call * will break out of the loop without having read any * packets, and return the number of packets we've * processed so far. */ if (p->break_loop) { if (n == 0) { p->break_loop = 0; return (PCAP_ERROR_BREAK); } else { p->bp = bp; p->cc = (int) (ep - bp); return (n); } } if (bp >= ep) break; caplen = bhp->Caplen; hdrlen = bhp->Hdrlen; datap = bp + hdrlen; /* * Short-circuit evaluation: if using BPF filter * in the AirPcap adapter, no need to do it now - * we already know the packet passed the filter. */ if (pa->filtering_in_kernel || p->fcode.bf_insns == NULL || pcap_filter(p->fcode.bf_insns, datap, bhp->Originallen, caplen)) { struct pcap_pkthdr pkthdr; pkthdr.ts.tv_sec = bhp->TsSec; pkthdr.ts.tv_usec = bhp->TsUsec; pkthdr.caplen = caplen; pkthdr.len = bhp->Originallen; (*callback)(user, &pkthdr, datap); bp += AIRPCAP_WORDALIGN(caplen + hdrlen); if (++n >= cnt && !PACKET_COUNT_IS_UNLIMITED(cnt)) { p->bp = bp; p->cc = (int)(ep - bp); return (n); } } else { /* * Skip this packet. */ bp += AIRPCAP_WORDALIGN(caplen + hdrlen); } } #undef bhp p->cc = 0; return (n); } static int airpcap_inject(pcap_t *p, const void *buf, int size) { struct pcap_airpcap *pa = p->priv; /* * XXX - the second argument to AirpcapWrite() *should* have * been declared as a const pointer - a write function that * stomps on what it writes is *extremely* rude - but such * is life. We assume it is, in fact, not going to write on * our buffer. */ if (!p_AirpcapWrite(pa->adapter, (void *)buf, size)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirpcapWrite() failed: %s", p_AirpcapGetLastError(pa->adapter)); return (-1); } /* * We assume it all got sent if "AirpcapWrite()" succeeded. * "pcap_inject()" is expected to return the number of bytes * sent. */ return (size); } static void airpcap_cleanup(pcap_t *p) { struct pcap_airpcap *pa = p->priv; if (pa->adapter != NULL) { p_AirpcapClose(pa->adapter); pa->adapter = NULL; } pcap_cleanup_live_common(p); } static void airpcap_breakloop(pcap_t *p) { HANDLE read_event; pcap_breakloop_common(p); struct pcap_airpcap *pa = p->priv; /* XXX - what if either of these fail? */ /* * XXX - will SetEvent() force a wakeup and, if so, will * the AirPcap read code handle that sanely? */ if (!p_AirpcapGetReadEvent(pa->adapter, &read_event)) return; SetEvent(read_event); } static int airpcap_activate(pcap_t *p) { struct pcap_airpcap *pa = p->priv; char *device = p->opt.device; char airpcap_errbuf[AIRPCAP_ERRBUF_SIZE]; BOOL status; AirpcapLinkType link_type; pa->adapter = p_AirpcapOpen(device, airpcap_errbuf); if (pa->adapter == NULL) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s", airpcap_errbuf); return (PCAP_ERROR); } /* * Set monitor mode appropriately. * Always turn off the "ACK frames sent to the card" mode. */ if (p->opt.rfmon) { status = p_AirpcapSetDeviceMacFlags(pa->adapter, AIRPCAP_MF_MONITOR_MODE_ON); } else status = p_AirpcapSetDeviceMacFlags(pa->adapter, AIRPCAP_MF_ACK_FRAMES_ON); if (!status) { p_AirpcapClose(pa->adapter); snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirpcapSetDeviceMacFlags() failed: %s", p_AirpcapGetLastError(pa->adapter)); return (PCAP_ERROR); } /* * 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 (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN) p->snapshot = MAXIMUM_SNAPLEN; /* * If the buffer size wasn't explicitly set, default to * AIRPCAP_DEFAULT_KERNEL_BUFFER_SIZE. */ if (p->opt.buffer_size == 0) p->opt.buffer_size = AIRPCAP_DEFAULT_KERNEL_BUFFER_SIZE; if (!p_AirpcapSetKernelBuffer(pa->adapter, p->opt.buffer_size)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirpcapSetKernelBuffer() failed: %s", p_AirpcapGetLastError(pa->adapter)); goto bad; } if(!p_AirpcapGetReadEvent(pa->adapter, &pa->read_event)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirpcapGetReadEvent() failed: %s", p_AirpcapGetLastError(pa->adapter)); goto bad; } /* Set the buffer size */ p->bufsize = AIRPCAP_DEFAULT_USER_BUFFER_SIZE; p->buffer = malloc(p->bufsize); if (p->buffer == NULL) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "malloc"); goto bad; } if (p->opt.immediate) { /* Tell the driver to copy the buffer as soon as data arrives. */ if (!p_AirpcapSetMinToCopy(pa->adapter, 0)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirpcapSetMinToCopy() failed: %s", p_AirpcapGetLastError(pa->adapter)); goto bad; } } else { /* * Tell the driver to copy the buffer only if it contains * at least 16K. */ if (!p_AirpcapSetMinToCopy(pa->adapter, 16000)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirpcapSetMinToCopy() failed: %s", p_AirpcapGetLastError(pa->adapter)); goto bad; } } /* * Find out what the default link-layer header type is, * and set p->datalink to that. * * We don't force it to another value because there * might be some programs using WinPcap/Npcap that, * when capturing on AirPcap devices, assume the * default value set with the AirPcap configuration * program is what you get. * * The out-of-the-box default appears to be radiotap. */ if (!p_AirpcapGetLinkType(pa->adapter, &link_type)) { /* That failed. */ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirpcapGetLinkType() failed: %s", p_AirpcapGetLastError(pa->adapter)); goto bad; } switch (link_type) { case AIRPCAP_LT_802_11_PLUS_RADIO: p->linktype = DLT_IEEE802_11_RADIO; break; case AIRPCAP_LT_802_11_PLUS_PPI: p->linktype = DLT_PPI; break; case AIRPCAP_LT_802_11: p->linktype = DLT_IEEE802_11; break; case AIRPCAP_LT_UNKNOWN: default: /* OK, what? */ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "AirpcapGetLinkType() returned unknown link type %u", link_type); goto bad; } /* * Now provide a list of all the supported types; we * assume they all work. We put radiotap at the top, * followed by PPI, followed by "no radio metadata". */ p->dlt_list = (u_int *) malloc(sizeof(u_int) * 3); if (p->dlt_list == NULL) goto bad; p->dlt_list[0] = DLT_IEEE802_11_RADIO; p->dlt_list[1] = DLT_PPI; p->dlt_list[2] = DLT_IEEE802_11; p->dlt_count = 3; p->read_op = airpcap_read; p->inject_op = airpcap_inject; p->setfilter_op = airpcap_setfilter; p->setdirection_op = NULL; /* Not implemented. */ p->set_datalink_op = airpcap_set_datalink; p->getnonblock_op = airpcap_getnonblock; p->setnonblock_op = airpcap_setnonblock; p->breakloop_op = airpcap_breakloop; p->stats_op = airpcap_stats; p->stats_ex_op = airpcap_stats_ex; p->setbuff_op = airpcap_setbuff; p->setmode_op = airpcap_setmode; p->setmintocopy_op = airpcap_setmintocopy; p->getevent_op = airpcap_getevent; p->oid_get_request_op = airpcap_oid_get_request; p->oid_set_request_op = airpcap_oid_set_request; p->sendqueue_transmit_op = airpcap_sendqueue_transmit; p->setuserbuffer_op = airpcap_setuserbuffer; p->live_dump_op = airpcap_live_dump; p->live_dump_ended_op = airpcap_live_dump_ended; p->get_airpcap_handle_op = airpcap_get_airpcap_handle; p->cleanup_op = airpcap_cleanup; return (0); bad: airpcap_cleanup(p); return (PCAP_ERROR); } /* * Monitor mode is supported. */ static int airpcap_can_set_rfmon(pcap_t *p) { return (1); } int device_is_airpcap(const char *device, char *ebuf) { static const char airpcap_prefix[] = "\\\\.\\airpcap"; /* * We don't determine this by calling AirpcapGetDeviceList() * and looking at the list, as that appears to be a costly * operation. * * Instead, we just check whether it begins with "\\.\airpcap". */ if (strncmp(device, airpcap_prefix, sizeof airpcap_prefix - 1) == 0) { /* * Yes, it's an AirPcap device. */ return (1); } /* * No, it's not an AirPcap device. */ return (0); } pcap_t * airpcap_create(const char *device, char *ebuf, int *is_ours) { int ret; pcap_t *p; /* * This can be called before we've tried loading the library, * so do so if we haven't already tried to do so. */ if (load_airpcap_functions() != AIRPCAP_API_LOADED) { /* * We assume this means that we don't have the AirPcap * software installed, which probably means we don't * have an AirPcap device. * * Don't treat that as an error. */ *is_ours = 0; return (NULL); } /* * Is this an AirPcap device? */ ret = device_is_airpcap(device, ebuf); if (ret == 0) { /* No. */ *is_ours = 0; return (NULL); } /* * Yes. */ *is_ours = 1; p = PCAP_CREATE_COMMON(ebuf, struct pcap_airpcap); if (p == NULL) return (NULL); p->activate_op = airpcap_activate; p->can_set_rfmon_op = airpcap_can_set_rfmon; return (p); } /* * Add all AirPcap devices. */ int airpcap_findalldevs(pcap_if_list_t *devlistp, char *errbuf) { AirpcapDeviceDescription *airpcap_devices, *airpcap_device; char airpcap_errbuf[AIRPCAP_ERRBUF_SIZE]; /* * This can be called before we've tried loading the library, * so do so if we haven't already tried to do so. */ if (load_airpcap_functions() != AIRPCAP_API_LOADED) { /* * XXX - unless the error is "no such DLL", report this * as an error rather than as "no AirPcap devices"? */ return (0); } if (!p_AirpcapGetDeviceList(&airpcap_devices, airpcap_errbuf)) { snprintf(errbuf, PCAP_ERRBUF_SIZE, "AirpcapGetDeviceList() failed: %s", airpcap_errbuf); return (-1); } for (airpcap_device = airpcap_devices; airpcap_device != NULL; airpcap_device = airpcap_device->next) { if (add_dev(devlistp, airpcap_device->Name, 0, airpcap_device->Description, errbuf) == NULL) { /* * Failure. */ p_AirpcapFreeDeviceList(airpcap_devices); return (-1); } } p_AirpcapFreeDeviceList(airpcap_devices); return (0); }