VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DrvNetSniffer.cpp@ 28056

最後變更 在這個檔案從28056是 28056,由 vboxsync 提交於 15 年 前

DrvNetSniffer: Handle GSO SGs.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 17.9 KB
 
1/* $Id: DrvNetSniffer.cpp 28056 2010-04-07 17:22:59Z vboxsync $ */
2/** @file
3 * DrvNetSniffer - Network sniffer filter driver.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DRV_NAT
27#include <VBox/pdmdrv.h>
28#include <VBox/pdmnetifs.h>
29
30#include <VBox/log.h>
31#include <iprt/assert.h>
32#include <iprt/critsect.h>
33#include <iprt/file.h>
34#include <iprt/process.h>
35#include <iprt/string.h>
36#include <iprt/time.h>
37#include <iprt/uuid.h>
38#include <VBox/param.h>
39
40#include "Pcap.h"
41#include "Builtins.h"
42
43
44/*******************************************************************************
45* Structures and Typedefs *
46*******************************************************************************/
47/**
48 * Block driver instance data.
49 *
50 * @implements PDMINETWORKUP
51 * @implements PDMINETWORKDOWN
52 * @implements PDMINETWORKCONFIG
53 */
54typedef struct DRVNETSNIFFER
55{
56 /** The network interface. */
57 PDMINETWORKUP INetworkUp;
58 /** The network interface. */
59 PDMINETWORKDOWN INetworkDown;
60 /** The network config interface.
61 * @todo this is a main interface and shouldn't be here... */
62 PDMINETWORKCONFIG INetworkConfig;
63 /** The port we're attached to. */
64 PPDMINETWORKDOWN pIAboveNet;
65 /** The config port interface we're attached to. */
66 PPDMINETWORKCONFIG pIAboveConfig;
67 /** The connector that's attached to us. */
68 PPDMINETWORKUP pIBelowNet;
69 /** The filename. */
70 char szFilename[RTPATH_MAX];
71 /** The filehandle. */
72 RTFILE File;
73 /** The lock serializing the file access. */
74 RTCRITSECT Lock;
75 /** The NanoTS delta we pass to the pcap writers. */
76 uint64_t StartNanoTS;
77 /** Pointer to the driver instance. */
78 PPDMDRVINS pDrvIns;
79
80} DRVNETSNIFFER, *PDRVNETSNIFFER;
81
82
83
84/**
85 * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
86 */
87static DECLCALLBACK(int) drvNetSnifferUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
88 PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
89{
90 PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp);
91 if (RT_UNLIKELY(!pThis->pIBelowNet))
92 return VERR_NET_DOWN;
93 return pThis->pIBelowNet->pfnAllocBuf(pThis->pIBelowNet, cbMin, pGso, ppSgBuf);
94}
95
96
97/**
98 * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
99 */
100static DECLCALLBACK(int) drvNetSnifferUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
101{
102 PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp);
103 if (RT_UNLIKELY(!pThis->pIBelowNet))
104 return VERR_NET_DOWN;
105 return pThis->pIBelowNet->pfnFreeBuf(pThis->pIBelowNet, pSgBuf);
106}
107
108
109/**
110 * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
111 */
112static DECLCALLBACK(int) drvNetSnifferUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
113{
114 PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp);
115 if (RT_UNLIKELY(!pThis->pIBelowNet))
116 return VERR_NET_DOWN;
117
118 /* output to sniffer */
119 RTCritSectEnter(&pThis->Lock);
120 if (!pSgBuf->pvUser)
121 PcapFileFrame(pThis->File, pThis->StartNanoTS,
122 pSgBuf->aSegs[0].pvSeg,
123 pSgBuf->cbUsed,
124 RT_MIN(pSgBuf->cbUsed, pSgBuf->aSegs[0].cbSeg));
125 else
126 PcapFileGsoFrame(pThis->File, pThis->StartNanoTS, (PCPDMNETWORKGSO)pSgBuf->pvUser,
127 pSgBuf->aSegs[0].pvSeg,
128 pSgBuf->cbUsed,
129 RT_MIN(pSgBuf->cbUsed, pSgBuf->aSegs[0].cbSeg));
130 RTCritSectLeave(&pThis->Lock);
131
132 return pThis->pIBelowNet->pfnSendBuf(pThis->pIBelowNet, pSgBuf, fOnWorkerThread);
133}
134
135
136/**
137 * @interface_method_impl{PDMINETWORKUP,pfnSendDeprecated}
138 */
139static DECLCALLBACK(int) drvNetSnifferUp_SendDeprecated(PPDMINETWORKUP pInterface, const void *pvBuf, size_t cb)
140{
141 PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp);
142
143 /* output to sniffer */
144 RTCritSectEnter(&pThis->Lock);
145 PcapFileFrame(pThis->File, pThis->StartNanoTS, pvBuf, cb, cb);
146 RTCritSectLeave(&pThis->Lock);
147
148 /* pass down */
149 if (RT_LIKELY(pThis->pIBelowNet))
150 {
151 int rc = pThis->pIBelowNet->pfnSendDeprecated(pThis->pIBelowNet, pvBuf, cb);
152#if 0
153 RTCritSectEnter(&pThis->Lock);
154 u64TS = RTTimeProgramNanoTS();
155 Hdr.ts_sec = (uint32_t)(u64TS / 1000000000);
156 Hdr.ts_usec = (uint32_t)((u64TS / 1000) % 1000000);
157 Hdr.incl_len = 0;
158 RTFileWrite(pThis->File, &Hdr, sizeof(Hdr), NULL);
159 RTCritSectLeave(&pThis->Lock);
160#endif
161 return rc;
162 }
163 return VINF_SUCCESS;
164}
165
166
167/**
168 * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
169 */
170static DECLCALLBACK(void) drvNetSnifferUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
171{
172 LogFlow(("drvNetSnifferUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
173 PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp);
174 if (pThis->pIBelowNet)
175 pThis->pIBelowNet->pfnSetPromiscuousMode(pThis->pIBelowNet, fPromiscuous);
176}
177
178
179/**
180 * @interface_method_impl{PDMINETWORKUP,pfnNotifyLinkChanged}
181 */
182static DECLCALLBACK(void) drvNetSnifferUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
183{
184 LogFlow(("drvNetSnifferUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
185 PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp);
186 if (pThis->pIBelowNet)
187 pThis->pIBelowNet->pfnNotifyLinkChanged(pThis->pIBelowNet, enmLinkState);
188}
189
190
191/**
192 * @copydoc PDMINETWORKDOWN::pfnWaitReceiveAvail
193 */
194static DECLCALLBACK(int) drvNetSnifferDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
195{
196 PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkDown);
197 return pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, cMillies);
198}
199
200
201/**
202 * @copydoc PDMINETWORKDOWN::pfnReceive
203 */
204static DECLCALLBACK(int) drvNetSnifferDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
205{
206 PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkDown);
207
208 /* output to sniffer */
209 RTCritSectEnter(&pThis->Lock);
210 PcapFileFrame(pThis->File, pThis->StartNanoTS, pvBuf, cb, cb);
211 RTCritSectLeave(&pThis->Lock);
212
213 /* pass up */
214 int rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pvBuf, cb);
215#if 0
216 RTCritSectEnter(&pThis->Lock);
217 u64TS = RTTimeProgramNanoTS();
218 Hdr.ts_sec = (uint32_t)(u64TS / 1000000000);
219 Hdr.ts_usec = (uint32_t)((u64TS / 1000) % 1000000);
220 Hdr.incl_len = 0;
221 RTFileWrite(pThis->File, &Hdr, sizeof(Hdr), NULL);
222 RTCritSectLeave(&pThis->Lock);
223#endif
224 return rc;
225}
226
227
228/**
229 * @copydoc PDMINETWORKDOWN::pfnNotifyBufAvailable
230 */
231static DECLCALLBACK(void) drvNetSnifferDown_NotifyBufAvailable(PPDMINETWORKDOWN pInterface)
232{
233 PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkDown);
234 pThis->pIAboveNet->pfnNotifyBufAvailable(pThis->pIAboveNet);
235}
236
237
238/**
239 * Gets the current Media Access Control (MAC) address.
240 *
241 * @returns VBox status code.
242 * @param pInterface Pointer to the interface structure containing the called function pointer.
243 * @param pMac Where to store the MAC address.
244 * @thread EMT
245 */
246static DECLCALLBACK(int) drvNetSnifferDownCfg_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
247{
248 PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkConfig);
249 return pThis->pIAboveConfig->pfnGetMac(pThis->pIAboveConfig, pMac);
250}
251
252/**
253 * Gets the new link state.
254 *
255 * @returns The current link state.
256 * @param pInterface Pointer to the interface structure containing the called function pointer.
257 * @thread EMT
258 */
259static DECLCALLBACK(PDMNETWORKLINKSTATE) drvNetSnifferDownCfg_GetLinkState(PPDMINETWORKCONFIG pInterface)
260{
261 PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkConfig);
262 return pThis->pIAboveConfig->pfnGetLinkState(pThis->pIAboveConfig);
263}
264
265/**
266 * Sets the new link state.
267 *
268 * @returns VBox status code.
269 * @param pInterface Pointer to the interface structure containing the called function pointer.
270 * @param enmState The new link state
271 * @thread EMT
272 */
273static DECLCALLBACK(int) drvNetSnifferDownCfg_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
274{
275 PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkConfig);
276 return pThis->pIAboveConfig->pfnSetLinkState(pThis->pIAboveConfig, enmState);
277}
278
279
280/**
281 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
282 */
283static DECLCALLBACK(void *) drvNetSnifferQueryInterface(PPDMIBASE pInterface, const char *pszIID)
284{
285 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
286 PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER);
287 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
288 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
289 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThis->INetworkDown);
290 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig);
291 return NULL;
292}
293
294
295/**
296 * Detach a driver instance.
297 *
298 * @param pDrvIns The driver instance.
299 * @param fFlags Flags, combination of the PDM_TACH_FLAGS_* \#defines.
300 */
301static DECLCALLBACK(void) drvNetSnifferDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
302{
303 PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER);
304
305 LogFlow(("drvNetSnifferDetach: pDrvIns: %p, fFlags: %u\n", pDrvIns, fFlags));
306
307 pThis->pIBelowNet = NULL;
308}
309
310
311/**
312 * Attach a driver instance.
313 *
314 * @returns VBox status code.
315 * @param pDrvIns The driver instance.
316 * @param fFlags Flags, combination of the PDM_TACH_FLAGS_* \#defines.
317 */
318static DECLCALLBACK(int) drvNetSnifferAttach(PPDMDRVINS pDrvIns, uint32_t fFlags)
319{
320 PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER);
321
322 LogFlow(("drvNetSnifferAttach: pDrvIns: %p, fFlags: %u\n", pDrvIns, fFlags));
323
324 /*
325 * Query the network connector interface.
326 */
327 PPDMIBASE pBaseDown;
328 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBaseDown);
329 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
330 pThis->pIBelowNet = NULL;
331 else if (RT_SUCCESS(rc))
332 {
333 pThis->pIBelowNet = PDMIBASE_QUERY_INTERFACE(pBaseDown, PDMINETWORKUP);
334 if (!pThis->pIBelowNet)
335 {
336 AssertMsgFailed(("Configuration error: the driver below didn't export the network connector interface!\n"));
337 return VERR_PDM_MISSING_INTERFACE_BELOW;
338 }
339 }
340 else
341 {
342 AssertMsgFailed(("Failed to attach to driver below! rc=%Rrc\n", rc));
343 return rc;
344 }
345
346 return VINF_SUCCESS;
347}
348
349
350/**
351 * Destruct a driver instance.
352 *
353 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
354 * resources can be freed correctly.
355 *
356 * @param pDrvIns The driver instance data.
357 */
358static DECLCALLBACK(void) drvNetSnifferDestruct(PPDMDRVINS pDrvIns)
359{
360 PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER);
361 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
362
363 if (RTCritSectIsInitialized(&pThis->Lock))
364 RTCritSectDelete(&pThis->Lock);
365
366 if (pThis->File != NIL_RTFILE)
367 {
368 RTFileClose(pThis->File);
369 pThis->File = NIL_RTFILE;
370 }
371}
372
373
374/**
375 * Construct a NAT network transport driver instance.
376 *
377 * @copydoc FNPDMDRVCONSTRUCT
378 */
379static DECLCALLBACK(int) drvNetSnifferConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
380{
381 PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER);
382 LogFlow(("drvNetSnifferConstruct:\n"));
383 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
384
385 /*
386 * Validate the config.
387 */
388 if (!CFGMR3AreValuesValid(pCfg, "File\0"))
389 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
390
391 if (CFGMR3GetFirstChild(pCfg))
392 LogRel(("NetSniffer: Found child config entries -- are you trying to redirect ports?\n"));
393
394 /*
395 * Init the static parts.
396 */
397 pThis->pDrvIns = pDrvIns;
398 pThis->File = NIL_RTFILE;
399 /* The pcap file *must* start at time offset 0,0. */
400 pThis->StartNanoTS = RTTimeNanoTS() - RTTimeProgramNanoTS();
401 /* IBase */
402 pDrvIns->IBase.pfnQueryInterface = drvNetSnifferQueryInterface;
403 /* INetworkUp */
404 pThis->INetworkUp.pfnAllocBuf = drvNetSnifferUp_AllocBuf;
405 pThis->INetworkUp.pfnFreeBuf = drvNetSnifferUp_FreeBuf;
406 pThis->INetworkUp.pfnSendBuf = drvNetSnifferUp_SendBuf;
407 pThis->INetworkUp.pfnSendDeprecated = drvNetSnifferUp_SendDeprecated;
408 pThis->INetworkUp.pfnSetPromiscuousMode = drvNetSnifferUp_SetPromiscuousMode;
409 pThis->INetworkUp.pfnNotifyLinkChanged = drvNetSnifferUp_NotifyLinkChanged;
410 /* INetworkDown */
411 pThis->INetworkDown.pfnWaitReceiveAvail = drvNetSnifferDown_WaitReceiveAvail;
412 pThis->INetworkDown.pfnReceive = drvNetSnifferDown_Receive;
413 pThis->INetworkDown.pfnNotifyBufAvailable = drvNetSnifferDown_NotifyBufAvailable;
414 /* INetworkConfig */
415 pThis->INetworkConfig.pfnGetMac = drvNetSnifferDownCfg_GetMac;
416 pThis->INetworkConfig.pfnGetLinkState = drvNetSnifferDownCfg_GetLinkState;
417 pThis->INetworkConfig.pfnSetLinkState = drvNetSnifferDownCfg_SetLinkState;
418
419 /*
420 * Get the filename.
421 */
422 int rc = CFGMR3QueryString(pCfg, "File", pThis->szFilename, sizeof(pThis->szFilename));
423 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
424 {
425 if (pDrvIns->iInstance > 0)
426 RTStrPrintf(pThis->szFilename, sizeof(pThis->szFilename), "./VBox-%x-%u.pcap", RTProcSelf(), pDrvIns->iInstance);
427 else
428 RTStrPrintf(pThis->szFilename, sizeof(pThis->szFilename), "./VBox-%x.pcap", RTProcSelf());
429 }
430
431 else if (RT_FAILURE(rc))
432 {
433 AssertMsgFailed(("Failed to query \"File\", rc=%Rrc.\n", rc));
434 return rc;
435 }
436
437 /*
438 * Query the network port interface.
439 */
440 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
441 if (!pThis->pIAboveNet)
442 {
443 AssertMsgFailed(("Configuration error: the above device/driver didn't export the network port interface!\n"));
444 return VERR_PDM_MISSING_INTERFACE_ABOVE;
445 }
446
447 /*
448 * Query the network config interface.
449 */
450 pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
451 if (!pThis->pIAboveConfig)
452 {
453 AssertMsgFailed(("Configuration error: the above device/driver didn't export the network config interface!\n"));
454 return VERR_PDM_MISSING_INTERFACE_ABOVE;
455 }
456
457 /*
458 * Query the network connector interface.
459 */
460 PPDMIBASE pBaseDown;
461 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBaseDown);
462 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
463 pThis->pIBelowNet = NULL;
464 else if (RT_SUCCESS(rc))
465 {
466 pThis->pIBelowNet = PDMIBASE_QUERY_INTERFACE(pBaseDown, PDMINETWORKUP);
467 if (!pThis->pIBelowNet)
468 {
469 AssertMsgFailed(("Configuration error: the driver below didn't export the network connector interface!\n"));
470 return VERR_PDM_MISSING_INTERFACE_BELOW;
471 }
472 }
473 else
474 {
475 AssertMsgFailed(("Failed to attach to driver below! rc=%Rrc\n", rc));
476 return rc;
477 }
478
479 /*
480 * Create the lock.
481 */
482 rc = RTCritSectInit(&pThis->Lock);
483 if (RT_FAILURE(rc))
484 return rc;
485
486 /*
487 * Open output file / pipe.
488 */
489 rc = RTFileOpen(&pThis->File, pThis->szFilename,
490 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE);
491 if (RT_FAILURE(rc))
492 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
493 N_("Netsniffer cannot open '%s' for writing. The directory must exist and it must be writable for the current user"), pThis->szFilename);
494
495 /*
496 * Write pcap header.
497 * Some time is done since capturing pThis->StartNanoTS so capture the current time again.
498 */
499 PcapFileHdr(pThis->File, RTTimeNanoTS());
500
501 return VINF_SUCCESS;
502}
503
504
505
506/**
507 * Network sniffer filter driver registration record.
508 */
509const PDMDRVREG g_DrvNetSniffer =
510{
511 /* u32Version */
512 PDM_DRVREG_VERSION,
513 /* szName */
514 "NetSniffer",
515 /* szRCMod */
516 "",
517 /* szR0Mod */
518 "",
519 /* pszDescription */
520 "Network Sniffer Filter Driver",
521 /* fFlags */
522 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
523 /* fClass. */
524 PDM_DRVREG_CLASS_NETWORK,
525 /* cMaxInstances */
526 UINT32_MAX,
527 /* cbInstance */
528 sizeof(DRVNETSNIFFER),
529 /* pfnConstruct */
530 drvNetSnifferConstruct,
531 /* pfnDestruct */
532 drvNetSnifferDestruct,
533 /* pfnRelocate */
534 NULL,
535 /* pfnIOCtl */
536 NULL,
537 /* pfnPowerOn */
538 NULL,
539 /* pfnReset */
540 NULL,
541 /* pfnSuspend */
542 NULL,
543 /* pfnResume */
544 NULL,
545 /* pfnAttach */
546 drvNetSnifferAttach,
547 /* pfnDetach */
548 drvNetSnifferDetach,
549 /* pfnPowerOff */
550 NULL,
551 /* pfnSoftReset */
552 NULL,
553 /* u32EndVersion */
554 PDM_DRVREG_VERSION
555};
556
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette