diff options
Diffstat (limited to 'pcap-tc.c')
-rw-r--r-- | pcap-tc.c | 1292 |
1 files changed, 1292 insertions, 0 deletions
diff --git a/pcap-tc.c b/pcap-tc.c new file mode 100644 index 0000000..65fb0e2 --- /dev/null +++ b/pcap-tc.c @@ -0,0 +1,1292 @@ +/* + * Copyright (c) 2008 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 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.h> +#include <pcap-int.h> + +#include "pcap-tc.h" + +#include <malloc.h> +#include <memory.h> +#include <string.h> +#include <errno.h> + +#ifdef _WIN32 +#include <tchar.h> +#endif + +typedef TC_STATUS (TC_CALLCONV *TcFcnQueryPortList) (PTC_PORT *ppPorts, PULONG pLength); +typedef TC_STATUS (TC_CALLCONV *TcFcnFreePortList) (TC_PORT *pPorts); + +typedef PCHAR (TC_CALLCONV *TcFcnStatusGetString) (TC_STATUS status); + +typedef PCHAR (TC_CALLCONV *TcFcnPortGetName) (TC_PORT port); +typedef PCHAR (TC_CALLCONV *TcFcnPortGetDescription) (TC_PORT port); + +typedef TC_STATUS (TC_CALLCONV *TcFcnInstanceOpenByName) (PCHAR name, PTC_INSTANCE pInstance); +typedef TC_STATUS (TC_CALLCONV *TcFcnInstanceClose) (TC_INSTANCE instance); +typedef TC_STATUS (TC_CALLCONV *TcFcnInstanceSetFeature) (TC_INSTANCE instance, ULONG feature, ULONG value); +typedef TC_STATUS (TC_CALLCONV *TcFcnInstanceQueryFeature) (TC_INSTANCE instance, ULONG feature, PULONG pValue); +typedef TC_STATUS (TC_CALLCONV *TcFcnInstanceReceivePackets) (TC_INSTANCE instance, PTC_PACKETS_BUFFER pBuffer); +typedef HANDLE (TC_CALLCONV *TcFcnInstanceGetReceiveWaitHandle) (TC_INSTANCE instance); +typedef TC_STATUS (TC_CALLCONV *TcFcnInstanceTransmitPackets) (TC_INSTANCE instance, TC_PACKETS_BUFFER pBuffer); +typedef TC_STATUS (TC_CALLCONV *TcFcnInstanceQueryStatistics) (TC_INSTANCE instance, PTC_STATISTICS pStatistics); + +typedef TC_STATUS (TC_CALLCONV *TcFcnPacketsBufferCreate) (ULONG size, PTC_PACKETS_BUFFER pBuffer); +typedef VOID (TC_CALLCONV *TcFcnPacketsBufferDestroy) (TC_PACKETS_BUFFER buffer); +typedef TC_STATUS (TC_CALLCONV *TcFcnPacketsBufferQueryNextPacket)(TC_PACKETS_BUFFER buffer, PTC_PACKET_HEADER pHeader, PVOID *ppData); +typedef TC_STATUS (TC_CALLCONV *TcFcnPacketsBufferCommitNextPacket)(TC_PACKETS_BUFFER buffer, PTC_PACKET_HEADER pHeader, PVOID pData); + +typedef VOID (TC_CALLCONV *TcFcnStatisticsDestroy) (TC_STATISTICS statistics); +typedef TC_STATUS (TC_CALLCONV *TcFcnStatisticsUpdate) (TC_STATISTICS statistics); +typedef TC_STATUS (TC_CALLCONV *TcFcnStatisticsQueryValue) (TC_STATISTICS statistics, ULONG counterId, PULONGLONG pValue); + +typedef enum LONG +{ + TC_API_UNLOADED = 0, + TC_API_LOADED, + TC_API_CANNOT_LOAD, + TC_API_LOADING +} + TC_API_LOAD_STATUS; + + +typedef struct _TC_FUNCTIONS +{ + TC_API_LOAD_STATUS LoadStatus; +#ifdef _WIN32 + HMODULE hTcApiDllHandle; +#endif + TcFcnQueryPortList QueryPortList; + TcFcnFreePortList FreePortList; + TcFcnStatusGetString StatusGetString; + + TcFcnPortGetName PortGetName; + TcFcnPortGetDescription PortGetDescription; + + TcFcnInstanceOpenByName InstanceOpenByName; + TcFcnInstanceClose InstanceClose; + TcFcnInstanceSetFeature InstanceSetFeature; + TcFcnInstanceQueryFeature InstanceQueryFeature; + TcFcnInstanceReceivePackets InstanceReceivePackets; +#ifdef _WIN32 + TcFcnInstanceGetReceiveWaitHandle InstanceGetReceiveWaitHandle; +#endif + TcFcnInstanceTransmitPackets InstanceTransmitPackets; + TcFcnInstanceQueryStatistics InstanceQueryStatistics; + + TcFcnPacketsBufferCreate PacketsBufferCreate; + TcFcnPacketsBufferDestroy PacketsBufferDestroy; + TcFcnPacketsBufferQueryNextPacket PacketsBufferQueryNextPacket; + TcFcnPacketsBufferCommitNextPacket PacketsBufferCommitNextPacket; + + TcFcnStatisticsDestroy StatisticsDestroy; + TcFcnStatisticsUpdate StatisticsUpdate; + TcFcnStatisticsQueryValue StatisticsQueryValue; +} + TC_FUNCTIONS; + +static pcap_if_t* TcCreatePcapIfFromPort(TC_PORT port); +static int TcSetDatalink(pcap_t *p, int dlt); +static int TcGetNonBlock(pcap_t *p); +static int TcSetNonBlock(pcap_t *p, int nonblock); +static void TcCleanup(pcap_t *p); +static int TcInject(pcap_t *p, const void *buf, int size); +static int TcRead(pcap_t *p, int cnt, pcap_handler callback, u_char *user); +static int TcStats(pcap_t *p, struct pcap_stat *ps); +static int TcSetFilter(pcap_t *p, struct bpf_program *fp); +#ifdef _WIN32 +static struct pcap_stat *TcStatsEx(pcap_t *p, int *pcap_stat_size); +static int TcSetBuff(pcap_t *p, int dim); +static int TcSetMode(pcap_t *p, int mode); +static int TcSetMinToCopy(pcap_t *p, int size); +static HANDLE TcGetReceiveWaitHandle(pcap_t *p); +static int TcOidGetRequest(pcap_t *p, bpf_u_int32 oid, void *data, size_t *lenp); +static int TcOidSetRequest(pcap_t *p, bpf_u_int32 oid, const void *data, size_t *lenp); +static u_int TcSendqueueTransmit(pcap_t *p, pcap_send_queue *queue, int sync); +static int TcSetUserBuffer(pcap_t *p, int size); +static int TcLiveDump(pcap_t *p, char *filename, int maxsize, int maxpacks); +static int TcLiveDumpEnded(pcap_t *p, int sync); +static PAirpcapHandle TcGetAirPcapHandle(pcap_t *p); +#endif + +#ifdef _WIN32 +TC_FUNCTIONS g_TcFunctions = +{ + TC_API_UNLOADED, /* LoadStatus */ + NULL, /* hTcApiDllHandle */ + NULL, /* QueryPortList */ + NULL, /* FreePortList */ + NULL, /* StatusGetString */ + NULL, /* PortGetName */ + NULL, /* PortGetDescription */ + NULL, /* InstanceOpenByName */ + NULL, /* InstanceClose */ + NULL, /* InstanceSetFeature */ + NULL, /* InstanceQueryFeature */ + NULL, /* InstanceReceivePackets */ + NULL, /* InstanceGetReceiveWaitHandle */ + NULL, /* InstanceTransmitPackets */ + NULL, /* InstanceQueryStatistics */ + NULL, /* PacketsBufferCreate */ + NULL, /* PacketsBufferDestroy */ + NULL, /* PacketsBufferQueryNextPacket */ + NULL, /* PacketsBufferCommitNextPacket */ + NULL, /* StatisticsDestroy */ + NULL, /* StatisticsUpdate */ + NULL /* StatisticsQueryValue */ +}; +#else +TC_FUNCTIONS g_TcFunctions = +{ + TC_API_LOADED, /* LoadStatus */ + TcQueryPortList, + TcFreePortList, + TcStatusGetString, + TcPortGetName, + TcPortGetDescription, + TcInstanceOpenByName, + TcInstanceClose, + TcInstanceSetFeature, + TcInstanceQueryFeature, + TcInstanceReceivePackets, +#ifdef _WIN32 + TcInstanceGetReceiveWaitHandle, +#endif + TcInstanceTransmitPackets, + TcInstanceQueryStatistics, + TcPacketsBufferCreate, + TcPacketsBufferDestroy, + TcPacketsBufferQueryNextPacket, + TcPacketsBufferCommitNextPacket, + TcStatisticsDestroy, + TcStatisticsUpdate, + TcStatisticsQueryValue, +}; +#endif + +#define MAX_TC_PACKET_SIZE 9500 + +#pragma pack(push, 1) + +#define PPH_PH_FLAG_PADDING ((UCHAR)0x01) +#define PPH_PH_VERSION ((UCHAR)0x00) + +typedef struct _PPI_PACKET_HEADER +{ + UCHAR PphVersion; + UCHAR PphFlags; + USHORT PphLength; + ULONG PphDlt; +} + PPI_PACKET_HEADER, *PPPI_PACKET_HEADER; + +typedef struct _PPI_FIELD_HEADER +{ + USHORT PfhType; + USHORT PfhLength; +} + PPI_FIELD_HEADER, *PPPI_FIELD_HEADER; + + +#define PPI_FIELD_TYPE_AGGREGATION_EXTENSION ((UCHAR)0x08) + +typedef struct _PPI_FIELD_AGGREGATION_EXTENSION +{ + ULONG InterfaceId; +} + PPI_FIELD_AGGREGATION_EXTENSION, *PPPI_FIELD_AGGREGATION_EXTENSION; + + +#define PPI_FIELD_TYPE_802_3_EXTENSION ((UCHAR)0x09) + +#define PPI_FLD_802_3_EXT_FLAG_FCS_PRESENT ((ULONG)0x00000001) + +typedef struct _PPI_FIELD_802_3_EXTENSION +{ + ULONG Flags; + ULONG Errors; +} + PPI_FIELD_802_3_EXTENSION, *PPPI_FIELD_802_3_EXTENSION; + +typedef struct _PPI_HEADER +{ + PPI_PACKET_HEADER PacketHeader; + PPI_FIELD_HEADER AggregationFieldHeader; + PPI_FIELD_AGGREGATION_EXTENSION AggregationField; + PPI_FIELD_HEADER Dot3FieldHeader; + PPI_FIELD_802_3_EXTENSION Dot3Field; +} + PPI_HEADER, *PPPI_HEADER; +#pragma pack(pop) + +#ifdef _WIN32 +// +// This wrapper around loadlibrary appends the system folder (usually c:\windows\system32) +// to the relative path of the DLL, so that the DLL is always loaded from an absolute path +// (It's no longer possible to load airpcap.dll from the application folder). +// This solves the DLL Hijacking issue discovered in August 2010 +// http://blog.metasploit.com/2010/08/exploiting-dll-hijacking-flaws.html +// +HMODULE LoadLibrarySafe(LPCTSTR lpFileName) +{ + TCHAR path[MAX_PATH]; + TCHAR fullFileName[MAX_PATH]; + UINT res; + HMODULE hModule = NULL; + do + { + res = GetSystemDirectory(path, MAX_PATH); + + if (res == 0) + { + // + // some bad failure occurred; + // + break; + } + + if (res > MAX_PATH) + { + // + // the buffer was not big enough + // + SetLastError(ERROR_INSUFFICIENT_BUFFER); + break; + } + + if (res + 1 + _tcslen(lpFileName) + 1 < MAX_PATH) + { + memcpy(fullFileName, path, res * sizeof(TCHAR)); + fullFileName[res] = _T('\\'); + memcpy(&fullFileName[res + 1], lpFileName, (_tcslen(lpFileName) + 1) * sizeof(TCHAR)); + + hModule = LoadLibrary(fullFileName); + } + else + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + } + + }while(FALSE); + + return hModule; +} + +/* + * NOTE: this function should be called by the pcap functions that can theoretically + * deal with the Tc library for the first time, namely listing the adapters and + * opening one. All the other ones (close, read, write, set parameters) work + * on an open instance of TC, so we do not care to call this function + */ +TC_API_LOAD_STATUS LoadTcFunctions(void) +{ + TC_API_LOAD_STATUS currentStatus; + + do + { + currentStatus = InterlockedCompareExchange((LONG*)&g_TcFunctions.LoadStatus, TC_API_LOADING, TC_API_UNLOADED); + + while(currentStatus == TC_API_LOADING) + { + currentStatus = InterlockedCompareExchange((LONG*)&g_TcFunctions.LoadStatus, TC_API_LOADING, TC_API_LOADING); + Sleep(10); + } + + /* + * at this point we are either in the LOADED state, unloaded state (i.e. we are the ones loading everything) + * or in cannot load + */ + if(currentStatus == TC_API_LOADED) + { + return TC_API_LOADED; + } + + if (currentStatus == TC_API_CANNOT_LOAD) + { + return TC_API_CANNOT_LOAD; + } + + currentStatus = TC_API_CANNOT_LOAD; + + g_TcFunctions.hTcApiDllHandle = LoadLibrarySafe("TcApi.dll"); + if (g_TcFunctions.hTcApiDllHandle == NULL) break; + + g_TcFunctions.QueryPortList = (TcFcnQueryPortList) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcQueryPortList"); + g_TcFunctions.FreePortList = (TcFcnFreePortList) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcFreePortList"); + + g_TcFunctions.StatusGetString = (TcFcnStatusGetString) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatusGetString"); + + g_TcFunctions.PortGetName = (TcFcnPortGetName) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPortGetName"); + g_TcFunctions.PortGetDescription = (TcFcnPortGetDescription) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPortGetDescription"); + + g_TcFunctions.InstanceOpenByName = (TcFcnInstanceOpenByName) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceOpenByName"); + g_TcFunctions.InstanceClose = (TcFcnInstanceClose) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceClose"); + g_TcFunctions.InstanceSetFeature = (TcFcnInstanceSetFeature) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceSetFeature"); + g_TcFunctions.InstanceQueryFeature = (TcFcnInstanceQueryFeature) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceQueryFeature"); + g_TcFunctions.InstanceReceivePackets = (TcFcnInstanceReceivePackets) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceReceivePackets"); + g_TcFunctions.InstanceGetReceiveWaitHandle = (TcFcnInstanceGetReceiveWaitHandle)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceGetReceiveWaitHandle"); + g_TcFunctions.InstanceTransmitPackets = (TcFcnInstanceTransmitPackets)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceTransmitPackets"); + g_TcFunctions.InstanceQueryStatistics = (TcFcnInstanceQueryStatistics)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceQueryStatistics"); + + g_TcFunctions.PacketsBufferCreate = (TcFcnPacketsBufferCreate) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferCreate"); + g_TcFunctions.PacketsBufferDestroy = (TcFcnPacketsBufferDestroy) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferDestroy"); + g_TcFunctions.PacketsBufferQueryNextPacket = (TcFcnPacketsBufferQueryNextPacket)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferQueryNextPacket"); + g_TcFunctions.PacketsBufferCommitNextPacket = (TcFcnPacketsBufferCommitNextPacket)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferCommitNextPacket"); + + g_TcFunctions.StatisticsDestroy = (TcFcnStatisticsDestroy) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatisticsDestroy"); + g_TcFunctions.StatisticsUpdate = (TcFcnStatisticsUpdate) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatisticsUpdate"); + g_TcFunctions.StatisticsQueryValue = (TcFcnStatisticsQueryValue) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatisticsQueryValue"); + + if ( g_TcFunctions.QueryPortList == NULL + || g_TcFunctions.FreePortList == NULL + || g_TcFunctions.StatusGetString == NULL + || g_TcFunctions.PortGetName == NULL + || g_TcFunctions.PortGetDescription == NULL + || g_TcFunctions.InstanceOpenByName == NULL + || g_TcFunctions.InstanceClose == NULL + || g_TcFunctions.InstanceSetFeature == NULL + || g_TcFunctions.InstanceQueryFeature == NULL + || g_TcFunctions.InstanceReceivePackets == NULL + || g_TcFunctions.InstanceGetReceiveWaitHandle == NULL + || g_TcFunctions.InstanceTransmitPackets == NULL + || g_TcFunctions.InstanceQueryStatistics == NULL + || g_TcFunctions.PacketsBufferCreate == NULL + || g_TcFunctions.PacketsBufferDestroy == NULL + || g_TcFunctions.PacketsBufferQueryNextPacket == NULL + || g_TcFunctions.PacketsBufferCommitNextPacket == NULL + || g_TcFunctions.StatisticsDestroy == NULL + || g_TcFunctions.StatisticsUpdate == NULL + || g_TcFunctions.StatisticsQueryValue == NULL + ) + { + break; + } + + /* + * everything got loaded, yay!! + */ + currentStatus = TC_API_LOADED; + }while(FALSE); + + if (currentStatus != TC_API_LOADED) + { + if (g_TcFunctions.hTcApiDllHandle != NULL) + { + FreeLibrary(g_TcFunctions.hTcApiDllHandle); + g_TcFunctions.hTcApiDllHandle = NULL; + } + } + + InterlockedExchange((LONG*)&g_TcFunctions.LoadStatus, currentStatus); + + return currentStatus; +} +#else +// static linking +TC_API_LOAD_STATUS LoadTcFunctions(void) +{ + return TC_API_LOADED; +} +#endif + +/* + * Private data for capturing on TurboCap devices. + */ +struct pcap_tc { + TC_INSTANCE TcInstance; + TC_PACKETS_BUFFER TcPacketsBuffer; + ULONG TcAcceptedCount; + u_char *PpiPacket; +}; + +int +TcFindAllDevs(pcap_if_list_t *devlist, char *errbuf) +{ + TC_API_LOAD_STATUS loadStatus; + ULONG numPorts; + PTC_PORT pPorts = NULL; + TC_STATUS status; + int result = 0; + pcap_if_t *dev; + ULONG i; + + do + { + loadStatus = LoadTcFunctions(); + + if (loadStatus != TC_API_LOADED) + { + result = 0; + break; + } + + /* + * enumerate the ports, and add them to the list + */ + status = g_TcFunctions.QueryPortList(&pPorts, &numPorts); + + if (status != TC_SUCCESS) + { + result = 0; + break; + } + + for (i = 0; i < numPorts; i++) + { + /* + * transform the port into an entry in the list + */ + dev = TcCreatePcapIfFromPort(pPorts[i]); + + if (dev != NULL) + add_dev(devlist, dev->name, dev->flags, dev->description, errbuf); + } + + if (numPorts > 0) + { + /* + * ignore the result here + */ + status = g_TcFunctions.FreePortList(pPorts); + } + + }while(FALSE); + + return result; +} + +static pcap_if_t* TcCreatePcapIfFromPort(TC_PORT port) +{ + CHAR *name; + CHAR *description; + pcap_if_t *newIf = NULL; + + newIf = (pcap_if_t*)malloc(sizeof(*newIf)); + if (newIf == NULL) + { + return NULL; + } + + memset(newIf, 0, sizeof(*newIf)); + + name = g_TcFunctions.PortGetName(port); + description = g_TcFunctions.PortGetDescription(port); + + newIf->name = (char*)malloc(strlen(name) + 1); + if (newIf->name == NULL) + { + free(newIf); + return NULL; + } + + newIf->description = (char*)malloc(strlen(description) + 1); + if (newIf->description == NULL) + { + free(newIf->name); + free(newIf); + return NULL; + } + + strcpy(newIf->name, name); + strcpy(newIf->description, description); + + newIf->addresses = NULL; + newIf->next = NULL; + newIf->flags = 0; + + return newIf; + +} + +static int +TcActivate(pcap_t *p) +{ + struct pcap_tc *pt = p->priv; + TC_STATUS status; + ULONG timeout; + PPPI_HEADER pPpiHeader; + + if (p->opt.rfmon) + { + /* + * No monitor mode on Tc cards; they're Ethernet + * capture adapters. + */ + return PCAP_ERROR_RFMON_NOTSUP; + } + + pt->PpiPacket = malloc(sizeof(PPI_HEADER) + MAX_TC_PACKET_SIZE); + + if (pt->PpiPacket == NULL) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error allocating memory"); + 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; + + /* + * Initialize the PPI fixed fields + */ + pPpiHeader = (PPPI_HEADER)pt->PpiPacket; + pPpiHeader->PacketHeader.PphDlt = DLT_EN10MB; + pPpiHeader->PacketHeader.PphLength = sizeof(PPI_HEADER); + pPpiHeader->PacketHeader.PphFlags = 0; + pPpiHeader->PacketHeader.PphVersion = 0; + + pPpiHeader->AggregationFieldHeader.PfhLength = sizeof(PPI_FIELD_AGGREGATION_EXTENSION); + pPpiHeader->AggregationFieldHeader.PfhType = PPI_FIELD_TYPE_AGGREGATION_EXTENSION; + + pPpiHeader->Dot3FieldHeader.PfhLength = sizeof(PPI_FIELD_802_3_EXTENSION); + pPpiHeader->Dot3FieldHeader.PfhType = PPI_FIELD_TYPE_802_3_EXTENSION; + + status = g_TcFunctions.InstanceOpenByName(p->opt.device, &pt->TcInstance); + + if (status != TC_SUCCESS) + { + /* Adapter detected but we are not able to open it. Return failure. */ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error opening TurboCap adapter: %s", g_TcFunctions.StatusGetString(status)); + return PCAP_ERROR; + } + + p->linktype = DLT_EN10MB; + p->dlt_list = (u_int *) malloc(sizeof(u_int) * 2); + /* + * If that fails, just leave the list empty. + */ + if (p->dlt_list != NULL) { + p->dlt_list[0] = DLT_EN10MB; + p->dlt_list[1] = DLT_PPI; + p->dlt_count = 2; + } + + /* + * ignore promiscuous mode + * p->opt.promisc + */ + + + /* + * ignore all the buffer sizes + */ + + /* + * enable reception + */ + status = g_TcFunctions.InstanceSetFeature(pt->TcInstance, TC_INST_FT_RX_STATUS, 1); + + if (status != TC_SUCCESS) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,"Error enabling reception on a TurboCap instance: %s", g_TcFunctions.StatusGetString(status)); + goto bad; + } + + /* + * enable transmission + */ + status = g_TcFunctions.InstanceSetFeature(pt->TcInstance, TC_INST_FT_TX_STATUS, 1); + /* + * Ignore the error here. + */ + + p->inject_op = TcInject; + /* + * if the timeout is -1, it means immediate return, no timeout + * if the timeout is 0, it means INFINITE + */ + + if (p->opt.timeout == 0) + { + timeout = 0xFFFFFFFF; + } + else + if (p->opt.timeout < 0) + { + /* + * we insert a minimal timeout here + */ + timeout = 10; + } + else + { + timeout = p->opt.timeout; + } + + status = g_TcFunctions.InstanceSetFeature(pt->TcInstance, TC_INST_FT_READ_TIMEOUT, timeout); + + if (status != TC_SUCCESS) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,"Error setting the read timeout a TurboCap instance: %s", g_TcFunctions.StatusGetString(status)); + goto bad; + } + + p->read_op = TcRead; + p->setfilter_op = TcSetFilter; + p->setdirection_op = NULL; /* Not implemented. */ + p->set_datalink_op = TcSetDatalink; + p->getnonblock_op = TcGetNonBlock; + p->setnonblock_op = TcSetNonBlock; + p->stats_op = TcStats; +#ifdef _WIN32 + p->stats_ex_op = TcStatsEx; + p->setbuff_op = TcSetBuff; + p->setmode_op = TcSetMode; + p->setmintocopy_op = TcSetMinToCopy; + p->getevent_op = TcGetReceiveWaitHandle; + p->oid_get_request_op = TcOidGetRequest; + p->oid_set_request_op = TcOidSetRequest; + p->sendqueue_transmit_op = TcSendqueueTransmit; + p->setuserbuffer_op = TcSetUserBuffer; + p->live_dump_op = TcLiveDump; + p->live_dump_ended_op = TcLiveDumpEnded; + p->get_airpcap_handle_op = TcGetAirPcapHandle; +#else + p->selectable_fd = -1; +#endif + + p->cleanup_op = TcCleanup; + + return 0; +bad: + TcCleanup(p); + return PCAP_ERROR; +} + +pcap_t * +TcCreate(const char *device, char *ebuf, int *is_ours) +{ + ULONG numPorts; + PTC_PORT pPorts = NULL; + TC_STATUS status; + int is_tc; + ULONG i; + pcap_t *p; + + if (LoadTcFunctions() != TC_API_LOADED) + { + /* + * XXX - report this as an error rather than as + * "not a TurboCap device"? + */ + *is_ours = 0; + return NULL; + } + + /* + * enumerate the ports, and add them to the list + */ + status = g_TcFunctions.QueryPortList(&pPorts, &numPorts); + + if (status != TC_SUCCESS) + { + /* + * XXX - report this as an error rather than as + * "not a TurboCap device"? + */ + *is_ours = 0; + return NULL; + } + + is_tc = FALSE; + for (i = 0; i < numPorts; i++) + { + if (strcmp(g_TcFunctions.PortGetName(pPorts[i]), device) == 0) + { + is_tc = TRUE; + break; + } + } + + if (numPorts > 0) + { + /* + * ignore the result here + */ + (void)g_TcFunctions.FreePortList(pPorts); + } + + if (!is_tc) + { + *is_ours = 0; + return NULL; + } + + /* OK, it's probably ours. */ + *is_ours = 1; + + p = pcap_create_common(ebuf, sizeof (struct pcap_tc)); + if (p == NULL) + return NULL; + + p->activate_op = TcActivate; + /* + * Set these up front, so that, even if our client tries + * to set non-blocking mode before we're activated, or + * query the state of non-blocking mode, they get an error, + * rather than having the non-blocking mode option set + * for use later. + */ + p->getnonblock_op = TcGetNonBlock; + p->setnonblock_op = TcSetNonBlock; + return p; +} + +static int TcSetDatalink(pcap_t *p, int dlt) +{ + /* + * We don't have to do any work here; pcap_set_datalink() checks + * whether the value is in the list of DLT_ values we + * supplied, so we don't have to, and, if it is valid, sets + * p->linktype to the new value; we don't have to do anything + * in hardware, we just use what's in p->linktype. + * + * We do have to have a routine, however, so that pcap_set_datalink() + * doesn't think we don't support setting the link-layer header + * type at all. + */ + return 0; +} + +static int TcGetNonBlock(pcap_t *p) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Non-blocking mode isn't supported for TurboCap ports"); + return -1; +} + +static int TcSetNonBlock(pcap_t *p, int nonblock) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Non-blocking mode isn't supported for TurboCap ports"); + return -1; +} + +static void TcCleanup(pcap_t *p) +{ + struct pcap_tc *pt = p->priv; + + if (pt->TcPacketsBuffer != NULL) + { + g_TcFunctions.PacketsBufferDestroy(pt->TcPacketsBuffer); + pt->TcPacketsBuffer = NULL; + } + if (pt->TcInstance != NULL) + { + /* + * here we do not check for the error values + */ + g_TcFunctions.InstanceClose(pt->TcInstance); + pt->TcInstance = NULL; + } + + if (pt->PpiPacket != NULL) + { + free(pt->PpiPacket); + pt->PpiPacket = NULL; + } + + pcap_cleanup_live_common(p); +} + +/* Send a packet to the network */ +static int TcInject(pcap_t *p, const void *buf, int size) +{ + struct pcap_tc *pt = p->priv; + TC_STATUS status; + TC_PACKETS_BUFFER buffer; + TC_PACKET_HEADER header; + + if (size >= 0xFFFF) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: the TurboCap API does not support packets larger than 64k"); + return -1; + } + + status = g_TcFunctions.PacketsBufferCreate(sizeof(TC_PACKET_HEADER) + TC_ALIGN_USHORT_TO_64BIT((USHORT)size), &buffer); + + if (status != TC_SUCCESS) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcPacketsBufferCreate failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + return -1; + } + + /* + * we assume that the packet is without the checksum, as common with WinPcap + */ + memset(&header, 0, sizeof(header)); + + header.Length = (USHORT)size; + header.CapturedLength = header.Length; + + status = g_TcFunctions.PacketsBufferCommitNextPacket(buffer, &header, (PVOID)buf); + + if (status == TC_SUCCESS) + { + status = g_TcFunctions.InstanceTransmitPackets(pt->TcInstance, buffer); + + if (status != TC_SUCCESS) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcInstanceTransmitPackets failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + } + } + else + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcPacketsBufferCommitNextPacket failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + } + + g_TcFunctions.PacketsBufferDestroy(buffer); + + if (status != TC_SUCCESS) + { + return -1; + } + else + { + return 0; + } +} + +static int TcRead(pcap_t *p, int cnt, pcap_handler callback, u_char *user) +{ + struct pcap_tc *pt = p->priv; + TC_STATUS status; + int n = 0; + + /* + * Has "pcap_breakloop()" been called? + */ + if (p->break_loop) + { + /* + * Yes - clear the flag that indicates that it + * has, and return -2 to indicate that we were + * told to break out of the loop. + */ + p->break_loop = 0; + return -2; + } + + if (pt->TcPacketsBuffer == NULL) + { + status = g_TcFunctions.InstanceReceivePackets(pt->TcInstance, &pt->TcPacketsBuffer); + if (status != TC_SUCCESS) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error, TcInstanceReceivePackets failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + return -1; + } + } + + while (TRUE) + { + struct pcap_pkthdr hdr; + TC_PACKET_HEADER tcHeader; + PVOID data; + ULONG filterResult; + + /* + * Has "pcap_breakloop()" been called? + * If so, return immediately - if we haven't read any + * packets, clear the flag and return -2 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 -2; + } + else + { + return n; + } + } + + if (pt->TcPacketsBuffer == NULL) + { + break; + } + + status = g_TcFunctions.PacketsBufferQueryNextPacket(pt->TcPacketsBuffer, &tcHeader, &data); + + if (status == TC_ERROR_END_OF_BUFFER) + { + g_TcFunctions.PacketsBufferDestroy(pt->TcPacketsBuffer); + pt->TcPacketsBuffer = NULL; + break; + } + + if (status != TC_SUCCESS) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error, TcPacketsBufferQueryNextPacket failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + return -1; + } + + /* No underlaying filtering system. We need to filter on our own */ + if (p->fcode.bf_insns) + { + filterResult = bpf_filter(p->fcode.bf_insns, data, tcHeader.Length, tcHeader.CapturedLength); + + if (filterResult == 0) + { + continue; + } + + if (filterResult > tcHeader.CapturedLength) + { + filterResult = tcHeader.CapturedLength; + } + } + else + { + filterResult = tcHeader.CapturedLength; + } + + pt->TcAcceptedCount ++; + + hdr.ts.tv_sec = (bpf_u_int32)(tcHeader.Timestamp / (ULONGLONG)(1000 * 1000 * 1000)); + hdr.ts.tv_usec = (bpf_u_int32)((tcHeader.Timestamp % (ULONGLONG)(1000 * 1000 * 1000)) / 1000); + + if (p->linktype == DLT_EN10MB) + { + hdr.caplen = filterResult; + hdr.len = tcHeader.Length; + (*callback)(user, &hdr, data); + } + else + { + PPPI_HEADER pPpiHeader = (PPPI_HEADER)pt->PpiPacket; + PVOID data2 = pPpiHeader + 1; + + pPpiHeader->AggregationField.InterfaceId = TC_PH_FLAGS_RX_PORT_ID(tcHeader.Flags); + pPpiHeader->Dot3Field.Errors = tcHeader.Errors; + if (tcHeader.Flags & TC_PH_FLAGS_CHECKSUM) + { + pPpiHeader->Dot3Field.Flags = PPI_FLD_802_3_EXT_FLAG_FCS_PRESENT; + } + else + { + pPpiHeader->Dot3Field.Flags = 0; + } + + if (filterResult <= MAX_TC_PACKET_SIZE) + { + memcpy(data2, data, filterResult); + hdr.caplen = sizeof(PPI_HEADER) + filterResult; + hdr.len = sizeof(PPI_HEADER) + tcHeader.Length; + } + else + { + memcpy(data2, data, MAX_TC_PACKET_SIZE); + hdr.caplen = sizeof(PPI_HEADER) + MAX_TC_PACKET_SIZE; + hdr.len = sizeof(PPI_HEADER) + tcHeader.Length; + } + + (*callback)(user, &hdr, pt->PpiPacket); + + } + + if (++n >= cnt && cnt > 0) + { + return n; + } + } + + return n; +} + +static int +TcStats(pcap_t *p, struct pcap_stat *ps) +{ + struct pcap_tc *pt = p->priv; + TC_STATISTICS statistics; + TC_STATUS status; + ULONGLONG counter; + struct pcap_stat s; + + status = g_TcFunctions.InstanceQueryStatistics(pt->TcInstance, &statistics); + + if (status != TC_SUCCESS) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcInstanceQueryStatistics: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + return -1; + } + + memset(&s, 0, sizeof(s)); + + status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_TOTAL_RX_PACKETS, &counter); + if (status != TC_SUCCESS) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + return -1; + } + if (counter <= (ULONGLONG)0xFFFFFFFF) + { + s.ps_recv = (ULONG)counter; + } + else + { + s.ps_recv = 0xFFFFFFFF; + } + + status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_RX_DROPPED_PACKETS, &counter); + if (status != TC_SUCCESS) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + return -1; + } + if (counter <= (ULONGLONG)0xFFFFFFFF) + { + s.ps_ifdrop = (ULONG)counter; + s.ps_drop = (ULONG)counter; + } + else + { + s.ps_ifdrop = 0xFFFFFFFF; + s.ps_drop = 0xFFFFFFFF; + } + +#if defined(_WIN32) && defined(ENABLE_REMOTE) + s.ps_capt = pt->TcAcceptedCount; +#endif + *ps = s; + + return 0; +} + + +/* + * We filter at user level, since the kernel driver does't process the packets + */ +static int +TcSetFilter(pcap_t *p, struct bpf_program *fp) +{ + if(!fp) + { + strncpy(p->errbuf, "setfilter: No filter specified", sizeof(p->errbuf)); + return -1; + } + + /* Install a user level filter */ + if (install_bpf_program(p, fp) < 0) + { + return -1; + } + + return 0; +} + +#ifdef _WIN32 +static struct pcap_stat * +TcStatsEx(pcap_t *p, int *pcap_stat_size) +{ + struct pcap_tc *pt = p->priv; + TC_STATISTICS statistics; + TC_STATUS status; + ULONGLONG counter; + + *pcap_stat_size = sizeof (p->stat); + + status = g_TcFunctions.InstanceQueryStatistics(pt->TcInstance, &statistics); + + if (status != TC_SUCCESS) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcInstanceQueryStatistics: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + return NULL; + } + + memset(&p->stat, 0, sizeof(p->stat)); + + status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_TOTAL_RX_PACKETS, &counter); + if (status != TC_SUCCESS) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + return NULL; + } + if (counter <= (ULONGLONG)0xFFFFFFFF) + { + p->stat.ps_recv = (ULONG)counter; + } + else + { + p->stat.ps_recv = 0xFFFFFFFF; + } + + status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_RX_DROPPED_PACKETS, &counter); + if (status != TC_SUCCESS) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + return NULL; + } + if (counter <= (ULONGLONG)0xFFFFFFFF) + { + p->stat.ps_ifdrop = (ULONG)counter; + p->stat.ps_drop = (ULONG)counter; + } + else + { + p->stat.ps_ifdrop = 0xFFFFFFFF; + p->stat.ps_drop = 0xFFFFFFFF; + } + +#if defined(_WIN32) && defined(ENABLE_REMOTE) + p->stat.ps_capt = pt->TcAcceptedCount; +#endif + + return &p->stat; +} + +/* Set the dimension of the kernel-level capture buffer */ +static int +TcSetBuff(pcap_t *p, int dim) +{ + /* + * XXX turbocap has an internal way of managing buffers. + * And at the moment it's not configurable, so we just + * silently ignore the request to set the buffer. + */ + return 0; +} + +static int +TcSetMode(pcap_t *p, int mode) +{ + if (mode != MODE_CAPT) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Mode %u not supported by TurboCap devices. TurboCap only supports capture.", mode); + return -1; + } + + return 0; +} + +static int +TcSetMinToCopy(pcap_t *p, int size) +{ + struct pcap_tc *pt = p->priv; + TC_STATUS status; + + if (size < 0) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Mintocopy cannot be less than 0."); + return -1; + } + + status = g_TcFunctions.InstanceSetFeature(pt->TcInstance, TC_INST_FT_MINTOCOPY, (ULONG)size); + + if (status != TC_SUCCESS) + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error setting the mintocopy: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + } + + return 0; +} + +static HANDLE +TcGetReceiveWaitHandle(pcap_t *p) +{ + struct pcap_tc *pt = p->priv; + + return g_TcFunctions.InstanceGetReceiveWaitHandle(pt->TcInstance); +} + +static int +TcOidGetRequest(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_, size_t *lenp _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "An OID get request cannot be performed on a TurboCap device"); + return PCAP_ERROR; +} + +static int +TcOidSetRequest(pcap_t *p, bpf_u_int32 oid _U_, const void *data _U_, + size_t *lenp _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "An OID set request cannot be performed on a TurboCap device"); + return PCAP_ERROR; +} + +static u_int +TcSendqueueTransmit(pcap_t *p, pcap_send_queue *queue _U_, int sync _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Packets cannot be bulk transmitted on a TurboCap device"); + return 0; +} + +static int +TcSetUserBuffer(pcap_t *p, int size _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "The user buffer cannot be set on a TurboCap device"); + return -1; +} + +static int +TcLiveDump(pcap_t *p, char *filename _U_, int maxsize _U_, int maxpacks _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Live packet dumping cannot be performed on a TurboCap device"); + return -1; +} + +static int +TcLiveDumpEnded(pcap_t *p, int sync _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Live packet dumping cannot be performed on a TurboCap device"); + return -1; +} + +static PAirpcapHandle +TcGetAirPcapHandle(pcap_t *p _U_) +{ + return NULL; +} +#endif |