| /* |
| * 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 <config.h> |
| #endif |
| |
| #include "pcap-int.h" |
| |
| #include <airpcap.h> |
| |
| #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. |
| * |
| * This assumes that a single buffer of packets will have |
| * <= INT_MAX packets, so the packet count doesn't overflow. |
| */ |
| #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); |
| } |