VirtualBox

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

Last change on this file since 4014 was 4014, checked in by vboxsync, 17 years ago

Use pdmdrv.h and pdmdev.h where appropirate.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.9 KB
Line 
1/** @file
2 *
3 * VBox network devices:
4 * Network sniffer filter driver
5 */
6
7/*
8 * Copyright (C) 2006-2007 innotek GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DRV_NAT
28#include <VBox/pdmdrv.h>
29
30#include <VBox/log.h>
31#include <iprt/assert.h>
32#include <iprt/file.h>
33#include <iprt/process.h>
34#include <iprt/string.h>
35#include <iprt/time.h>
36#include <iprt/critsect.h>
37#include <VBox/param.h>
38
39#include "Builtins.h"
40
41
42/*******************************************************************************
43* Structures and Typedefs *
44*******************************************************************************/
45/**
46 * Block driver instance data.
47 */
48typedef struct DRVNETSNIFFER
49{
50 /** The network interface. */
51 PDMINETWORKCONNECTOR INetworkConnector;
52 /** The network interface. */
53 PDMINETWORKPORT INetworkPort;
54 /** The port we're attached to. */
55 PPDMINETWORKPORT pPort;
56 /** The connector that's attached to us. */
57 PPDMINETWORKCONNECTOR pConnector;
58 /** The filename. */
59 char szFilename[RTPATH_MAX];
60 /** The filehandle. */
61 RTFILE File;
62 /** The lock serializing the file access. */
63 RTCRITSECT Lock;
64 /** Pointer to the driver instance. */
65 PPDMDRVINS pDrvIns;
66
67} DRVNETSNIFFER, *PDRVNETSNIFFER;
68
69/** Converts a pointer to NAT::INetworkConnector to a PDRVNETSNIFFER. */
70#define PDMINETWORKCONNECTOR_2_DRVNETSNIFFER(pInterface) ( (PDRVNETSNIFFER)((uintptr_t)pInterface - RT_OFFSETOF(DRVNETSNIFFER, INetworkConnector)) )
71
72/** Converts a pointer to NAT::INetworkPort to a PDRVNETSNIFFER. */
73#define PDMINETWORKPORT_2_DRVNETSNIFFER(pInterface) ( (PDRVNETSNIFFER)((uintptr_t)pInterface - RT_OFFSETOF(DRVNETSNIFFER, INetworkPort)) )
74
75
76/* "libpcap" magic */
77#define PCAP_MAGIC 0xa1b2c3d4
78
79/* "libpcap" file header (minus magic number). */
80struct pcap_hdr
81{
82 uint16_t version_major; /* major version number = 2 */
83 uint16_t version_minor; /* minor version number = 4 */
84 int32_t thiszone; /* GMT to local correction = 0 */
85 uint32_t sigfigs; /* accuracy of timestamps = 0 */
86 uint32_t snaplen; /* max length of captured packets, in octets = 0xffff */
87 uint32_t network; /* data link type = 01 */
88};
89
90/* "libpcap" record header. */
91struct pcaprec_hdr
92{
93 uint32_t ts_sec; /* timestamp seconds */
94 uint32_t ts_usec; /* timestamp microseconds */
95 uint32_t incl_len; /* number of octets of packet saved in file */
96 uint32_t orig_len; /* actual length of packet */
97};
98
99struct pcaprec_hdr_init
100{
101 uint32_t u32Magic;
102 struct pcap_hdr pcap;
103#ifdef LOG_ENABLED
104 pcaprec_hdr rec;
105#endif
106};
107
108
109/**
110 * Send data to the network.
111 *
112 * @returns VBox status code.
113 * @param pInterface Pointer to the interface structure containing the called function pointer.
114 * @param pvBuf Data to send.
115 * @param cb Number of bytes to send.
116 * @thread EMT
117 */
118static DECLCALLBACK(int) drvNetSnifferSend(PPDMINETWORKCONNECTOR pInterface, const void *pvBuf, size_t cb)
119{
120 PDRVNETSNIFFER pData = PDMINETWORKCONNECTOR_2_DRVNETSNIFFER(pInterface);
121
122 /* output to sniffer */
123 struct pcaprec_hdr Hdr;
124 uint64_t u64TS = RTTimeProgramNanoTS();
125 Hdr.ts_sec = (uint32_t)(u64TS / 1000000000);
126 Hdr.ts_usec = (uint32_t)((u64TS / 1000) % 1000000);
127 Hdr.incl_len = cb;
128 Hdr.orig_len = cb;
129 RTCritSectEnter(&pData->Lock);
130 RTFileWrite(pData->File, &Hdr, sizeof(Hdr), NULL);
131 RTFileWrite(pData->File, pvBuf, cb, NULL);
132 RTCritSectLeave(&pData->Lock);
133
134 /* pass down */
135 if (pData->pConnector)
136 {
137 int rc = pData->pConnector->pfnSend(pData->pConnector, pvBuf, cb);
138#if 0
139 RTCritSectEnter(&pData->Lock);
140 u64TS = RTTimeProgramNanoTS();
141 Hdr.ts_sec = (uint32_t)(u64TS / 1000000000);
142 Hdr.ts_usec = (uint32_t)((u64TS / 1000) % 1000000);
143 Hdr.incl_len = 0;
144 RTFileWrite(pData->File, &Hdr, sizeof(Hdr), NULL);
145 RTCritSectLeave(&pData->Lock);
146#endif
147 return rc;
148 }
149 return VINF_SUCCESS;
150}
151
152
153/**
154 * Set promiscuous mode.
155 *
156 * This is called when the promiscuous mode is set. This means that there doesn't have
157 * to be a mode change when it's called.
158 *
159 * @param pInterface Pointer to the interface structure containing the called function pointer.
160 * @param fPromiscuous Set if the adaptor is now in promiscuous mode. Clear if it is not.
161 * @thread EMT
162 */
163static DECLCALLBACK(void) drvNetSnifferSetPromiscuousMode(PPDMINETWORKCONNECTOR pInterface, bool fPromiscuous)
164{
165 LogFlow(("drvNetSnifferSetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
166 PDRVNETSNIFFER pData = PDMINETWORKCONNECTOR_2_DRVNETSNIFFER(pInterface);
167 if (pData->pConnector)
168 pData->pConnector->pfnSetPromiscuousMode(pData->pConnector, fPromiscuous);
169}
170
171
172/**
173 * Notification on link status changes.
174 *
175 * @param pInterface Pointer to the interface structure containing the called function pointer.
176 * @param enmLinkState The new link state.
177 * @thread EMT
178 */
179static DECLCALLBACK(void) drvNetSnifferNotifyLinkChanged(PPDMINETWORKCONNECTOR pInterface, PDMNETWORKLINKSTATE enmLinkState)
180{
181 LogFlow(("drvNetSnifferNotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
182 PDRVNETSNIFFER pData = PDMINETWORKCONNECTOR_2_DRVNETSNIFFER(pInterface);
183 if (pData->pConnector)
184 pData->pConnector->pfnNotifyLinkChanged(pData->pConnector, enmLinkState);
185}
186
187
188/**
189 * More receive buffer has become available.
190 *
191 * This is called when the NIC frees up receive buffers.
192 *
193 * @param pInterface Pointer to the interface structure containing the called function pointer.
194 * @thread EMT
195 */
196static DECLCALLBACK(void) drvNetSnifferNotifyCanReceive(PPDMINETWORKCONNECTOR pInterface)
197{
198 LogFlow(("drvNetSnifferNotifyCanReceive:\n"));
199 PDRVNETSNIFFER pData = PDMINETWORKCONNECTOR_2_DRVNETSNIFFER(pInterface);
200 if (pData->pConnector)
201 pData->pConnector->pfnNotifyCanReceive(pData->pConnector);
202}
203
204
205/**
206 * Check how much data the device/driver can receive data now.
207 * This must be called before the pfnRecieve() method is called.
208 *
209 * @returns Number of bytes the device can receive now.
210 * @param pInterface Pointer to the interface structure containing the called function pointer.
211 * @thread EMT
212 */
213static DECLCALLBACK(size_t) drvNetSnifferCanReceive(PPDMINETWORKPORT pInterface)
214{
215 PDRVNETSNIFFER pData = PDMINETWORKPORT_2_DRVNETSNIFFER(pInterface);
216 return pData->pPort->pfnCanReceive(pData->pPort);
217}
218
219
220/**
221 * Receive data from the network.
222 *
223 * @returns VBox status code.
224 * @param pInterface Pointer to the interface structure containing the called function pointer.
225 * @param pvBuf The available data.
226 * @param cb Number of bytes available in the buffer.
227 * @thread EMT
228 */
229static DECLCALLBACK(int) drvNetSnifferReceive(PPDMINETWORKPORT pInterface, const void *pvBuf, size_t cb)
230{
231 PDRVNETSNIFFER pData = PDMINETWORKPORT_2_DRVNETSNIFFER(pInterface);
232
233 /* output to sniffer */
234 struct pcaprec_hdr Hdr;
235 uint64_t u64TS = RTTimeProgramNanoTS();
236 Hdr.ts_sec = (uint32_t)(u64TS / 1000000000);
237 Hdr.ts_usec = (uint32_t)((u64TS / 1000) % 1000000);
238 Hdr.incl_len = cb;
239 Hdr.orig_len = cb;
240 RTCritSectEnter(&pData->Lock);
241 RTFileWrite(pData->File, &Hdr, sizeof(Hdr), NULL);
242 RTFileWrite(pData->File, pvBuf, cb, NULL);
243 RTCritSectLeave(&pData->Lock);
244
245 /* pass up */
246 int rc = pData->pPort->pfnReceive(pData->pPort, pvBuf, cb);
247#if 0
248 RTCritSectEnter(&pData->Lock);
249 u64TS = RTTimeProgramNanoTS();
250 Hdr.ts_sec = (uint32_t)(u64TS / 1000000000);
251 Hdr.ts_usec = (uint32_t)((u64TS / 1000) % 1000000);
252 Hdr.incl_len = 0;
253 RTFileWrite(pData->File, &Hdr, sizeof(Hdr), NULL);
254 RTCritSectLeave(&pData->Lock);
255#endif
256 return rc;
257}
258
259/**
260 * Queries an interface to the driver.
261 *
262 * @returns Pointer to interface.
263 * @returns NULL if the interface was not supported by the driver.
264 * @param pInterface Pointer to this interface structure.
265 * @param enmInterface The requested interface identification.
266 * @thread Any thread.
267 */
268static DECLCALLBACK(void *) drvNetSnifferQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
269{
270 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
271 PDRVNETSNIFFER pData = PDMINS2DATA(pDrvIns, PDRVNETSNIFFER);
272 switch (enmInterface)
273 {
274 case PDMINTERFACE_BASE:
275 return &pDrvIns->IBase;
276 case PDMINTERFACE_NETWORK_CONNECTOR:
277 return &pData->INetworkConnector;
278 case PDMINTERFACE_NETWORK_PORT:
279 return &pData->INetworkPort;
280 default:
281 return NULL;
282 }
283}
284
285
286/**
287 * Destruct a driver instance.
288 *
289 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
290 * resources can be freed correctly.
291 *
292 * @param pDrvIns The driver instance data.
293 */
294static DECLCALLBACK(void) drvNetSnifferDestruct(PPDMDRVINS pDrvIns)
295{
296 PDRVNETSNIFFER pData = PDMINS2DATA(pDrvIns, PDRVNETSNIFFER);
297
298 if (RTCritSectIsInitialized(&pData->Lock))
299 RTCritSectDelete(&pData->Lock);
300
301 if (pData->File != NIL_RTFILE)
302 {
303 RTFileClose(pData->File);
304 pData->File = NIL_RTFILE;
305 }
306}
307
308
309/**
310 * Construct a NAT network transport driver instance.
311 *
312 * @returns VBox status.
313 * @param pDrvIns The driver instance data.
314 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
315 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
316 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
317 * iInstance it's expected to be used a bit in this function.
318 */
319static DECLCALLBACK(int) drvNetSnifferConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
320{
321 PDRVNETSNIFFER pData = PDMINS2DATA(pDrvIns, PDRVNETSNIFFER);
322 LogFlow(("drvNetSnifferConstruct:\n"));
323
324 /*
325 * Validate the config.
326 */
327 if (!CFGMR3AreValuesValid(pCfgHandle, "File\0"))
328 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
329
330 /*
331 * Init the static parts.
332 */
333 pData->pDrvIns = pDrvIns;
334 pData->File = NIL_RTFILE;
335 /* IBase */
336 pDrvIns->IBase.pfnQueryInterface = drvNetSnifferQueryInterface;
337 /* INetworkConnector */
338 pData->INetworkConnector.pfnSend = drvNetSnifferSend;
339 pData->INetworkConnector.pfnSetPromiscuousMode = drvNetSnifferSetPromiscuousMode;
340 pData->INetworkConnector.pfnNotifyLinkChanged = drvNetSnifferNotifyLinkChanged;
341 pData->INetworkConnector.pfnNotifyCanReceive = drvNetSnifferNotifyCanReceive;
342 /* INetworkPort */
343 pData->INetworkPort.pfnCanReceive = drvNetSnifferCanReceive;
344 pData->INetworkPort.pfnReceive = drvNetSnifferReceive;
345
346 /*
347 * Get the filename.
348 */
349 int rc = CFGMR3QueryString(pCfgHandle, "File", pData->szFilename, sizeof(pData->szFilename));
350 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
351 RTStrPrintf(pData->szFilename, sizeof(pData->szFilename), "./VBox-%x.pcap", RTProcSelf());
352 else if (VBOX_FAILURE(rc))
353 {
354 AssertMsgFailed(("Failed to query \"File\", rc=%Vrc.\n", rc));
355 return rc;
356 }
357
358 /*
359 * Query the network port interface.
360 */
361 pData->pPort = (PPDMINETWORKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_NETWORK_PORT);
362 if (!pData->pPort)
363 {
364 AssertMsgFailed(("Configuration error: the above device/driver didn't export the network port interface!\n"));
365 return VERR_PDM_MISSING_INTERFACE_ABOVE;
366 }
367
368 /*
369 * Query the network connector interface.
370 */
371 PPDMIBASE pBaseDown;
372 rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBaseDown);
373 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
374 pData->pConnector = NULL;
375 else if (VBOX_SUCCESS(rc))
376 {
377 pData->pConnector = (PPDMINETWORKCONNECTOR)pBaseDown->pfnQueryInterface(pBaseDown, PDMINTERFACE_NETWORK_CONNECTOR);
378 if (!pData->pConnector)
379 {
380 AssertMsgFailed(("Configuration error: the driver below didn't export the network connector interface!\n"));
381 return VERR_PDM_MISSING_INTERFACE_BELOW;
382 }
383 }
384 else
385 {
386 AssertMsgFailed(("Failed to attach to driver below! rc=%Vrc\n", rc));
387 return rc;
388 }
389
390 /*
391 * Create the lock.
392 */
393 rc = RTCritSectInit(&pData->Lock);
394 if (VBOX_FAILURE(rc))
395 return rc;
396
397 /*
398 * Open output file / pipe.
399 */
400 rc = RTFileOpen(&pData->File, pData->szFilename,
401 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE);
402 if (VBOX_FAILURE(rc))
403 {
404 AssertMsgFailed(("Failed to create file '%s' for writing. rc=%Vrc\n", pData->szFilename, rc));
405 return rc;
406 }
407
408 /*
409 * Write pcap header.
410 */
411#ifdef LOG_ENABLED
412 pcaprec_hdr_init Hdr =
413 {
414 PCAP_MAGIC,
415 { 2, 4, 0, 0, 0xffff, 1 },
416 { 0, 1, 0, 60}
417 }; /* force ethereal to start at 0.000000. */
418#else
419 pcaprec_hdr_init Hdr =
420 {
421 PCAP_MAGIC,
422 { 2, 4, 0, 0, 0xffff, 1 }
423 }; /* this is just to make it happy, not to be correct. */
424#endif
425
426 RTFileWrite(pData->File, &Hdr, sizeof(Hdr), NULL);
427
428 return VINF_SUCCESS;
429}
430
431
432
433/**
434 * Network sniffer filter driver registration record.
435 */
436const PDMDRVREG g_DrvNetSniffer =
437{
438 /* u32Version */
439 PDM_DRVREG_VERSION,
440 /* szDriverName */
441 "NetSniffer",
442 /* pszDescription */
443 "Network Sniffer Filter Driver",
444 /* fFlags */
445 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
446 /* fClass. */
447 PDM_DRVREG_CLASS_NETWORK,
448 /* cMaxInstances */
449 1,
450 /* cbInstance */
451 sizeof(DRVNETSNIFFER),
452 /* pfnConstruct */
453 drvNetSnifferConstruct,
454 /* pfnDestruct */
455 drvNetSnifferDestruct,
456 /* pfnIOCtl */
457 NULL,
458 /* pfnPowerOn */
459 NULL,
460 /* pfnReset */
461 NULL,
462 /* pfnSuspend */
463 NULL,
464 /* pfnResume */
465 NULL,
466 /* pfnDetach */
467 NULL,
468 /* pfnPowerOff */
469 NULL
470};
471
Note: See TracBrowser for help on using the repository browser.

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