From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Devices/Network/DrvNetSniffer.cpp | 575 +++++++++++++++++++++++++++++ 1 file changed, 575 insertions(+) create mode 100644 src/VBox/Devices/Network/DrvNetSniffer.cpp (limited to 'src/VBox/Devices/Network/DrvNetSniffer.cpp') diff --git a/src/VBox/Devices/Network/DrvNetSniffer.cpp b/src/VBox/Devices/Network/DrvNetSniffer.cpp new file mode 100644 index 00000000..b863f9eb --- /dev/null +++ b/src/VBox/Devices/Network/DrvNetSniffer.cpp @@ -0,0 +1,575 @@ +/* $Id: DrvNetSniffer.cpp $ */ +/** @file + * DrvNetSniffer - Network sniffer filter driver. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_DRV_NAT +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Pcap.h" +#include "VBoxDD.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Block driver instance data. + * + * @implements PDMINETWORKUP + * @implements PDMINETWORKDOWN + * @implements PDMINETWORKCONFIG + */ +typedef struct DRVNETSNIFFER +{ + /** The network interface. */ + PDMINETWORKUP INetworkUp; + /** The network interface. */ + PDMINETWORKDOWN INetworkDown; + /** The network config interface. + * @todo this is a main interface and shouldn't be here... */ + PDMINETWORKCONFIG INetworkConfig; + /** The port we're attached to. */ + PPDMINETWORKDOWN pIAboveNet; + /** The config port interface we're attached to. */ + PPDMINETWORKCONFIG pIAboveConfig; + /** The connector that's attached to us. */ + PPDMINETWORKUP pIBelowNet; + /** The filename. */ + char szFilename[RTPATH_MAX]; + /** The filehandle. */ + RTFILE hFile; + /** The lock serializing the file access. */ + RTCRITSECT Lock; + /** The NanoTS delta we pass to the pcap writers. */ + uint64_t StartNanoTS; + /** Pointer to the driver instance. */ + PPDMDRVINS pDrvIns; + /** For when we're the leaf driver. */ + RTCRITSECT XmitLock; + +} DRVNETSNIFFER, *PDRVNETSNIFFER; + + + +/** + * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit} + */ +static DECLCALLBACK(int) drvNetSnifferUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread) +{ + PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp); + if (RT_UNLIKELY(!pThis->pIBelowNet)) + { + int rc = RTCritSectTryEnter(&pThis->XmitLock); + if (RT_UNLIKELY(rc == VERR_SEM_BUSY)) + rc = VERR_TRY_AGAIN; + return rc; + } + return pThis->pIBelowNet->pfnBeginXmit(pThis->pIBelowNet, fOnWorkerThread); +} + + +/** + * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf} + */ +static DECLCALLBACK(int) drvNetSnifferUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin, + PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf) +{ + PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp); + if (RT_UNLIKELY(!pThis->pIBelowNet)) + return VERR_NET_DOWN; + return pThis->pIBelowNet->pfnAllocBuf(pThis->pIBelowNet, cbMin, pGso, ppSgBuf); +} + + +/** + * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf} + */ +static DECLCALLBACK(int) drvNetSnifferUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf) +{ + PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp); + if (RT_UNLIKELY(!pThis->pIBelowNet)) + return VERR_NET_DOWN; + return pThis->pIBelowNet->pfnFreeBuf(pThis->pIBelowNet, pSgBuf); +} + + +/** + * @interface_method_impl{PDMINETWORKUP,pfnSendBuf} + */ +static DECLCALLBACK(int) drvNetSnifferUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread) +{ + PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp); + if (RT_UNLIKELY(!pThis->pIBelowNet)) + return VERR_NET_DOWN; + + /* output to sniffer */ + RTCritSectEnter(&pThis->Lock); + if (!pSgBuf->pvUser) + PcapFileFrame(pThis->hFile, pThis->StartNanoTS, + pSgBuf->aSegs[0].pvSeg, + pSgBuf->cbUsed, + RT_MIN(pSgBuf->cbUsed, pSgBuf->aSegs[0].cbSeg)); + else + PcapFileGsoFrame(pThis->hFile, pThis->StartNanoTS, (PCPDMNETWORKGSO)pSgBuf->pvUser, + pSgBuf->aSegs[0].pvSeg, + pSgBuf->cbUsed, + RT_MIN(pSgBuf->cbUsed, pSgBuf->aSegs[0].cbSeg)); + RTCritSectLeave(&pThis->Lock); + + return pThis->pIBelowNet->pfnSendBuf(pThis->pIBelowNet, pSgBuf, fOnWorkerThread); +} + + +/** + * @interface_method_impl{PDMINETWORKUP,pfnEndXmit} + */ +static DECLCALLBACK(void) drvNetSnifferUp_EndXmit(PPDMINETWORKUP pInterface) +{ + LogFlow(("drvNetSnifferUp_EndXmit:\n")); + PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp); + if (RT_LIKELY(pThis->pIBelowNet)) + pThis->pIBelowNet->pfnEndXmit(pThis->pIBelowNet); + else + RTCritSectLeave(&pThis->XmitLock); +} + + +/** + * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode} + */ +static DECLCALLBACK(void) drvNetSnifferUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous) +{ + LogFlow(("drvNetSnifferUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous)); + PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp); + if (pThis->pIBelowNet) + pThis->pIBelowNet->pfnSetPromiscuousMode(pThis->pIBelowNet, fPromiscuous); +} + + +/** + * @interface_method_impl{PDMINETWORKUP,pfnNotifyLinkChanged} + */ +static DECLCALLBACK(void) drvNetSnifferUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState) +{ + LogFlow(("drvNetSnifferUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState)); + PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp); + if (pThis->pIBelowNet) + pThis->pIBelowNet->pfnNotifyLinkChanged(pThis->pIBelowNet, enmLinkState); +} + + +/** + * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail} + */ +static DECLCALLBACK(int) drvNetSnifferDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies) +{ + PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkDown); + return pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, cMillies); +} + + +/** + * @interface_method_impl{PDMINETWORKDOWN,pfnReceive} + */ +static DECLCALLBACK(int) drvNetSnifferDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb) +{ + PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkDown); + + /* output to sniffer */ + RTCritSectEnter(&pThis->Lock); + PcapFileFrame(pThis->hFile, pThis->StartNanoTS, pvBuf, cb, cb); + RTCritSectLeave(&pThis->Lock); + + /* pass up */ + int rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pvBuf, cb); +#if 0 + RTCritSectEnter(&pThis->Lock); + u64TS = RTTimeProgramNanoTS(); + Hdr.ts_sec = (uint32_t)(u64TS / 1000000000); + Hdr.ts_usec = (uint32_t)((u64TS / 1000) % 1000000); + Hdr.incl_len = 0; + RTFileWrite(pThis->hFile, &Hdr, sizeof(Hdr), NULL); + RTCritSectLeave(&pThis->Lock); +#endif + return rc; +} + + +/** + * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending} + */ +static DECLCALLBACK(void) drvNetSnifferDown_XmitPending(PPDMINETWORKDOWN pInterface) +{ + PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkDown); + pThis->pIAboveNet->pfnXmitPending(pThis->pIAboveNet); +} + + +/** + * Gets the current Media Access Control (MAC) address. + * + * @returns VBox status code. + * @param pInterface Pointer to the interface structure containing the called function pointer. + * @param pMac Where to store the MAC address. + * @thread EMT + */ +static DECLCALLBACK(int) drvNetSnifferDownCfg_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac) +{ + PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkConfig); + return pThis->pIAboveConfig->pfnGetMac(pThis->pIAboveConfig, pMac); +} + +/** + * Gets the new link state. + * + * @returns The current link state. + * @param pInterface Pointer to the interface structure containing the called function pointer. + * @thread EMT + */ +static DECLCALLBACK(PDMNETWORKLINKSTATE) drvNetSnifferDownCfg_GetLinkState(PPDMINETWORKCONFIG pInterface) +{ + PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkConfig); + return pThis->pIAboveConfig->pfnGetLinkState(pThis->pIAboveConfig); +} + +/** + * Sets the new link state. + * + * @returns VBox status code. + * @param pInterface Pointer to the interface structure containing the called function pointer. + * @param enmState The new link state + * @thread EMT + */ +static DECLCALLBACK(int) drvNetSnifferDownCfg_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState) +{ + PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkConfig); + return pThis->pIAboveConfig->pfnSetLinkState(pThis->pIAboveConfig, enmState); +} + + +/** + * @interface_method_impl{PDMIBASE,pfnQueryInterface} + */ +static DECLCALLBACK(void *) drvNetSnifferQueryInterface(PPDMIBASE pInterface, const char *pszIID) +{ + PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface); + PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThis->INetworkDown); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig); + return NULL; +} + + +/** + * @interface_method_impl{PDMDRVREG,pfnDetach} + */ +static DECLCALLBACK(void) drvNetSnifferDetach(PPDMDRVINS pDrvIns, uint32_t fFlags) +{ + RT_NOREF(fFlags); + PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER); + + LogFlow(("drvNetSnifferDetach: pDrvIns: %p, fFlags: %u\n", pDrvIns, fFlags)); + RTCritSectEnter(&pThis->XmitLock); + pThis->pIBelowNet = NULL; + RTCritSectLeave(&pThis->XmitLock); +} + + +/** + * @interface_method_impl{PDMDRVREG,pfnAttach} + */ +static DECLCALLBACK(int) drvNetSnifferAttach(PPDMDRVINS pDrvIns, uint32_t fFlags) +{ + PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER); + LogFlow(("drvNetSnifferAttach/#%#x: fFlags=%#x\n", pDrvIns->iInstance, fFlags)); + RTCritSectEnter(&pThis->XmitLock); + + /* + * Query the network connector interface. + */ + PPDMIBASE pBaseDown; + int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBaseDown); + if ( rc == VERR_PDM_NO_ATTACHED_DRIVER + || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME) + { + pThis->pIBelowNet = NULL; + rc = VINF_SUCCESS; + } + else if (RT_SUCCESS(rc)) + { + pThis->pIBelowNet = PDMIBASE_QUERY_INTERFACE(pBaseDown, PDMINETWORKUP); + if (pThis->pIBelowNet) + rc = VINF_SUCCESS; + else + { + AssertMsgFailed(("Configuration error: the driver below didn't export the network connector interface!\n")); + rc = VERR_PDM_MISSING_INTERFACE_BELOW; + } + } + else + AssertMsgFailed(("Failed to attach to driver below! rc=%Rrc\n", rc)); + + RTCritSectLeave(&pThis->XmitLock); + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{PDMDRVREG,pfnDestruct} + */ +static DECLCALLBACK(void) drvNetSnifferDestruct(PPDMDRVINS pDrvIns) +{ + PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER); + PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); + + if (RTCritSectIsInitialized(&pThis->Lock)) + RTCritSectDelete(&pThis->Lock); + + if (RTCritSectIsInitialized(&pThis->XmitLock)) + RTCritSectDelete(&pThis->XmitLock); + + if (pThis->hFile != NIL_RTFILE) + { + RTFileClose(pThis->hFile); + pThis->hFile = NIL_RTFILE; + } +} + + +/** + * @interface_method_impl{Construct a NAT network transport driver instance, + * PDMDRVREG,pfnDestruct} + */ +static DECLCALLBACK(int) drvNetSnifferConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags) +{ + PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns); + PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER); + PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3; + + LogFlow(("drvNetSnifferConstruct:\n")); + + /* + * Init the static parts. + */ + pThis->pDrvIns = pDrvIns; + pThis->hFile = NIL_RTFILE; + /* The pcap file *must* start at time offset 0,0. */ + pThis->StartNanoTS = RTTimeNanoTS() - RTTimeProgramNanoTS(); + /* IBase */ + pDrvIns->IBase.pfnQueryInterface = drvNetSnifferQueryInterface; + /* INetworkUp */ + pThis->INetworkUp.pfnBeginXmit = drvNetSnifferUp_BeginXmit; + pThis->INetworkUp.pfnAllocBuf = drvNetSnifferUp_AllocBuf; + pThis->INetworkUp.pfnFreeBuf = drvNetSnifferUp_FreeBuf; + pThis->INetworkUp.pfnSendBuf = drvNetSnifferUp_SendBuf; + pThis->INetworkUp.pfnEndXmit = drvNetSnifferUp_EndXmit; + pThis->INetworkUp.pfnSetPromiscuousMode = drvNetSnifferUp_SetPromiscuousMode; + pThis->INetworkUp.pfnNotifyLinkChanged = drvNetSnifferUp_NotifyLinkChanged; + /* INetworkDown */ + pThis->INetworkDown.pfnWaitReceiveAvail = drvNetSnifferDown_WaitReceiveAvail; + pThis->INetworkDown.pfnReceive = drvNetSnifferDown_Receive; + pThis->INetworkDown.pfnXmitPending = drvNetSnifferDown_XmitPending; + /* INetworkConfig */ + pThis->INetworkConfig.pfnGetMac = drvNetSnifferDownCfg_GetMac; + pThis->INetworkConfig.pfnGetLinkState = drvNetSnifferDownCfg_GetLinkState; + pThis->INetworkConfig.pfnSetLinkState = drvNetSnifferDownCfg_SetLinkState; + + /* + * Create the locks. + */ + int rc = RTCritSectInit(&pThis->Lock); + AssertRCReturn(rc, rc); + rc = RTCritSectInit(&pThis->XmitLock); + AssertRCReturn(rc, rc); + + /* + * Validate the config. + */ + PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "File", ""); + + if (pHlp->pfnCFGMGetFirstChild(pCfg)) + LogRel(("NetSniffer: Found child config entries -- are you trying to redirect ports?\n")); + + /* + * Get the filename. + */ + rc = pHlp->pfnCFGMQueryString(pCfg, "File", pThis->szFilename, sizeof(pThis->szFilename)); + if (rc == VERR_CFGM_VALUE_NOT_FOUND) + { + if (pDrvIns->iInstance > 0) + RTStrPrintf(pThis->szFilename, sizeof(pThis->szFilename), "./VBox-%x-%u.pcap", RTProcSelf(), pDrvIns->iInstance); + else + RTStrPrintf(pThis->szFilename, sizeof(pThis->szFilename), "./VBox-%x.pcap", RTProcSelf()); + } + + else if (RT_FAILURE(rc)) + { + AssertMsgFailed(("Failed to query \"File\", rc=%Rrc.\n", rc)); + return rc; + } + + /* + * Query the network port interface. + */ + pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN); + if (!pThis->pIAboveNet) + { + AssertMsgFailed(("Configuration error: the above device/driver didn't export the network port interface!\n")); + return VERR_PDM_MISSING_INTERFACE_ABOVE; + } + + /* + * Query the network config interface. + */ + pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG); + if (!pThis->pIAboveConfig) + { + AssertMsgFailed(("Configuration error: the above device/driver didn't export the network config interface!\n")); + return VERR_PDM_MISSING_INTERFACE_ABOVE; + } + + /* + * Query the network connector interface. + */ + PPDMIBASE pBaseDown; + rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBaseDown); + if ( rc == VERR_PDM_NO_ATTACHED_DRIVER + || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME) + pThis->pIBelowNet = NULL; + else if (RT_SUCCESS(rc)) + { + pThis->pIBelowNet = PDMIBASE_QUERY_INTERFACE(pBaseDown, PDMINETWORKUP); + if (!pThis->pIBelowNet) + { + AssertMsgFailed(("Configuration error: the driver below didn't export the network connector interface!\n")); + return VERR_PDM_MISSING_INTERFACE_BELOW; + } + } + else + { + AssertMsgFailed(("Failed to attach to driver below! rc=%Rrc\n", rc)); + return rc; + } + + /* + * Open output file / pipe. + */ + rc = RTFileOpen(&pThis->hFile, pThis->szFilename, + RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE); + if (RT_FAILURE(rc)) + return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, + N_("Netsniffer cannot open '%s' for writing. The directory must exist and it must be writable for the current user"), pThis->szFilename); + + char *pszPathReal = RTPathRealDup(pThis->szFilename); + if (pszPathReal) + { + LogRel(("NetSniffer: Sniffing to '%s'\n", pszPathReal)); + RTStrFree(pszPathReal); + } + else + LogRel(("NetSniffer: Sniffing to '%s'\n", pThis->szFilename)); + + /* + * Write pcap header. + * Some time has gone by since capturing pThis->StartNanoTS so get the + * current time again. + */ + PcapFileHdr(pThis->hFile, RTTimeNanoTS()); + + return VINF_SUCCESS; +} + + + +/** + * Network sniffer filter driver registration record. + */ +const PDMDRVREG g_DrvNetSniffer = +{ + /* u32Version */ + PDM_DRVREG_VERSION, + /* szName */ + "NetSniffer", + /* szRCMod */ + "", + /* szR0Mod */ + "", + /* pszDescription */ + "Network Sniffer Filter Driver", + /* fFlags */ + PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT, + /* fClass. */ + PDM_DRVREG_CLASS_NETWORK, + /* cMaxInstances */ + UINT32_MAX, + /* cbInstance */ + sizeof(DRVNETSNIFFER), + /* pfnConstruct */ + drvNetSnifferConstruct, + /* pfnDestruct */ + drvNetSnifferDestruct, + /* pfnRelocate */ + NULL, + /* pfnIOCtl */ + NULL, + /* pfnPowerOn */ + NULL, + /* pfnReset */ + NULL, + /* pfnSuspend */ + NULL, + /* pfnResume */ + NULL, + /* pfnAttach */ + drvNetSnifferAttach, + /* pfnDetach */ + drvNetSnifferDetach, + /* pfnPowerOff */ + NULL, + /* pfnSoftReset */ + NULL, + /* u32EndVersion */ + PDM_DRVREG_VERSION +}; + -- cgit v1.2.3