1 /* 2 * Copyright (c) 2008 CACE Technologies, Davis (California) 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of CACE Technologies nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 */ 31 32 #include <config.h> 33 34 #include <pcap.h> 35 #include <pcap-int.h> 36 37 #include "pcap-tc.h" 38 39 #include <malloc.h> 40 #include <memory.h> 41 #include <string.h> 42 #include <errno.h> 43 44 #ifdef _WIN32 45 #include <tchar.h> 46 #endif 47 48 typedef TC_STATUS (TC_CALLCONV *TcFcnQueryPortList) (PTC_PORT *ppPorts, PULONG pLength); 49 typedef TC_STATUS (TC_CALLCONV *TcFcnFreePortList) (TC_PORT *pPorts); 50 51 typedef PCHAR (TC_CALLCONV *TcFcnStatusGetString) (TC_STATUS status); 52 53 typedef PCHAR (TC_CALLCONV *TcFcnPortGetName) (TC_PORT port); 54 typedef PCHAR (TC_CALLCONV *TcFcnPortGetDescription) (TC_PORT port); 55 56 typedef TC_STATUS (TC_CALLCONV *TcFcnInstanceOpenByName) (PCHAR name, PTC_INSTANCE pInstance); 57 typedef TC_STATUS (TC_CALLCONV *TcFcnInstanceClose) (TC_INSTANCE instance); 58 typedef TC_STATUS (TC_CALLCONV *TcFcnInstanceSetFeature) (TC_INSTANCE instance, ULONG feature, ULONG value); 59 typedef TC_STATUS (TC_CALLCONV *TcFcnInstanceQueryFeature) (TC_INSTANCE instance, ULONG feature, PULONG pValue); 60 typedef TC_STATUS (TC_CALLCONV *TcFcnInstanceReceivePackets) (TC_INSTANCE instance, PTC_PACKETS_BUFFER pBuffer); 61 typedef HANDLE (TC_CALLCONV *TcFcnInstanceGetReceiveWaitHandle) (TC_INSTANCE instance); 62 typedef TC_STATUS (TC_CALLCONV *TcFcnInstanceTransmitPackets) (TC_INSTANCE instance, TC_PACKETS_BUFFER pBuffer); 63 typedef TC_STATUS (TC_CALLCONV *TcFcnInstanceQueryStatistics) (TC_INSTANCE instance, PTC_STATISTICS pStatistics); 64 65 typedef TC_STATUS (TC_CALLCONV *TcFcnPacketsBufferCreate) (ULONG size, PTC_PACKETS_BUFFER pBuffer); 66 typedef VOID (TC_CALLCONV *TcFcnPacketsBufferDestroy) (TC_PACKETS_BUFFER buffer); 67 typedef TC_STATUS (TC_CALLCONV *TcFcnPacketsBufferQueryNextPacket)(TC_PACKETS_BUFFER buffer, PTC_PACKET_HEADER pHeader, PVOID *ppData); 68 typedef TC_STATUS (TC_CALLCONV *TcFcnPacketsBufferCommitNextPacket)(TC_PACKETS_BUFFER buffer, PTC_PACKET_HEADER pHeader, PVOID pData); 69 70 typedef VOID (TC_CALLCONV *TcFcnStatisticsDestroy) (TC_STATISTICS statistics); 71 typedef TC_STATUS (TC_CALLCONV *TcFcnStatisticsUpdate) (TC_STATISTICS statistics); 72 typedef TC_STATUS (TC_CALLCONV *TcFcnStatisticsQueryValue) (TC_STATISTICS statistics, ULONG counterId, PULONGLONG pValue); 73 74 typedef enum LONG 75 { 76 TC_API_UNLOADED = 0, 77 TC_API_LOADED, 78 TC_API_CANNOT_LOAD, 79 TC_API_LOADING 80 } 81 TC_API_LOAD_STATUS; 82 83 84 typedef struct _TC_FUNCTIONS 85 { 86 TC_API_LOAD_STATUS LoadStatus; 87 #ifdef _WIN32 88 HMODULE hTcApiDllHandle; 89 #endif 90 TcFcnQueryPortList QueryPortList; 91 TcFcnFreePortList FreePortList; 92 TcFcnStatusGetString StatusGetString; 93 94 TcFcnPortGetName PortGetName; 95 TcFcnPortGetDescription PortGetDescription; 96 97 TcFcnInstanceOpenByName InstanceOpenByName; 98 TcFcnInstanceClose InstanceClose; 99 TcFcnInstanceSetFeature InstanceSetFeature; 100 TcFcnInstanceQueryFeature InstanceQueryFeature; 101 TcFcnInstanceReceivePackets InstanceReceivePackets; 102 #ifdef _WIN32 103 TcFcnInstanceGetReceiveWaitHandle InstanceGetReceiveWaitHandle; 104 #endif 105 TcFcnInstanceTransmitPackets InstanceTransmitPackets; 106 TcFcnInstanceQueryStatistics InstanceQueryStatistics; 107 108 TcFcnPacketsBufferCreate PacketsBufferCreate; 109 TcFcnPacketsBufferDestroy PacketsBufferDestroy; 110 TcFcnPacketsBufferQueryNextPacket PacketsBufferQueryNextPacket; 111 TcFcnPacketsBufferCommitNextPacket PacketsBufferCommitNextPacket; 112 113 TcFcnStatisticsDestroy StatisticsDestroy; 114 TcFcnStatisticsUpdate StatisticsUpdate; 115 TcFcnStatisticsQueryValue StatisticsQueryValue; 116 } 117 TC_FUNCTIONS; 118 119 static pcap_if_t* TcCreatePcapIfFromPort(TC_PORT port); 120 static int TcSetDatalink(pcap_t *p, int dlt); 121 static int TcGetNonBlock(pcap_t *p); 122 static int TcSetNonBlock(pcap_t *p, int nonblock); 123 static void TcCleanup(pcap_t *p); 124 static int TcInject(pcap_t *p, const void *buf, int size); 125 static int TcRead(pcap_t *p, int cnt, pcap_handler callback, u_char *user); 126 static int TcStats(pcap_t *p, struct pcap_stat *ps); 127 #ifdef _WIN32 128 static struct pcap_stat *TcStatsEx(pcap_t *p, int *pcap_stat_size); 129 static int TcSetBuff(pcap_t *p, int dim); 130 static int TcSetMode(pcap_t *p, int mode); 131 static int TcSetMinToCopy(pcap_t *p, int size); 132 static HANDLE TcGetReceiveWaitHandle(pcap_t *p); 133 static int TcOidGetRequest(pcap_t *p, bpf_u_int32 oid, void *data, size_t *lenp); 134 static int TcOidSetRequest(pcap_t *p, bpf_u_int32 oid, const void *data, size_t *lenp); 135 static u_int TcSendqueueTransmit(pcap_t *p, pcap_send_queue *queue, int sync); 136 static int TcSetUserBuffer(pcap_t *p, int size); 137 static int TcLiveDump(pcap_t *p, char *filename, int maxsize, int maxpacks); 138 static int TcLiveDumpEnded(pcap_t *p, int sync); 139 static PAirpcapHandle TcGetAirPcapHandle(pcap_t *p); 140 #endif 141 142 #ifdef _WIN32 143 TC_FUNCTIONS g_TcFunctions = 144 { 145 TC_API_UNLOADED, /* LoadStatus */ 146 NULL, /* hTcApiDllHandle */ 147 NULL, /* QueryPortList */ 148 NULL, /* FreePortList */ 149 NULL, /* StatusGetString */ 150 NULL, /* PortGetName */ 151 NULL, /* PortGetDescription */ 152 NULL, /* InstanceOpenByName */ 153 NULL, /* InstanceClose */ 154 NULL, /* InstanceSetFeature */ 155 NULL, /* InstanceQueryFeature */ 156 NULL, /* InstanceReceivePackets */ 157 NULL, /* InstanceGetReceiveWaitHandle */ 158 NULL, /* InstanceTransmitPackets */ 159 NULL, /* InstanceQueryStatistics */ 160 NULL, /* PacketsBufferCreate */ 161 NULL, /* PacketsBufferDestroy */ 162 NULL, /* PacketsBufferQueryNextPacket */ 163 NULL, /* PacketsBufferCommitNextPacket */ 164 NULL, /* StatisticsDestroy */ 165 NULL, /* StatisticsUpdate */ 166 NULL /* StatisticsQueryValue */ 167 }; 168 #else 169 TC_FUNCTIONS g_TcFunctions = 170 { 171 TC_API_LOADED, /* LoadStatus */ 172 TcQueryPortList, 173 TcFreePortList, 174 TcStatusGetString, 175 TcPortGetName, 176 TcPortGetDescription, 177 TcInstanceOpenByName, 178 TcInstanceClose, 179 TcInstanceSetFeature, 180 TcInstanceQueryFeature, 181 TcInstanceReceivePackets, 182 #ifdef _WIN32 183 TcInstanceGetReceiveWaitHandle, 184 #endif 185 TcInstanceTransmitPackets, 186 TcInstanceQueryStatistics, 187 TcPacketsBufferCreate, 188 TcPacketsBufferDestroy, 189 TcPacketsBufferQueryNextPacket, 190 TcPacketsBufferCommitNextPacket, 191 TcStatisticsDestroy, 192 TcStatisticsUpdate, 193 TcStatisticsQueryValue, 194 }; 195 #endif 196 197 #define MAX_TC_PACKET_SIZE 9500 198 199 #pragma pack(push, 1) 200 201 #define PPH_PH_FLAG_PADDING ((UCHAR)0x01) 202 #define PPH_PH_VERSION ((UCHAR)0x00) 203 204 typedef struct _PPI_PACKET_HEADER 205 { 206 UCHAR PphVersion; 207 UCHAR PphFlags; 208 USHORT PphLength; 209 ULONG PphDlt; 210 } 211 PPI_PACKET_HEADER, *PPPI_PACKET_HEADER; 212 213 typedef struct _PPI_FIELD_HEADER 214 { 215 USHORT PfhType; 216 USHORT PfhLength; 217 } 218 PPI_FIELD_HEADER, *PPPI_FIELD_HEADER; 219 220 221 #define PPI_FIELD_TYPE_AGGREGATION_EXTENSION ((UCHAR)0x08) 222 223 typedef struct _PPI_FIELD_AGGREGATION_EXTENSION 224 { 225 ULONG InterfaceId; 226 } 227 PPI_FIELD_AGGREGATION_EXTENSION, *PPPI_FIELD_AGGREGATION_EXTENSION; 228 229 230 #define PPI_FIELD_TYPE_802_3_EXTENSION ((UCHAR)0x09) 231 232 #define PPI_FLD_802_3_EXT_FLAG_FCS_PRESENT ((ULONG)0x00000001) 233 234 typedef struct _PPI_FIELD_802_3_EXTENSION 235 { 236 ULONG Flags; 237 ULONG Errors; 238 } 239 PPI_FIELD_802_3_EXTENSION, *PPPI_FIELD_802_3_EXTENSION; 240 241 typedef struct _PPI_HEADER 242 { 243 PPI_PACKET_HEADER PacketHeader; 244 PPI_FIELD_HEADER AggregationFieldHeader; 245 PPI_FIELD_AGGREGATION_EXTENSION AggregationField; 246 PPI_FIELD_HEADER Dot3FieldHeader; 247 PPI_FIELD_802_3_EXTENSION Dot3Field; 248 } 249 PPI_HEADER, *PPPI_HEADER; 250 #pragma pack(pop) 251 252 #ifdef _WIN32 253 /* 254 * NOTE: this function should be called by the pcap functions that can theoretically 255 * deal with the Tc library for the first time, namely listing the adapters and 256 * opening one. All the other ones (close, read, write, set parameters) work 257 * on an open instance of TC, so we do not care to call this function 258 */ 259 TC_API_LOAD_STATUS LoadTcFunctions(void) 260 { 261 TC_API_LOAD_STATUS currentStatus; 262 263 do 264 { 265 currentStatus = InterlockedCompareExchange((LONG*)&g_TcFunctions.LoadStatus, TC_API_LOADING, TC_API_UNLOADED); 266 267 while(currentStatus == TC_API_LOADING) 268 { 269 currentStatus = InterlockedCompareExchange((LONG*)&g_TcFunctions.LoadStatus, TC_API_LOADING, TC_API_LOADING); 270 Sleep(10); 271 } 272 273 /* 274 * at this point we are either in the LOADED state, unloaded state (i.e. we are the ones loading everything) 275 * or in cannot load 276 */ 277 if(currentStatus == TC_API_LOADED) 278 { 279 return TC_API_LOADED; 280 } 281 282 if (currentStatus == TC_API_CANNOT_LOAD) 283 { 284 return TC_API_CANNOT_LOAD; 285 } 286 287 currentStatus = TC_API_CANNOT_LOAD; 288 289 g_TcFunctions.hTcApiDllHandle = pcapint_load_code("TcApi.dll"); 290 if (g_TcFunctions.hTcApiDllHandle == NULL) break; 291 292 g_TcFunctions.QueryPortList = (TcFcnQueryPortList) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcQueryPortList"); 293 g_TcFunctions.FreePortList = (TcFcnFreePortList) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcFreePortList"); 294 295 g_TcFunctions.StatusGetString = (TcFcnStatusGetString) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcStatusGetString"); 296 297 g_TcFunctions.PortGetName = (TcFcnPortGetName) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcPortGetName"); 298 g_TcFunctions.PortGetDescription = (TcFcnPortGetDescription) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcPortGetDescription"); 299 300 g_TcFunctions.InstanceOpenByName = (TcFcnInstanceOpenByName) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceOpenByName"); 301 g_TcFunctions.InstanceClose = (TcFcnInstanceClose) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceClose"); 302 g_TcFunctions.InstanceSetFeature = (TcFcnInstanceSetFeature) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceSetFeature"); 303 g_TcFunctions.InstanceQueryFeature = (TcFcnInstanceQueryFeature) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceQueryFeature"); 304 g_TcFunctions.InstanceReceivePackets = (TcFcnInstanceReceivePackets) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceReceivePackets"); 305 g_TcFunctions.InstanceGetReceiveWaitHandle = (TcFcnInstanceGetReceiveWaitHandle)pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceGetReceiveWaitHandle"); 306 g_TcFunctions.InstanceTransmitPackets = (TcFcnInstanceTransmitPackets)pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceTransmitPackets"); 307 g_TcFunctions.InstanceQueryStatistics = (TcFcnInstanceQueryStatistics)pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceQueryStatistics"); 308 309 g_TcFunctions.PacketsBufferCreate = (TcFcnPacketsBufferCreate) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferCreate"); 310 g_TcFunctions.PacketsBufferDestroy = (TcFcnPacketsBufferDestroy) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferDestroy"); 311 g_TcFunctions.PacketsBufferQueryNextPacket = (TcFcnPacketsBufferQueryNextPacket)pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferQueryNextPacket"); 312 g_TcFunctions.PacketsBufferCommitNextPacket = (TcFcnPacketsBufferCommitNextPacket)pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferCommitNextPacket"); 313 314 g_TcFunctions.StatisticsDestroy = (TcFcnStatisticsDestroy) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcStatisticsDestroy"); 315 g_TcFunctions.StatisticsUpdate = (TcFcnStatisticsUpdate) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcStatisticsUpdate"); 316 g_TcFunctions.StatisticsQueryValue = (TcFcnStatisticsQueryValue) pcapint_find_function(g_TcFunctions.hTcApiDllHandle, "TcStatisticsQueryValue"); 317 318 if ( g_TcFunctions.QueryPortList == NULL 319 || g_TcFunctions.FreePortList == NULL 320 || g_TcFunctions.StatusGetString == NULL 321 || g_TcFunctions.PortGetName == NULL 322 || g_TcFunctions.PortGetDescription == NULL 323 || g_TcFunctions.InstanceOpenByName == NULL 324 || g_TcFunctions.InstanceClose == NULL 325 || g_TcFunctions.InstanceSetFeature == NULL 326 || g_TcFunctions.InstanceQueryFeature == NULL 327 || g_TcFunctions.InstanceReceivePackets == NULL 328 || g_TcFunctions.InstanceGetReceiveWaitHandle == NULL 329 || g_TcFunctions.InstanceTransmitPackets == NULL 330 || g_TcFunctions.InstanceQueryStatistics == NULL 331 || g_TcFunctions.PacketsBufferCreate == NULL 332 || g_TcFunctions.PacketsBufferDestroy == NULL 333 || g_TcFunctions.PacketsBufferQueryNextPacket == NULL 334 || g_TcFunctions.PacketsBufferCommitNextPacket == NULL 335 || g_TcFunctions.StatisticsDestroy == NULL 336 || g_TcFunctions.StatisticsUpdate == NULL 337 || g_TcFunctions.StatisticsQueryValue == NULL 338 ) 339 { 340 break; 341 } 342 343 /* 344 * everything got loaded, yay!! 345 */ 346 currentStatus = TC_API_LOADED; 347 }while(FALSE); 348 349 if (currentStatus != TC_API_LOADED) 350 { 351 if (g_TcFunctions.hTcApiDllHandle != NULL) 352 { 353 FreeLibrary(g_TcFunctions.hTcApiDllHandle); 354 g_TcFunctions.hTcApiDllHandle = NULL; 355 } 356 } 357 358 InterlockedExchange((LONG*)&g_TcFunctions.LoadStatus, currentStatus); 359 360 return currentStatus; 361 } 362 #else 363 // static linking 364 TC_API_LOAD_STATUS LoadTcFunctions(void) 365 { 366 return TC_API_LOADED; 367 } 368 #endif 369 370 /* 371 * Private data for capturing on TurboCap devices. 372 */ 373 struct pcap_tc { 374 TC_INSTANCE TcInstance; 375 TC_PACKETS_BUFFER TcPacketsBuffer; 376 ULONG TcAcceptedCount; 377 u_char *PpiPacket; 378 }; 379 380 int 381 TcFindAllDevs(pcap_if_list_t *devlist, char *errbuf) 382 { 383 TC_API_LOAD_STATUS loadStatus; 384 ULONG numPorts; 385 PTC_PORT pPorts = NULL; 386 TC_STATUS status; 387 int result = 0; 388 pcap_if_t *dev; 389 ULONG i; 390 391 do 392 { 393 loadStatus = LoadTcFunctions(); 394 395 if (loadStatus != TC_API_LOADED) 396 { 397 result = 0; 398 break; 399 } 400 401 /* 402 * enumerate the ports, and add them to the list 403 */ 404 status = g_TcFunctions.QueryPortList(&pPorts, &numPorts); 405 406 if (status != TC_SUCCESS) 407 { 408 result = 0; 409 break; 410 } 411 412 for (i = 0; i < numPorts; i++) 413 { 414 /* 415 * transform the port into an entry in the list 416 */ 417 dev = TcCreatePcapIfFromPort(pPorts[i]); 418 419 if (dev != NULL) 420 pcapint_add_dev(devlist, dev->name, dev->flags, dev->description, errbuf); 421 } 422 423 if (numPorts > 0) 424 { 425 /* 426 * ignore the result here 427 */ 428 status = g_TcFunctions.FreePortList(pPorts); 429 } 430 431 }while(FALSE); 432 433 return result; 434 } 435 436 static pcap_if_t* TcCreatePcapIfFromPort(TC_PORT port) 437 { 438 CHAR *name; 439 CHAR *description; 440 pcap_if_t *newIf = NULL; 441 442 newIf = (pcap_if_t*)malloc(sizeof(*newIf)); 443 if (newIf == NULL) 444 { 445 return NULL; 446 } 447 448 memset(newIf, 0, sizeof(*newIf)); 449 450 name = g_TcFunctions.PortGetName(port); 451 description = g_TcFunctions.PortGetDescription(port); 452 453 newIf->name = (char*)malloc(strlen(name) + 1); 454 if (newIf->name == NULL) 455 { 456 free(newIf); 457 return NULL; 458 } 459 460 newIf->description = (char*)malloc(strlen(description) + 1); 461 if (newIf->description == NULL) 462 { 463 free(newIf->name); 464 free(newIf); 465 return NULL; 466 } 467 468 strcpy(newIf->name, name); 469 strcpy(newIf->description, description); 470 471 newIf->addresses = NULL; 472 newIf->next = NULL; 473 newIf->flags = 0; 474 475 return newIf; 476 477 } 478 479 static int 480 TcActivate(pcap_t *p) 481 { 482 struct pcap_tc *pt = p->priv; 483 TC_STATUS status; 484 ULONG timeout; 485 PPPI_HEADER pPpiHeader; 486 487 if (p->opt.rfmon) 488 { 489 /* 490 * No monitor mode on Tc cards; they're Ethernet 491 * capture adapters. 492 */ 493 return PCAP_ERROR_RFMON_NOTSUP; 494 } 495 496 pt->PpiPacket = malloc(sizeof(PPI_HEADER) + MAX_TC_PACKET_SIZE); 497 498 if (pt->PpiPacket == NULL) 499 { 500 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error allocating memory"); 501 return PCAP_ERROR; 502 } 503 504 /* 505 * Turn a negative snapshot value (invalid), a snapshot value of 506 * 0 (unspecified), or a value bigger than the normal maximum 507 * value, into the maximum allowed value. 508 * 509 * If some application really *needs* a bigger snapshot 510 * length, we should just increase MAXIMUM_SNAPLEN. 511 */ 512 if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN) 513 p->snapshot = MAXIMUM_SNAPLEN; 514 515 /* 516 * Initialize the PPI fixed fields 517 */ 518 pPpiHeader = (PPPI_HEADER)pt->PpiPacket; 519 pPpiHeader->PacketHeader.PphDlt = DLT_EN10MB; 520 pPpiHeader->PacketHeader.PphLength = sizeof(PPI_HEADER); 521 pPpiHeader->PacketHeader.PphFlags = 0; 522 pPpiHeader->PacketHeader.PphVersion = 0; 523 524 pPpiHeader->AggregationFieldHeader.PfhLength = sizeof(PPI_FIELD_AGGREGATION_EXTENSION); 525 pPpiHeader->AggregationFieldHeader.PfhType = PPI_FIELD_TYPE_AGGREGATION_EXTENSION; 526 527 pPpiHeader->Dot3FieldHeader.PfhLength = sizeof(PPI_FIELD_802_3_EXTENSION); 528 pPpiHeader->Dot3FieldHeader.PfhType = PPI_FIELD_TYPE_802_3_EXTENSION; 529 530 status = g_TcFunctions.InstanceOpenByName(p->opt.device, &pt->TcInstance); 531 532 if (status != TC_SUCCESS) 533 { 534 /* Adapter detected but we are not able to open it. Return failure. */ 535 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error opening TurboCap adapter: %s", g_TcFunctions.StatusGetString(status)); 536 return PCAP_ERROR; 537 } 538 539 p->linktype = DLT_EN10MB; 540 p->dlt_list = (u_int *) malloc(sizeof(u_int) * 2); 541 if (p->dlt_list == NULL) 542 { 543 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error allocating memory"); 544 return PCAP_ERROR; 545 } 546 p->dlt_list[0] = DLT_EN10MB; 547 p->dlt_list[1] = DLT_PPI; 548 p->dlt_count = 2; 549 550 /* 551 * ignore promiscuous mode 552 * p->opt.promisc 553 */ 554 555 556 /* 557 * ignore all the buffer sizes 558 */ 559 560 /* 561 * enable reception 562 */ 563 status = g_TcFunctions.InstanceSetFeature(pt->TcInstance, TC_INST_FT_RX_STATUS, 1); 564 565 if (status != TC_SUCCESS) 566 { 567 snprintf(p->errbuf, PCAP_ERRBUF_SIZE,"Error enabling reception on a TurboCap instance: %s", g_TcFunctions.StatusGetString(status)); 568 goto bad; 569 } 570 571 /* 572 * enable transmission 573 */ 574 status = g_TcFunctions.InstanceSetFeature(pt->TcInstance, TC_INST_FT_TX_STATUS, 1); 575 /* 576 * Ignore the error here. 577 */ 578 579 p->inject_op = TcInject; 580 /* 581 * if the timeout is -1, it means immediate return, no timeout 582 * if the timeout is 0, it means INFINITE 583 */ 584 585 if (p->opt.timeout == 0) 586 { 587 timeout = 0xFFFFFFFF; 588 } 589 else 590 if (p->opt.timeout < 0) 591 { 592 /* 593 * we insert a minimal timeout here 594 */ 595 timeout = 10; 596 } 597 else 598 { 599 timeout = p->opt.timeout; 600 } 601 602 status = g_TcFunctions.InstanceSetFeature(pt->TcInstance, TC_INST_FT_READ_TIMEOUT, timeout); 603 604 if (status != TC_SUCCESS) 605 { 606 snprintf(p->errbuf, PCAP_ERRBUF_SIZE,"Error setting the read timeout a TurboCap instance: %s", g_TcFunctions.StatusGetString(status)); 607 goto bad; 608 } 609 610 p->read_op = TcRead; 611 p->setfilter_op = pcapint_install_bpf_program; 612 p->setdirection_op = NULL; /* Not implemented. */ 613 p->set_datalink_op = TcSetDatalink; 614 p->getnonblock_op = TcGetNonBlock; 615 p->setnonblock_op = TcSetNonBlock; 616 p->stats_op = TcStats; 617 #ifdef _WIN32 618 p->stats_ex_op = TcStatsEx; 619 p->setbuff_op = TcSetBuff; 620 p->setmode_op = TcSetMode; 621 p->setmintocopy_op = TcSetMinToCopy; 622 p->getevent_op = TcGetReceiveWaitHandle; 623 p->oid_get_request_op = TcOidGetRequest; 624 p->oid_set_request_op = TcOidSetRequest; 625 p->sendqueue_transmit_op = TcSendqueueTransmit; 626 p->setuserbuffer_op = TcSetUserBuffer; 627 p->live_dump_op = TcLiveDump; 628 p->live_dump_ended_op = TcLiveDumpEnded; 629 p->get_airpcap_handle_op = TcGetAirPcapHandle; 630 #else 631 p->selectable_fd = -1; 632 #endif 633 634 p->cleanup_op = TcCleanup; 635 636 return 0; 637 bad: 638 TcCleanup(p); 639 return PCAP_ERROR; 640 } 641 642 pcap_t * 643 TcCreate(const char *device, char *ebuf, int *is_ours) 644 { 645 ULONG numPorts; 646 PTC_PORT pPorts = NULL; 647 TC_STATUS status; 648 int is_tc; 649 ULONG i; 650 pcap_t *p; 651 652 if (LoadTcFunctions() != TC_API_LOADED) 653 { 654 /* 655 * XXX - report this as an error rather than as 656 * "not a TurboCap device"? 657 */ 658 *is_ours = 0; 659 return NULL; 660 } 661 662 /* 663 * enumerate the ports, and add them to the list 664 */ 665 status = g_TcFunctions.QueryPortList(&pPorts, &numPorts); 666 667 if (status != TC_SUCCESS) 668 { 669 /* 670 * XXX - report this as an error rather than as 671 * "not a TurboCap device"? 672 */ 673 *is_ours = 0; 674 return NULL; 675 } 676 677 is_tc = FALSE; 678 for (i = 0; i < numPorts; i++) 679 { 680 if (strcmp(g_TcFunctions.PortGetName(pPorts[i]), device) == 0) 681 { 682 is_tc = TRUE; 683 break; 684 } 685 } 686 687 if (numPorts > 0) 688 { 689 /* 690 * ignore the result here 691 */ 692 (void)g_TcFunctions.FreePortList(pPorts); 693 } 694 695 if (!is_tc) 696 { 697 *is_ours = 0; 698 return NULL; 699 } 700 701 /* OK, it's probably ours. */ 702 *is_ours = 1; 703 704 p = PCAP_CREATE_COMMON(ebuf, struct pcap_tc); 705 if (p == NULL) 706 return NULL; 707 708 p->activate_op = TcActivate; 709 /* 710 * Set these up front, so that, even if our client tries 711 * to set non-blocking mode before we're activated, or 712 * query the state of non-blocking mode, they get an error, 713 * rather than having the non-blocking mode option set 714 * for use later. 715 */ 716 p->getnonblock_op = TcGetNonBlock; 717 p->setnonblock_op = TcSetNonBlock; 718 return p; 719 } 720 721 static int TcSetDatalink(pcap_t *p, int dlt) 722 { 723 /* 724 * We don't have to do any work here; pcap_set_datalink() checks 725 * whether the value is in the list of DLT_ values we 726 * supplied, so we don't have to, and, if it is valid, sets 727 * p->linktype to the new value; we don't have to do anything 728 * in hardware, we just use what's in p->linktype. 729 * 730 * We do have to have a routine, however, so that pcap_set_datalink() 731 * doesn't think we don't support setting the link-layer header 732 * type at all. 733 */ 734 return 0; 735 } 736 737 static int TcGetNonBlock(pcap_t *p) 738 { 739 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 740 "Non-blocking mode isn't supported for TurboCap ports"); 741 return -1; 742 } 743 744 static int TcSetNonBlock(pcap_t *p, int nonblock) 745 { 746 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 747 "Non-blocking mode isn't supported for TurboCap ports"); 748 return -1; 749 } 750 751 static void TcCleanup(pcap_t *p) 752 { 753 struct pcap_tc *pt = p->priv; 754 755 if (pt->TcPacketsBuffer != NULL) 756 { 757 g_TcFunctions.PacketsBufferDestroy(pt->TcPacketsBuffer); 758 pt->TcPacketsBuffer = NULL; 759 } 760 if (pt->TcInstance != NULL) 761 { 762 /* 763 * here we do not check for the error values 764 */ 765 g_TcFunctions.InstanceClose(pt->TcInstance); 766 pt->TcInstance = NULL; 767 } 768 769 if (pt->PpiPacket != NULL) 770 { 771 free(pt->PpiPacket); 772 pt->PpiPacket = NULL; 773 } 774 775 pcapint_cleanup_live_common(p); 776 } 777 778 /* Send a packet to the network */ 779 static int TcInject(pcap_t *p, const void *buf, int size) 780 { 781 struct pcap_tc *pt = p->priv; 782 TC_STATUS status; 783 TC_PACKETS_BUFFER buffer; 784 TC_PACKET_HEADER header; 785 786 if (size >= 0xFFFF) 787 { 788 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: the TurboCap API does not support packets larger than 64k"); 789 return -1; 790 } 791 792 status = g_TcFunctions.PacketsBufferCreate(sizeof(TC_PACKET_HEADER) + TC_ALIGN_USHORT_TO_64BIT((USHORT)size), &buffer); 793 794 if (status != TC_SUCCESS) 795 { 796 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcPacketsBufferCreate failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); 797 return -1; 798 } 799 800 /* 801 * we assume that the packet is without the checksum, as common with WinPcap 802 */ 803 memset(&header, 0, sizeof(header)); 804 805 header.Length = (USHORT)size; 806 header.CapturedLength = header.Length; 807 808 status = g_TcFunctions.PacketsBufferCommitNextPacket(buffer, &header, (PVOID)buf); 809 810 if (status == TC_SUCCESS) 811 { 812 status = g_TcFunctions.InstanceTransmitPackets(pt->TcInstance, buffer); 813 814 if (status != TC_SUCCESS) 815 { 816 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcInstanceTransmitPackets failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); 817 } 818 } 819 else 820 { 821 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcPacketsBufferCommitNextPacket failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); 822 } 823 824 g_TcFunctions.PacketsBufferDestroy(buffer); 825 826 if (status != TC_SUCCESS) 827 { 828 return -1; 829 } 830 else 831 { 832 return 0; 833 } 834 } 835 836 static int TcRead(pcap_t *p, int cnt, pcap_handler callback, u_char *user) 837 { 838 struct pcap_tc *pt = p->priv; 839 TC_STATUS status; 840 int n = 0; 841 842 /* 843 * Has "pcap_breakloop()" been called? 844 */ 845 if (p->break_loop) 846 { 847 /* 848 * Yes - clear the flag that indicates that it 849 * has, and return -2 to indicate that we were 850 * told to break out of the loop. 851 */ 852 p->break_loop = 0; 853 return -2; 854 } 855 856 if (pt->TcPacketsBuffer == NULL) 857 { 858 status = g_TcFunctions.InstanceReceivePackets(pt->TcInstance, &pt->TcPacketsBuffer); 859 if (status != TC_SUCCESS) 860 { 861 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error, TcInstanceReceivePackets failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); 862 return -1; 863 } 864 } 865 866 while (TRUE) 867 { 868 struct pcap_pkthdr hdr; 869 TC_PACKET_HEADER tcHeader; 870 PVOID data; 871 ULONG filterResult; 872 873 /* 874 * Has "pcap_breakloop()" been called? 875 * If so, return immediately - if we haven't read any 876 * packets, clear the flag and return -2 to indicate 877 * that we were told to break out of the loop, otherwise 878 * leave the flag set, so that the *next* call will break 879 * out of the loop without having read any packets, and 880 * return the number of packets we've processed so far. 881 */ 882 if (p->break_loop) 883 { 884 if (n == 0) 885 { 886 p->break_loop = 0; 887 return -2; 888 } 889 else 890 { 891 return n; 892 } 893 } 894 895 if (pt->TcPacketsBuffer == NULL) 896 { 897 break; 898 } 899 900 status = g_TcFunctions.PacketsBufferQueryNextPacket(pt->TcPacketsBuffer, &tcHeader, &data); 901 902 if (status == TC_ERROR_END_OF_BUFFER) 903 { 904 g_TcFunctions.PacketsBufferDestroy(pt->TcPacketsBuffer); 905 pt->TcPacketsBuffer = NULL; 906 break; 907 } 908 909 if (status != TC_SUCCESS) 910 { 911 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error, TcPacketsBufferQueryNextPacket failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); 912 return -1; 913 } 914 915 /* No underlying filtering system. We need to filter on our own */ 916 if (p->fcode.bf_insns) 917 { 918 filterResult = pcapint_filter(p->fcode.bf_insns, data, tcHeader.Length, tcHeader.CapturedLength); 919 920 if (filterResult == 0) 921 { 922 continue; 923 } 924 925 if (filterResult > tcHeader.CapturedLength) 926 { 927 filterResult = tcHeader.CapturedLength; 928 } 929 } 930 else 931 { 932 filterResult = tcHeader.CapturedLength; 933 } 934 935 pt->TcAcceptedCount ++; 936 937 hdr.ts.tv_sec = (bpf_u_int32)(tcHeader.Timestamp / (ULONGLONG)(1000 * 1000 * 1000)); 938 hdr.ts.tv_usec = (bpf_u_int32)((tcHeader.Timestamp % (ULONGLONG)(1000 * 1000 * 1000)) / 1000); 939 940 if (p->linktype == DLT_EN10MB) 941 { 942 hdr.caplen = filterResult; 943 hdr.len = tcHeader.Length; 944 (*callback)(user, &hdr, data); 945 } 946 else 947 { 948 PPPI_HEADER pPpiHeader = (PPPI_HEADER)pt->PpiPacket; 949 PVOID data2 = pPpiHeader + 1; 950 951 pPpiHeader->AggregationField.InterfaceId = TC_PH_FLAGS_RX_PORT_ID(tcHeader.Flags); 952 pPpiHeader->Dot3Field.Errors = tcHeader.Errors; 953 if (tcHeader.Flags & TC_PH_FLAGS_CHECKSUM) 954 { 955 pPpiHeader->Dot3Field.Flags = PPI_FLD_802_3_EXT_FLAG_FCS_PRESENT; 956 } 957 else 958 { 959 pPpiHeader->Dot3Field.Flags = 0; 960 } 961 962 if (filterResult <= MAX_TC_PACKET_SIZE) 963 { 964 memcpy(data2, data, filterResult); 965 hdr.caplen = sizeof(PPI_HEADER) + filterResult; 966 hdr.len = sizeof(PPI_HEADER) + tcHeader.Length; 967 } 968 else 969 { 970 memcpy(data2, data, MAX_TC_PACKET_SIZE); 971 hdr.caplen = sizeof(PPI_HEADER) + MAX_TC_PACKET_SIZE; 972 hdr.len = sizeof(PPI_HEADER) + tcHeader.Length; 973 } 974 975 (*callback)(user, &hdr, pt->PpiPacket); 976 977 } 978 979 if (++n >= cnt && cnt > 0) 980 { 981 return n; 982 } 983 } 984 985 return n; 986 } 987 988 static int 989 TcStats(pcap_t *p, struct pcap_stat *ps) 990 { 991 struct pcap_tc *pt = p->priv; 992 TC_STATISTICS statistics; 993 TC_STATUS status; 994 ULONGLONG counter; 995 struct pcap_stat s; 996 997 status = g_TcFunctions.InstanceQueryStatistics(pt->TcInstance, &statistics); 998 999 if (status != TC_SUCCESS) 1000 { 1001 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcInstanceQueryStatistics: %s (%08x)", g_TcFunctions.StatusGetString(status), status); 1002 return -1; 1003 } 1004 1005 memset(&s, 0, sizeof(s)); 1006 1007 status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_TOTAL_RX_PACKETS, &counter); 1008 if (status != TC_SUCCESS) 1009 { 1010 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); 1011 return -1; 1012 } 1013 if (counter <= (ULONGLONG)0xFFFFFFFF) 1014 { 1015 s.ps_recv = (ULONG)counter; 1016 } 1017 else 1018 { 1019 s.ps_recv = 0xFFFFFFFF; 1020 } 1021 1022 status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_RX_DROPPED_PACKETS, &counter); 1023 if (status != TC_SUCCESS) 1024 { 1025 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); 1026 return -1; 1027 } 1028 if (counter <= (ULONGLONG)0xFFFFFFFF) 1029 { 1030 s.ps_ifdrop = (ULONG)counter; 1031 s.ps_drop = (ULONG)counter; 1032 } 1033 else 1034 { 1035 s.ps_ifdrop = 0xFFFFFFFF; 1036 s.ps_drop = 0xFFFFFFFF; 1037 } 1038 1039 #if defined(_WIN32) && defined(ENABLE_REMOTE) 1040 s.ps_capt = pt->TcAcceptedCount; 1041 #endif 1042 *ps = s; 1043 1044 return 0; 1045 } 1046 1047 1048 #ifdef _WIN32 1049 static struct pcap_stat * 1050 TcStatsEx(pcap_t *p, int *pcap_stat_size) 1051 { 1052 struct pcap_tc *pt = p->priv; 1053 TC_STATISTICS statistics; 1054 TC_STATUS status; 1055 ULONGLONG counter; 1056 1057 *pcap_stat_size = sizeof (p->stat); 1058 1059 status = g_TcFunctions.InstanceQueryStatistics(pt->TcInstance, &statistics); 1060 1061 if (status != TC_SUCCESS) 1062 { 1063 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcInstanceQueryStatistics: %s (%08x)", g_TcFunctions.StatusGetString(status), status); 1064 return NULL; 1065 } 1066 1067 memset(&p->stat, 0, sizeof(p->stat)); 1068 1069 status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_TOTAL_RX_PACKETS, &counter); 1070 if (status != TC_SUCCESS) 1071 { 1072 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); 1073 return NULL; 1074 } 1075 if (counter <= (ULONGLONG)0xFFFFFFFF) 1076 { 1077 p->stat.ps_recv = (ULONG)counter; 1078 } 1079 else 1080 { 1081 p->stat.ps_recv = 0xFFFFFFFF; 1082 } 1083 1084 status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_RX_DROPPED_PACKETS, &counter); 1085 if (status != TC_SUCCESS) 1086 { 1087 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); 1088 return NULL; 1089 } 1090 if (counter <= (ULONGLONG)0xFFFFFFFF) 1091 { 1092 p->stat.ps_ifdrop = (ULONG)counter; 1093 p->stat.ps_drop = (ULONG)counter; 1094 } 1095 else 1096 { 1097 p->stat.ps_ifdrop = 0xFFFFFFFF; 1098 p->stat.ps_drop = 0xFFFFFFFF; 1099 } 1100 1101 #if defined(_WIN32) && defined(ENABLE_REMOTE) 1102 p->stat.ps_capt = pt->TcAcceptedCount; 1103 #endif 1104 1105 return &p->stat; 1106 } 1107 1108 /* Set the dimension of the kernel-level capture buffer */ 1109 static int 1110 TcSetBuff(pcap_t *p, int dim) 1111 { 1112 /* 1113 * XXX turbocap has an internal way of managing buffers. 1114 * And at the moment it's not configurable, so we just 1115 * silently ignore the request to set the buffer. 1116 */ 1117 return 0; 1118 } 1119 1120 static int 1121 TcSetMode(pcap_t *p, int mode) 1122 { 1123 if (mode != MODE_CAPT) 1124 { 1125 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Mode %d not supported by TurboCap devices. TurboCap only supports capture.", mode); 1126 return -1; 1127 } 1128 1129 return 0; 1130 } 1131 1132 static int 1133 TcSetMinToCopy(pcap_t *p, int size) 1134 { 1135 struct pcap_tc *pt = p->priv; 1136 TC_STATUS status; 1137 1138 if (size < 0) 1139 { 1140 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Mintocopy cannot be less than 0."); 1141 return -1; 1142 } 1143 1144 status = g_TcFunctions.InstanceSetFeature(pt->TcInstance, TC_INST_FT_MINTOCOPY, (ULONG)size); 1145 1146 if (status != TC_SUCCESS) 1147 { 1148 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error setting the mintocopy: %s (%08x)", g_TcFunctions.StatusGetString(status), status); 1149 } 1150 1151 return 0; 1152 } 1153 1154 static HANDLE 1155 TcGetReceiveWaitHandle(pcap_t *p) 1156 { 1157 struct pcap_tc *pt = p->priv; 1158 1159 return g_TcFunctions.InstanceGetReceiveWaitHandle(pt->TcInstance); 1160 } 1161 1162 static int 1163 TcOidGetRequest(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_, size_t *lenp _U_) 1164 { 1165 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 1166 "An OID get request cannot be performed on a TurboCap device"); 1167 return PCAP_ERROR; 1168 } 1169 1170 static int 1171 TcOidSetRequest(pcap_t *p, bpf_u_int32 oid _U_, const void *data _U_, 1172 size_t *lenp _U_) 1173 { 1174 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 1175 "An OID set request cannot be performed on a TurboCap device"); 1176 return PCAP_ERROR; 1177 } 1178 1179 static u_int 1180 TcSendqueueTransmit(pcap_t *p, pcap_send_queue *queue _U_, int sync _U_) 1181 { 1182 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 1183 "Packets cannot be bulk transmitted on a TurboCap device"); 1184 return 0; 1185 } 1186 1187 static int 1188 TcSetUserBuffer(pcap_t *p, int size _U_) 1189 { 1190 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 1191 "The user buffer cannot be set on a TurboCap device"); 1192 return -1; 1193 } 1194 1195 static int 1196 TcLiveDump(pcap_t *p, char *filename _U_, int maxsize _U_, int maxpacks _U_) 1197 { 1198 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 1199 "Live packet dumping cannot be performed on a TurboCap device"); 1200 return -1; 1201 } 1202 1203 static int 1204 TcLiveDumpEnded(pcap_t *p, int sync _U_) 1205 { 1206 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 1207 "Live packet dumping cannot be performed on a TurboCap device"); 1208 return -1; 1209 } 1210 1211 static PAirpcapHandle 1212 TcGetAirPcapHandle(pcap_t *p _U_) 1213 { 1214 return NULL; 1215 } 1216 #endif 1217