/*
* 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 _WIN32
/*
* 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);
/*
* 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 = pcapint_load_code("TcApi.dll");
if (g_TcFunctions.hTcApiDllHandle == NULL) break;
/*
* 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;
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. */
snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error opening TurboCap adapter: %s", g_TcFunctions.StatusGetString(status));
return PCAP_ERROR;
}
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, 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)
{
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)
{
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"Non-blocking mode isn't supported for TurboCap ports");
return -1;
}
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;
}
pcapint_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)
{
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);
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)
{
snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error, TcInstanceReceivePackets failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status);
return -1;
}
}
/*
* 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);
/* No underlying filtering system. We need to filter on our own */
if (p->fcode.bf_insns)
{
filterResult = pcapint_filter(p->fcode.bf_insns, data, tcHeader.Length, tcHeader.CapturedLength);
/* 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)
{
snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Mode %d 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)
{
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)
{
snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error setting the mintocopy: %s (%08x)", g_TcFunctions.StatusGetString(status), status);
}
static int
TcOidGetRequest(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_, size_t *lenp _U_)
{
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_)
{
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_)
{
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_)
{
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_)
{
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_)
{
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"Live packet dumping cannot be performed on a TurboCap device");
return -1;
}