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/NetworkServices/NetLib/IntNetIf.cpp | 575 ++++++++++++++ src/VBox/NetworkServices/NetLib/IntNetIf.h | 112 +++ src/VBox/NetworkServices/NetLib/Makefile.kup | 0 src/VBox/NetworkServices/NetLib/VBoxNetARP.cpp | 165 ++++ .../NetworkServices/NetLib/VBoxNetBaseService.cpp | 858 +++++++++++++++++++++ .../NetworkServices/NetLib/VBoxNetBaseService.h | 159 ++++ src/VBox/NetworkServices/NetLib/VBoxNetIntIf.cpp | 150 ++++ src/VBox/NetworkServices/NetLib/VBoxNetLib.h | 82 ++ .../NetLib/VBoxNetPortForwardString.cpp | 382 +++++++++ src/VBox/NetworkServices/NetLib/VBoxNetUDP.cpp | 314 ++++++++ .../NetworkServices/NetLib/VBoxPortForwardString.h | 69 ++ src/VBox/NetworkServices/NetLib/cpp/utils.h | 57 ++ src/VBox/NetworkServices/NetLib/shared_ptr.h | 112 +++ 13 files changed, 3035 insertions(+) create mode 100644 src/VBox/NetworkServices/NetLib/IntNetIf.cpp create mode 100644 src/VBox/NetworkServices/NetLib/IntNetIf.h create mode 100644 src/VBox/NetworkServices/NetLib/Makefile.kup create mode 100644 src/VBox/NetworkServices/NetLib/VBoxNetARP.cpp create mode 100644 src/VBox/NetworkServices/NetLib/VBoxNetBaseService.cpp create mode 100644 src/VBox/NetworkServices/NetLib/VBoxNetBaseService.h create mode 100644 src/VBox/NetworkServices/NetLib/VBoxNetIntIf.cpp create mode 100644 src/VBox/NetworkServices/NetLib/VBoxNetLib.h create mode 100644 src/VBox/NetworkServices/NetLib/VBoxNetPortForwardString.cpp create mode 100644 src/VBox/NetworkServices/NetLib/VBoxNetUDP.cpp create mode 100644 src/VBox/NetworkServices/NetLib/VBoxPortForwardString.h create mode 100644 src/VBox/NetworkServices/NetLib/cpp/utils.h create mode 100644 src/VBox/NetworkServices/NetLib/shared_ptr.h (limited to 'src/VBox/NetworkServices/NetLib') diff --git a/src/VBox/NetworkServices/NetLib/IntNetIf.cpp b/src/VBox/NetworkServices/NetLib/IntNetIf.cpp new file mode 100644 index 00000000..850474c2 --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/IntNetIf.cpp @@ -0,0 +1,575 @@ +/* $Id: IntNetIf.cpp $ */ +/** @file + * IntNetIfCtx - Abstract API implementing an IntNet connection using the R0 support driver or some R3 IPC variant. + */ + +/* + * Copyright (C) 2022-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 * +*********************************************************************************************************************************/ +#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3) +# if defined(RT_OS_DARWIN) +# include /* This needs to be here because it drags PVM in and cdefs.h needs to undefine it... */ +# else +# error "R3 internal networking not implemented for this platform yet!" +# endif +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include "IntNetIf.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + + +/** + * Internal network interface context instance data. + */ +typedef struct INTNETIFCTXINT +{ + /** The support driver session handle. */ + PSUPDRVSESSION pSupDrvSession; + /** Interface handle. */ + INTNETIFHANDLE hIf; + /** The internal network buffer. */ + PINTNETBUF pBuf; +#if defined (VBOX_WITH_INTNET_SERVICE_IN_R3) + /** Flag whether this interface is using the internal network switch in userspace path. */ + bool fIntNetR3Svc; + /** Receive event semaphore. */ + RTSEMEVENT hEvtRecv; +# if defined(RT_OS_DARWIN) + /** XPC connection handle to the R3 internal network switch service. */ + xpc_connection_t hXpcCon; + /** Size of the communication buffer in bytes. */ + size_t cbBuf; +# endif +#endif +} INTNETIFCTXINT; +/** Pointer to the internal network interface context instance data. */ +typedef INTNETIFCTXINT *PINTNETIFCTXINT; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ + +/** + * Calls the internal networking switch service living in either R0 or in another R3 process. + * + * @returns VBox status code. + * @param pThis The internal network driver instance data. + * @param uOperation The operation to execute. + * @param pReqHdr Pointer to the request header. + */ +static int intnetR3IfCallSvc(PINTNETIFCTXINT pThis, uint32_t uOperation, PSUPVMMR0REQHDR pReqHdr) +{ +#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3) + if (pThis->fIntNetR3Svc) + { +# if defined(RT_OS_DARWIN) + size_t cbReq = pReqHdr->cbReq; + xpc_object_t hObj = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(hObj, "req-id", uOperation); + xpc_dictionary_set_data(hObj, "req", pReqHdr, pReqHdr->cbReq); + xpc_object_t hObjReply = xpc_connection_send_message_with_reply_sync(pThis->hXpcCon, hObj); + xpc_release(hObj); + + int rc = (int)xpc_dictionary_get_int64(hObjReply, "rc"); + + size_t cbReply = 0; + const void *pvData = xpc_dictionary_get_data(hObjReply, "reply", &cbReply); + AssertRelease(cbReply == cbReq); + memcpy(pReqHdr, pvData, cbReq); + xpc_release(hObjReply); + + return rc; +# endif + } + else +#else + RT_NOREF(pThis); +#endif + return SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, uOperation, 0, pReqHdr); +} + + +#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_INTNET_SERVICE_IN_R3) +/** + * Calls the internal networking switch service living in either R0 or in another R3 process. + * + * @returns VBox status code. + * @param pThis The internal network driver instance data. + * @param uOperation The operation to execute. + * @param pReqHdr Pointer to the request header. + */ +static int intnetR3IfCallSvcAsync(PINTNETIFCTXINT pThis, uint32_t uOperation, PSUPVMMR0REQHDR pReqHdr) +{ + if (pThis->fIntNetR3Svc) + { + xpc_object_t hObj = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(hObj, "req-id", uOperation); + xpc_dictionary_set_data(hObj, "req", pReqHdr, pReqHdr->cbReq); + xpc_connection_send_message(pThis->hXpcCon, hObj); + return VINF_SUCCESS; + } + else + return SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, uOperation, 0, pReqHdr); +} +#endif + + +/** + * Map the ring buffer pointer into this process R3 address space. + * + * @returns VBox status code. + * @param pThis The internal network driver instance data. + */ +static int intnetR3IfMapBufferPointers(PINTNETIFCTXINT pThis) +{ + int rc = VINF_SUCCESS; + + INTNETIFGETBUFFERPTRSREQ GetBufferPtrsReq; + GetBufferPtrsReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + GetBufferPtrsReq.Hdr.cbReq = sizeof(GetBufferPtrsReq); + GetBufferPtrsReq.pSession = pThis->pSupDrvSession; + GetBufferPtrsReq.hIf = pThis->hIf; + GetBufferPtrsReq.pRing3Buf = NULL; + GetBufferPtrsReq.pRing0Buf = NIL_RTR0PTR; + +#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3) + if (pThis->fIntNetR3Svc) + { +#if defined(RT_OS_DARWIN) + xpc_object_t hObj = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(hObj, "req-id", VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS); + xpc_dictionary_set_data(hObj, "req", &GetBufferPtrsReq, sizeof(GetBufferPtrsReq)); + xpc_object_t hObjReply = xpc_connection_send_message_with_reply_sync(pThis->hXpcCon, hObj); + xpc_release(hObj); + + rc = (int)xpc_dictionary_get_int64(hObjReply, "rc"); + if (RT_SUCCESS(rc)) + { + /* Get the shared memory object. */ + xpc_object_t hObjShMem = xpc_dictionary_get_value(hObjReply, "buf-ptr"); + size_t cbMem = xpc_shmem_map(hObjShMem, (void **)&pThis->pBuf); + if (!cbMem) + rc = VERR_NO_MEMORY; + else + pThis->cbBuf = cbMem; + } + xpc_release(hObjReply); +#endif + } + else +#endif + { + rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS, 0 /*u64Arg*/, &GetBufferPtrsReq.Hdr); + if (RT_SUCCESS(rc)) + { + AssertRelease(RT_VALID_PTR(GetBufferPtrsReq.pRing3Buf)); + pThis->pBuf = GetBufferPtrsReq.pRing3Buf; + } + } + + return rc; +} + + +static void intnetR3IfClose(PINTNETIFCTXINT pThis) +{ + if (pThis->hIf != INTNET_HANDLE_INVALID) + { + INTNETIFCLOSEREQ CloseReq; + CloseReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + CloseReq.Hdr.cbReq = sizeof(CloseReq); + CloseReq.pSession = pThis->pSupDrvSession; + CloseReq.hIf = pThis->hIf; + + pThis->hIf = INTNET_HANDLE_INVALID; + int rc = intnetR3IfCallSvc(pThis, VMMR0_DO_INTNET_IF_CLOSE, &CloseReq.Hdr); + AssertRC(rc); + } +} + + +DECLHIDDEN(int) IntNetR3IfCreate(PINTNETIFCTX phIfCtx, const char *pszNetwork) +{ + return IntNetR3IfCreateEx(phIfCtx, pszNetwork, kIntNetTrunkType_WhateverNone, "", + _128K /*cbSend*/, _256K /*cbRecv*/, 0 /*fFlags*/); +} + + +DECLHIDDEN(int) IntNetR3IfCreateEx(PINTNETIFCTX phIfCtx, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType, + const char *pszTrunk, uint32_t cbSend, uint32_t cbRecv, uint32_t fFlags) +{ + AssertPtrReturn(phIfCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pszNetwork, VERR_INVALID_POINTER); + AssertPtrReturn(pszTrunk, VERR_INVALID_POINTER); + + PSUPDRVSESSION pSession = NIL_RTR0PTR; + int rc = SUPR3Init(&pSession); + if (RT_SUCCESS(rc)) + { + PINTNETIFCTXINT pThis = (PINTNETIFCTXINT)RTMemAllocZ(sizeof(*pThis)); + if (RT_LIKELY(pThis)) + { + pThis->pSupDrvSession = pSession; +#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3) + pThis->hEvtRecv = NIL_RTSEMEVENT; +#endif + + /* Driverless operation needs support for running the internal network switch using IPC. */ + if (SUPR3IsDriverless()) + { +#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3) +# if defined(RT_OS_DARWIN) + xpc_connection_t hXpcCon = xpc_connection_create(INTNET_R3_SVC_NAME, NULL); + xpc_connection_set_event_handler(hXpcCon, ^(xpc_object_t hObj) { + if (xpc_get_type(hObj) == XPC_TYPE_ERROR) + { + /** @todo Error handling - reconnecting. */ + } + else + { + /* Out of band messages should only come when there is something to receive. */ + RTSemEventSignal(pThis->hEvtRecv); + } + }); + + xpc_connection_resume(hXpcCon); + pThis->hXpcCon = hXpcCon; +# endif + pThis->fIntNetR3Svc = true; + rc = RTSemEventCreate(&pThis->hEvtRecv); +#else + rc = VERR_SUP_DRIVERLESS; +#endif + } + else + { + /* Need to load VMMR0.r0 containing the network switching code. */ + char szPathVMMR0[RTPATH_MAX]; + + rc = RTPathExecDir(szPathVMMR0, sizeof(szPathVMMR0)); + if (RT_SUCCESS(rc)) + { + rc = RTPathAppend(szPathVMMR0, sizeof(szPathVMMR0), "VMMR0.r0"); + if (RT_SUCCESS(rc)) + rc = SUPR3LoadVMM(szPathVMMR0, /* :pErrInfo */ NULL); + } + } + + if (RT_SUCCESS(rc)) + { + /* Open the interface. */ + INTNETOPENREQ OpenReq; + RT_ZERO(OpenReq); + + OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + OpenReq.Hdr.cbReq = sizeof(OpenReq); + OpenReq.pSession = pThis->pSupDrvSession; + OpenReq.enmTrunkType = enmTrunkType; + OpenReq.fFlags = fFlags; + OpenReq.cbSend = cbSend; + OpenReq.cbRecv = cbRecv; + OpenReq.hIf = INTNET_HANDLE_INVALID; + + rc = RTStrCopy(OpenReq.szNetwork, sizeof(OpenReq.szNetwork), pszNetwork); + if (RT_SUCCESS(rc)) + rc = RTStrCopy(OpenReq.szTrunk, sizeof(OpenReq.szTrunk), pszTrunk); + if (RT_SUCCESS(rc)) + { + rc = intnetR3IfCallSvc(pThis, VMMR0_DO_INTNET_OPEN, &OpenReq.Hdr); + if (RT_SUCCESS(rc)) + { + pThis->hIf = OpenReq.hIf; + + rc = intnetR3IfMapBufferPointers(pThis); + if (RT_SUCCESS(rc)) + { + *phIfCtx = pThis; + return VINF_SUCCESS; + } + } + + intnetR3IfClose(pThis); + } + } + +#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3) + if (pThis->fIntNetR3Svc) + { +# if defined(RT_OS_DARWIN) + if (pThis->hXpcCon) + xpc_connection_cancel(pThis->hXpcCon); + pThis->hXpcCon = NULL; +# endif + + if (pThis->hEvtRecv != NIL_RTSEMEVENT) + RTSemEventDestroy(pThis->hEvtRecv); + } +#endif + + RTMemFree(pThis); + } + + SUPR3Term(); + } + + return rc; +} + + +DECLHIDDEN(int) IntNetR3IfDestroy(INTNETIFCTX hIfCtx) +{ + PINTNETIFCTXINT pThis = hIfCtx; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + + intnetR3IfClose(pThis); + +#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3) + if (pThis->fIntNetR3Svc) + { +# if defined(RT_OS_DARWIN) + /* Unmap the shared buffer. */ + munmap(pThis->pBuf, pThis->cbBuf); + xpc_connection_cancel(pThis->hXpcCon); + pThis->hXpcCon = NULL; +# endif + RTSemEventDestroy(pThis->hEvtRecv); + pThis->fIntNetR3Svc = false; + } +#endif + + RTMemFree(pThis); + return VINF_SUCCESS; +} + + +DECLHIDDEN(int) IntNetR3IfQueryBufferPtr(INTNETIFCTX hIfCtx, PINTNETBUF *ppIfBuf) +{ + PINTNETIFCTXINT pThis = hIfCtx; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertPtrReturn(ppIfBuf, VERR_INVALID_POINTER); + + *ppIfBuf = pThis->pBuf; + return VINF_SUCCESS; +} + + +DECLHIDDEN(int) IntNetR3IfSetActive(INTNETIFCTX hIfCtx, bool fActive) +{ + PINTNETIFCTXINT pThis = hIfCtx; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + + INTNETIFSETACTIVEREQ Req; + Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + Req.Hdr.cbReq = sizeof(Req); + Req.pSession = pThis->pSupDrvSession; + Req.hIf = pThis->hIf; + Req.fActive = fActive; + return intnetR3IfCallSvc(pThis, VMMR0_DO_INTNET_IF_SET_ACTIVE, &Req.Hdr); +} + + +DECLHIDDEN(int) IntNetR3IfSetPromiscuous(INTNETIFCTX hIfCtx, bool fPromiscuous) +{ + PINTNETIFCTXINT pThis = hIfCtx; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + + INTNETIFSETPROMISCUOUSMODEREQ Req; + Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + Req.Hdr.cbReq = sizeof(Req); + Req.pSession = pThis->pSupDrvSession; + Req.hIf = pThis->hIf; + Req.fPromiscuous = fPromiscuous; + return intnetR3IfCallSvc(pThis, VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE, &Req.Hdr); +} + + +DECLHIDDEN(int) IntNetR3IfSend(INTNETIFCTX hIfCtx) +{ + PINTNETIFCTXINT pThis = hIfCtx; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + + INTNETIFSENDREQ Req; + Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + Req.Hdr.cbReq = sizeof(Req); + Req.pSession = pThis->pSupDrvSession; + Req.hIf = pThis->hIf; + return intnetR3IfCallSvc(pThis, VMMR0_DO_INTNET_IF_SEND, &Req.Hdr); +} + + +DECLHIDDEN(int) IntNetR3IfWait(INTNETIFCTX hIfCtx, uint32_t cMillies) +{ + PINTNETIFCTXINT pThis = hIfCtx; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + + int rc = VINF_SUCCESS; + INTNETIFWAITREQ WaitReq; + WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + WaitReq.Hdr.cbReq = sizeof(WaitReq); + WaitReq.pSession = pThis->pSupDrvSession; + WaitReq.hIf = pThis->hIf; + WaitReq.cMillies = cMillies; +#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3) + if (pThis->fIntNetR3Svc) + { + /* Send an asynchronous message. */ + rc = intnetR3IfCallSvcAsync(pThis, VMMR0_DO_INTNET_IF_WAIT, &WaitReq.Hdr); + if (RT_SUCCESS(rc)) + { + /* Wait on the receive semaphore. */ + rc = RTSemEventWait(pThis->hEvtRecv, cMillies); + } + } + else +#endif + rc = intnetR3IfCallSvc(pThis, VMMR0_DO_INTNET_IF_WAIT, &WaitReq.Hdr); + + return rc; +} + + +DECLHIDDEN(int) IntNetR3IfWaitAbort(INTNETIFCTX hIfCtx) +{ + PINTNETIFCTXINT pThis = hIfCtx; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + + INTNETIFABORTWAITREQ AbortWaitReq; + AbortWaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + AbortWaitReq.Hdr.cbReq = sizeof(AbortWaitReq); + AbortWaitReq.pSession = pThis->pSupDrvSession; + AbortWaitReq.hIf = pThis->hIf; + AbortWaitReq.fNoMoreWaits = true; + return intnetR3IfCallSvc(pThis, VMMR0_DO_INTNET_IF_ABORT_WAIT, &AbortWaitReq.Hdr); +} + + +DECLHIDDEN(int) IntNetR3IfPumpPkts(INTNETIFCTX hIfCtx, PFNINPUT pfnInput, void *pvUser, + PFNINPUTGSO pfnInputGso, void *pvUserGso) +{ + PINTNETIFCTXINT pThis = hIfCtx; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertPtrReturn(pfnInput, VERR_INVALID_POINTER); + + int rc; + for (;;) + { + rc = IntNetR3IfWait(hIfCtx, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc) || rc == VERR_INTERRUPTED || rc == VERR_TIMEOUT) + { + PCINTNETHDR pHdr = IntNetRingGetNextFrameToRead(&pThis->pBuf->Recv); + while (pHdr) + { + const uint8_t u8Type = pHdr->u8Type; + void *pvSegFrame; + uint32_t cbSegFrame; + + if (u8Type == INTNETHDR_TYPE_FRAME) + { + pvSegFrame = IntNetHdrGetFramePtr(pHdr, pThis->pBuf); + cbSegFrame = pHdr->cbFrame; + + /* pass the frame to the user callback */ + pfnInput(pvUser, pvSegFrame, cbSegFrame); + } + else if (u8Type == INTNETHDR_TYPE_GSO) + { + size_t cbGso = pHdr->cbFrame; + size_t cbFrame = cbGso - sizeof(PDMNETWORKGSO); + + PCPDMNETWORKGSO pcGso = IntNetHdrGetGsoContext(pHdr, pThis->pBuf); + if (PDMNetGsoIsValid(pcGso, cbGso, cbFrame)) + { + if (pfnInputGso != NULL) + { + /* pass the frame to the user GSO input callback if set */ + pfnInputGso(pvUserGso, pcGso, (uint32_t)cbFrame); + } + else + { + const uint32_t cSegs = PDMNetGsoCalcSegmentCount(pcGso, cbFrame); + for (uint32_t i = 0; i < cSegs; ++i) + { + uint8_t abHdrScratch[256]; + pvSegFrame = PDMNetGsoCarveSegmentQD(pcGso, (uint8_t *)(pcGso + 1), cbFrame, + abHdrScratch, + i, cSegs, + &cbSegFrame); + + /* pass carved frames to the user input callback */ + pfnInput(pvUser, pvSegFrame, (uint32_t)cbSegFrame); + } + } + } + } + + /* advance to the next input frame */ + IntNetRingSkipFrame(&pThis->pBuf->Recv); + pHdr = IntNetRingGetNextFrameToRead(&pThis->pBuf->Recv); + } + } + else + break; + } + return rc; +} + + +DECLHIDDEN(int) IntNetR3IfQueryOutputFrame(INTNETIFCTX hIfCtx, uint32_t cbFrame, PINTNETFRAME pFrame) +{ + PINTNETIFCTXINT pThis = hIfCtx; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + + return IntNetRingAllocateFrame(&pThis->pBuf->Send, cbFrame, &pFrame->pHdr, &pFrame->pvFrame); +} + + +DECLHIDDEN(int) IntNetR3IfOutputFrameCommit(INTNETIFCTX hIfCtx, PCINTNETFRAME pFrame) +{ + PINTNETIFCTXINT pThis = hIfCtx; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + + IntNetRingCommitFrame(&pThis->pBuf->Send, pFrame->pHdr); + return IntNetR3IfSend(hIfCtx); +} diff --git a/src/VBox/NetworkServices/NetLib/IntNetIf.h b/src/VBox/NetworkServices/NetLib/IntNetIf.h new file mode 100644 index 00000000..7e8c0063 --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/IntNetIf.h @@ -0,0 +1,112 @@ +/* $Id: IntNetIf.h $ */ +/** @file + * IntNetIf - Convenience class implementing an IntNet connection. + */ + +/* + * Copyright (C) 2009-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 + */ + +#ifndef VBOX_INCLUDED_SRC_NetLib_IntNetIf_h +#define VBOX_INCLUDED_SRC_NetLib_IntNetIf_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include + +#include +#include + +#include +#include +#include + + + +/** + * Low-level internal network access helpers to hide away the different variants (R0 SUP or R3 XPC on macOS). + */ +/** Internal networking interface context handle. */ +typedef struct INTNETIFCTXINT *INTNETIFCTX; +/** Pointer to an internal networking interface context handle. */ +typedef INTNETIFCTX *PINTNETIFCTX; + +/** + * User input callback function. + * + * @param pvUser The user specified argument. + * @param pvFrame The pointer to the frame data. + * @param cbFrame The length of the frame data. + */ +typedef DECLCALLBACKTYPE(void, FNINPUT,(void *pvUser, void *pvFrame, uint32_t cbFrame)); + +/** Pointer to the user input callback function. */ +typedef FNINPUT *PFNINPUT; + +/** + * User GSO input callback function. + * + * @param pvUser The user specified argument. + * @param pcGso The pointer to the GSO context. + * @param cbFrame The length of the GSO data. + */ +typedef DECLCALLBACKTYPE(void, FNINPUTGSO,(void *pvUser, PCPDMNETWORKGSO pcGso, uint32_t cbFrame)); + +/** Pointer to the user GSO input callback function. */ +typedef FNINPUTGSO *PFNINPUTGSO; + + +/** + * An output frame in the send ring buffer. + * + * Obtained with IntNetR3IfCtxQueryOutputFrame(). Caller should copy frame + * contents to pvFrame and pass the frame structure to IntNetR3IfCtxOutputFrameCommit() + * to be sent to the network. + */ +typedef struct INTNETFRAME +{ + /** The intrnal network frame header. */ + PINTNETHDR pHdr; + /** The actual frame data. */ + void *pvFrame; +} INTNETFRAME; +typedef INTNETFRAME *PINTNETFRAME; +typedef const INTNETFRAME *PCINTNETFRAME; + + +DECLHIDDEN(int) IntNetR3IfCreate(PINTNETIFCTX phIfCtx, const char *pszNetwork); +DECLHIDDEN(int) IntNetR3IfCreateEx(PINTNETIFCTX phIfCtx, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType, + const char *pszTrunk, uint32_t cbSend, uint32_t cbRecv, uint32_t fFlags); +DECLHIDDEN(int) IntNetR3IfDestroy(INTNETIFCTX hIfCtx); +DECLHIDDEN(int) IntNetR3IfQueryBufferPtr(INTNETIFCTX hIfCtx, PINTNETBUF *ppIfBuf); +DECLHIDDEN(int) IntNetR3IfSetActive(INTNETIFCTX hIfCtx, bool fActive); +DECLHIDDEN(int) IntNetR3IfSetPromiscuous(INTNETIFCTX hIfCtx, bool fPromiscuous); +DECLHIDDEN(int) IntNetR3IfSend(INTNETIFCTX hIfCtx); +DECLHIDDEN(int) IntNetR3IfWait(INTNETIFCTX hIfCtx, uint32_t cMillies); +DECLHIDDEN(int) IntNetR3IfWaitAbort(INTNETIFCTX hIfCtx); + +DECLHIDDEN(int) IntNetR3IfPumpPkts(INTNETIFCTX hIfCtx, PFNINPUT pfnInput, void *pvUser, + PFNINPUTGSO pfnInputGso, void *pvUserGso); +DECLHIDDEN(int) IntNetR3IfQueryOutputFrame(INTNETIFCTX hIfCtx, uint32_t cbFrame, PINTNETFRAME pFrame); +DECLHIDDEN(int) IntNetR3IfOutputFrameCommit(INTNETIFCTX hIfCtx, PCINTNETFRAME pFrame); + +#endif /* !VBOX_INCLUDED_SRC_NetLib_IntNetIf_h */ diff --git a/src/VBox/NetworkServices/NetLib/Makefile.kup b/src/VBox/NetworkServices/NetLib/Makefile.kup new file mode 100644 index 00000000..e69de29b diff --git a/src/VBox/NetworkServices/NetLib/VBoxNetARP.cpp b/src/VBox/NetworkServices/NetLib/VBoxNetARP.cpp new file mode 100644 index 00000000..510685c9 --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxNetARP.cpp @@ -0,0 +1,165 @@ +/* $Id: VBoxNetARP.cpp $ */ +/** @file + * VBoxNetARP - IntNet ARP Client Routines. + */ + +/* + * Copyright (C) 2009-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_DEFAULT +#include "VBoxNetLib.h" +#include +#include +#include + + +/** + * Deal with ARP queries. + * + * @returns true if ARP. + * + * @param pSession The support driver session. + * @param hIf The internal network interface handle. + * @param pBuf The internal network interface buffer. + * @param pMacAddr Our MAC address. + * @param IPv4Addr Our IPv4 address. + */ +bool VBoxNetArpHandleIt(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf, PCRTMAC pMacAddr, RTNETADDRIPV4 IPv4Addr) +{ + /* + * Valid IntNet Ethernet frame? Skip GSO, no ARP in there. + */ + PCINTNETHDR pHdr = IntNetRingGetNextFrameToRead(&pBuf->Recv); + if ( !pHdr + || pHdr->u8Type != INTNETHDR_TYPE_FRAME) + return false; + + size_t cbFrame = pHdr->cbFrame; + const void *pvFrame = IntNetHdrGetFramePtr(pHdr, pBuf); + PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame; + + /* + * Arp frame? + */ + if (pEthHdr->EtherType != RT_H2N_U16_C(RTNET_ETHERTYPE_ARP)) + return false; + if ( ( pEthHdr->DstMac.au16[0] != 0xffff + || pEthHdr->DstMac.au16[1] != 0xffff + || pEthHdr->DstMac.au16[2] != 0xffff) + && ( pEthHdr->DstMac.au16[0] != pMacAddr->au16[0] + || pEthHdr->DstMac.au16[1] != pMacAddr->au16[1] + || pEthHdr->DstMac.au16[2] != pMacAddr->au16[2]) + ) + return false; + if (cbFrame < sizeof(RTNETARPIPV4) + sizeof(RTNETETHERHDR)) + return false; + + PCRTNETARPHDR pArpHdr = (PCRTNETARPHDR)(pEthHdr + 1); + if (pArpHdr->ar_htype != RT_H2N_U16_C(RTNET_ARP_ETHER)) + return false; + if (pArpHdr->ar_hlen != sizeof(RTMAC)) + return false; + if (pArpHdr->ar_ptype != RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4)) + return false; + if (pArpHdr->ar_plen != sizeof(RTNETADDRIPV4)) + return false; + + /* It's ARP, alright. Anything we need to do something about. */ + PCRTNETARPIPV4 pArp = (PCRTNETARPIPV4)pArpHdr; + switch (pArp->Hdr.ar_oper) + { + case RT_H2N_U16_C(RTNET_ARPOP_REQUEST): + case RT_H2N_U16_C(RTNET_ARPOP_REVREQUEST): + case RT_H2N_U16_C(RTNET_ARPOP_INVREQUEST): + break; + default: + return true; + } + + /* + * Deal with the queries. + */ + RTNETARPIPV4 Reply; + switch (pArp->Hdr.ar_oper) + { + /* 'Who has ar_tpa? Tell ar_spa.' */ + case RT_H2N_U16_C(RTNET_ARPOP_REQUEST): + if (pArp->ar_tpa.u != IPv4Addr.u) + return true; + Reply.Hdr.ar_oper = RT_H2N_U16_C(RTNET_ARPOP_REPLY); + break; + + case RT_H2N_U16_C(RTNET_ARPOP_REVREQUEST): + if ( pArp->ar_tha.au16[0] != pMacAddr->au16[0] + || pArp->ar_tha.au16[1] != pMacAddr->au16[1] + || pArp->ar_tha.au16[2] != pMacAddr->au16[2]) + return true; + Reply.Hdr.ar_oper = RT_H2N_U16_C(RTNET_ARPOP_REVREPLY); + break; + + case RT_H2N_U16_C(RTNET_ARPOP_INVREQUEST): + /** @todo RTNET_ARPOP_INVREQUEST */ + return true; + //Reply.Hdr.ar_oper = RT_H2N_U16_C(RTNET_ARPOP_INVREPLY); + //break; + } + + /* + * Complete the reply and send it. + */ + Reply.Hdr.ar_htype = RT_H2N_U16_C(RTNET_ARP_ETHER); + Reply.Hdr.ar_ptype = RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4); + Reply.Hdr.ar_hlen = sizeof(RTMAC); + Reply.Hdr.ar_plen = sizeof(RTNETADDRIPV4); + Reply.ar_sha = *pMacAddr; + Reply.ar_spa = IPv4Addr; + Reply.ar_tha = pArp->ar_sha; + Reply.ar_tpa = pArp->ar_spa; + + + RTNETETHERHDR EthHdr; + EthHdr.DstMac = pArp->ar_sha; + EthHdr.SrcMac = *pMacAddr; + EthHdr.EtherType = RT_H2N_U16_C(RTNET_ETHERTYPE_ARP); + + uint8_t abTrailer[60 - sizeof(Reply) - sizeof(EthHdr)]; + RT_ZERO(abTrailer); + + INTNETSEG aSegs[3]; + aSegs[0].cb = sizeof(EthHdr); + aSegs[0].pv = &EthHdr; + + aSegs[1].pv = &Reply; + aSegs[1].cb = sizeof(Reply); + + aSegs[2].pv = &abTrailer[0]; + aSegs[2].cb = sizeof(abTrailer); + + VBoxNetIntIfSend(pSession, hIf, pBuf, RT_ELEMENTS(aSegs), &aSegs[0], true /* fFlush */); + + return true; +} + diff --git a/src/VBox/NetworkServices/NetLib/VBoxNetBaseService.cpp b/src/VBox/NetworkServices/NetLib/VBoxNetBaseService.cpp new file mode 100644 index 00000000..1700c0d1 --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxNetBaseService.cpp @@ -0,0 +1,858 @@ +/* $Id: VBoxNetBaseService.cpp $ */ +/** @file + * VBoxNetBaseService - common services for VBoxNetDHCP and VBoxNetNAT. + */ + +/* + * Copyright (C) 2009-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_NET_SERVICE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include /* must come before getopt.h. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "VBoxNetLib.h" +#include "VBoxNetBaseService.h" + +#ifdef RT_OS_WINDOWS /* WinMain */ +# include +# include +#endif + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +struct VBoxNetBaseService::Data +{ + Data(const std::string& aServiceName, const std::string& aNetworkName): + m_ServiceName(aServiceName), + m_NetworkName(aNetworkName), + m_enmTrunkType(kIntNetTrunkType_WhateverNone), + m_pSession(NIL_RTR0PTR), + m_cbSendBuf(128 * _1K), + m_cbRecvBuf(256 * _1K), + m_hIf(INTNET_HANDLE_INVALID), + m_pIfBuf(NULL), + m_cVerbosity(0), + m_fNeedMain(false), + m_EventQ(NULL), + m_hThrRecv(NIL_RTTHREAD), + fShutdown(false) + { + int rc = RTCritSectInit(&m_csThis); + AssertRC(rc); + }; + + std::string m_ServiceName; + std::string m_NetworkName; + std::string m_TrunkName; + INTNETTRUNKTYPE m_enmTrunkType; + + RTMAC m_MacAddress; + RTNETADDRIPV4 m_Ipv4Address; + RTNETADDRIPV4 m_Ipv4Netmask; + + PSUPDRVSESSION m_pSession; + uint32_t m_cbSendBuf; + uint32_t m_cbRecvBuf; + INTNETIFHANDLE m_hIf; /**< The handle to the network interface. */ + PINTNETBUF m_pIfBuf; /**< Interface buffer. */ + + std::vector m_vecOptionDefs; + + int32_t m_cVerbosity; + + /* cs for syncing */ + RTCRITSECT m_csThis; + + /* Controls whether service will connect SVC for runtime needs */ + bool m_fNeedMain; + /* Event Queue */ + com::NativeEventQueue *m_EventQ; + + /** receiving thread, used only if main is used */ + RTTHREAD m_hThrRecv; + + bool fShutdown; + static DECLCALLBACK(int) recvLoop(RTTHREAD, void *); +}; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/* Commonly used options for network configuration */ +static RTGETOPTDEF g_aGetOptDef[] = +{ + { "--name", 'N', RTGETOPT_REQ_STRING }, + { "--network", 'n', RTGETOPT_REQ_STRING }, + { "--trunk-name", 't', RTGETOPT_REQ_STRING }, + { "--trunk-type", 'T', RTGETOPT_REQ_STRING }, + { "--mac-address", 'a', RTGETOPT_REQ_MACADDR }, + { "--ip-address", 'i', RTGETOPT_REQ_IPV4ADDR }, + { "--netmask", 'm', RTGETOPT_REQ_IPV4ADDR }, + { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, + { "--need-main", 'M', RTGETOPT_REQ_BOOL }, +}; + + +DECLCALLBACK(int) VBoxNetBaseService::Data::recvLoop(RTTHREAD, void *pvUser) +{ + VBoxNetBaseService *pThis = static_cast(pvUser); + + HRESULT hrc = com::Initialize(); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + + pThis->doReceiveLoop(); + + return VINF_SUCCESS; +} + + +VBoxNetBaseService::VBoxNetBaseService(const std::string& aName, const std::string& aNetworkName):m(NULL) +{ + m = new VBoxNetBaseService::Data(aName, aNetworkName); + + for(unsigned int i = 0; i < RT_ELEMENTS(g_aGetOptDef); ++i) + m->m_vecOptionDefs.push_back(&g_aGetOptDef[i]); +} + + +VBoxNetBaseService::~VBoxNetBaseService() +{ + /* + * Close the interface connection. + */ + if (m) + { + shutdown(); + if (m->m_hIf != INTNET_HANDLE_INVALID) + { + INTNETIFCLOSEREQ CloseReq; + CloseReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + CloseReq.Hdr.cbReq = sizeof(CloseReq); + CloseReq.pSession = m->m_pSession; + CloseReq.hIf = m->m_hIf; + m->m_hIf = INTNET_HANDLE_INVALID; + int rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_CLOSE, 0, &CloseReq.Hdr); + AssertRC(rc); + } + + if (m->m_pSession != NIL_RTR0PTR) + { + SUPR3Term(false /*fForced*/); + m->m_pSession = NIL_RTR0PTR; + } + + RTCritSectDelete(&m->m_csThis); + + delete m; + m = NULL; + } +} + + +int VBoxNetBaseService::init() +{ + if (isMainNeeded()) + { + HRESULT hrc = com::Initialize(); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + + hrc = virtualboxClient.createInprocObject(CLSID_VirtualBoxClient); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + + hrc = virtualboxClient->COMGETTER(VirtualBox)(virtualbox.asOutParam()); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + } + + return VINF_SUCCESS; +} + + +bool VBoxNetBaseService::isMainNeeded() const +{ + return m->m_fNeedMain; +} + + +int VBoxNetBaseService::run() +{ + /** + * If the child class needs Main we start the receving thread which calls + * doReceiveLoop and enter to event polling loop. For other clients we do + * receiving on the current (main) thread. + */ + if (isMainNeeded()) + return startReceiveThreadAndEnterEventLoop(); + + doReceiveLoop(); + return VINF_SUCCESS; +} + +/** + * Parse the arguments. + * + * @returns 0 on success, fully bitched exit code on failure. + * + * @param argc Argument count. + * @param argv Argument vector. + * + * @todo r=bird: The --help and --version options shall not return a + * non-zero exit code. So, this method need to grow some + * complexity. I'm to blame for that blunder :/ + */ +int VBoxNetBaseService::parseArgs(int argc, char **argv) +{ + + RTGETOPTSTATE State; + PRTGETOPTDEF paOptionArray = getOptionsPtr(); + int rc = RTGetOptInit(&State, argc, argv, paOptionArray, m->m_vecOptionDefs.size(), 0, 0 /*fFlags*/); + AssertRCReturn(rc, 49); +#if 0 + /* default initialization */ + m_enmTrunkType = kIntNetTrunkType_WhateverNone; +#endif + Log2(("BaseService: parseArgs enter\n")); + + for (;;) + { + RTGETOPTUNION Val; + rc = RTGetOpt(&State, &Val); + if (!rc) + break; + switch (rc) + { + case 'N': // --name + m->m_ServiceName = Val.psz; + break; + + case 'n': // --network + m->m_NetworkName = Val.psz; + break; + + case 't': //--trunk-name + m->m_TrunkName = Val.psz; + break; + + case 'T': //--trunk-type + if (!strcmp(Val.psz, "none")) + m->m_enmTrunkType = kIntNetTrunkType_None; + else if (!strcmp(Val.psz, "whatever")) + m->m_enmTrunkType = kIntNetTrunkType_WhateverNone; + else if (!strcmp(Val.psz, "netflt")) + m->m_enmTrunkType = kIntNetTrunkType_NetFlt; + else if (!strcmp(Val.psz, "netadp")) + m->m_enmTrunkType = kIntNetTrunkType_NetAdp; + else if (!strcmp(Val.psz, "srvnat")) + m->m_enmTrunkType = kIntNetTrunkType_SrvNat; + else + { + RTStrmPrintf(g_pStdErr, "Invalid trunk type '%s'\n", Val.psz); + return RTEXITCODE_SYNTAX; + } + break; + + case 'a': // --mac-address + m->m_MacAddress = Val.MacAddr; + break; + + case 'i': // --ip-address + m->m_Ipv4Address = Val.IPv4Addr; + break; + + case 'm': // --netmask + m->m_Ipv4Netmask = Val.IPv4Addr; + break; + + case 'v': // --verbose + m->m_cVerbosity++; + break; + + case 'V': // --version (missed) + RTPrintf("%sr%u\n", RTBldCfgVersion(), RTBldCfgRevision()); + return 1; /** @todo this exit code is wrong, of course. :/ */ + + case 'M': // --need-main + m->m_fNeedMain = true; + break; + + case 'h': // --help (missed) + RTPrintf("%s Version %sr%u\n" + "Copyright (C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n" + "\n" + "Usage: %s \n" + "\n" + "Options:\n", + RTProcShortName(), + RTBldCfgVersion(), + RTBldCfgRevision(), + RTProcShortName()); + for (unsigned int i = 0; i < m->m_vecOptionDefs.size(); i++) + RTPrintf(" -%c, %s\n", m->m_vecOptionDefs[i]->iShort, m->m_vecOptionDefs[i]->pszLong); + usage(); /* to print Service Specific usage */ + return 1; /** @todo this exit code is wrong, of course. :/ */ + + default: + { + int rc1 = parseOpt(rc, Val); + if (RT_FAILURE(rc1)) + { + RTEXITCODE rcExit = RTGetOptPrintError(rc, &Val); + RTPrintf("Use --help for more information.\n"); + return rcExit; + } + break; + } + } + } + + RTMemFree(paOptionArray); + return RTEXITCODE_SUCCESS; +} + + +int VBoxNetBaseService::tryGoOnline(void) +{ + /* + * Open the session, load ring-0 and issue the request. + */ + int rc = SUPR3Init(&m->m_pSession); + if (RT_FAILURE(rc)) + { + m->m_pSession = NIL_RTR0PTR; + LogRel(("VBoxNetBaseService: SUPR3Init -> %Rrc\n", rc)); + return rc; + } + + char szPath[RTPATH_MAX]; + rc = RTPathExecDir(szPath, sizeof(szPath) - sizeof("/VMMR0.r0")); + if (RT_FAILURE(rc)) + { + LogRel(("VBoxNetBaseService: RTPathExecDir -> %Rrc\n", rc)); + return rc; + } + + rc = SUPR3LoadVMM(strcat(szPath, "/VMMR0.r0"), NULL); + if (RT_FAILURE(rc)) + { + LogRel(("VBoxNetBaseService: SUPR3LoadVMM(\"%s\") -> %Rrc\n", szPath, rc)); + return rc; + } + + /* + * Create the open request. + */ + PINTNETBUF pBuf; + INTNETOPENREQ OpenReq; + OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + OpenReq.Hdr.cbReq = sizeof(OpenReq); + OpenReq.pSession = m->m_pSession; + RTStrCopy(OpenReq.szNetwork, sizeof(OpenReq.szNetwork), m->m_NetworkName.c_str()); + OpenReq.szNetwork[sizeof(OpenReq.szNetwork) - 1] = '\0'; + RTStrCopy(OpenReq.szTrunk, sizeof(OpenReq.szTrunk), m->m_TrunkName.c_str()); + OpenReq.szTrunk[sizeof(OpenReq.szTrunk) - 1] = '\0'; + OpenReq.enmTrunkType = m->m_enmTrunkType; + OpenReq.fFlags = 0; /** @todo check this */ + OpenReq.cbSend = m->m_cbSendBuf; + OpenReq.cbRecv = m->m_cbRecvBuf; + OpenReq.hIf = INTNET_HANDLE_INVALID; + + /* + * Issue the request. + */ + Log2(("attempting to open/create network \"%s\"...\n", OpenReq.szNetwork)); + rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_OPEN, 0, &OpenReq.Hdr); + if (RT_FAILURE(rc)) + { + Log2(("VBoxNetBaseService: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_OPEN,) failed, rc=%Rrc\n", rc)); + return rc; + } + m->m_hIf = OpenReq.hIf; + Log2(("successfully opened/created \"%s\" - hIf=%#x\n", OpenReq.szNetwork, m->m_hIf)); + + /* + * Get the ring-3 address of the shared interface buffer. + */ + INTNETIFGETBUFFERPTRSREQ GetBufferPtrsReq; + GetBufferPtrsReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + GetBufferPtrsReq.Hdr.cbReq = sizeof(GetBufferPtrsReq); + GetBufferPtrsReq.pSession = m->m_pSession; + GetBufferPtrsReq.hIf = m->m_hIf; + GetBufferPtrsReq.pRing3Buf = NULL; + GetBufferPtrsReq.pRing0Buf = NIL_RTR0PTR; + rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS, 0, &GetBufferPtrsReq.Hdr); + if (RT_FAILURE(rc)) + { + Log2(("VBoxNetBaseService: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS,) failed, rc=%Rrc\n", rc)); + return rc; + } + pBuf = GetBufferPtrsReq.pRing3Buf; + Log2(("pBuf=%p cbBuf=%d cbSend=%d cbRecv=%d\n", + pBuf, pBuf->cbBuf, pBuf->cbSend, pBuf->cbRecv)); + m->m_pIfBuf = pBuf; + + /* + * Activate the interface. + */ + INTNETIFSETACTIVEREQ ActiveReq; + ActiveReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + ActiveReq.Hdr.cbReq = sizeof(ActiveReq); + ActiveReq.pSession = m->m_pSession; + ActiveReq.hIf = m->m_hIf; + ActiveReq.fActive = true; + rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SET_ACTIVE, 0, &ActiveReq.Hdr); + if (RT_SUCCESS(rc)) + return 0; + + /* bail out */ + Log2(("VBoxNetBaseService: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE,) failed, rc=%Rrc\n", rc)); + + /* ignore this error */ + return VINF_SUCCESS; +} + + +void VBoxNetBaseService::shutdown(void) +{ + syncEnter(); + if (!m->fShutdown) + { + m->fShutdown = true; + if (m->m_hThrRecv != NIL_RTTHREAD) + { + int rc = abortWait(); + AssertRC(rc == VINF_SUCCESS || rc == VERR_SEM_DESTROYED); + rc = m->m_EventQ->interruptEventQueueProcessing(); + if (RT_SUCCESS(rc)) + { + rc = RTThreadWait(m->m_hThrRecv, 60000, NULL); + if (RT_FAILURE(rc)) + Log1WarningFunc(("RTThreadWait(%RTthrd) -> %Rrc\n", m->m_hThrRecv, rc)); + } + else + { + AssertMsgFailed(("interruptEventQueueProcessing() failed\n")); + RTThreadWait(m->m_hThrRecv , 0, NULL); + } + } + } + syncLeave(); +} + + +int VBoxNetBaseService::syncEnter() +{ + return RTCritSectEnter(&m->m_csThis); +} + + +int VBoxNetBaseService::syncLeave() +{ + return RTCritSectLeave(&m->m_csThis); +} + + +int VBoxNetBaseService::waitForIntNetEvent(int cMillis) +{ + INTNETIFWAITREQ WaitReq; + LogFlowFunc(("ENTER:cMillis: %d\n", cMillis)); + WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + WaitReq.Hdr.cbReq = sizeof(WaitReq); + WaitReq.pSession = m->m_pSession; + WaitReq.hIf = m->m_hIf; + WaitReq.cMillies = cMillis; + + int rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_WAIT, 0, &WaitReq.Hdr); + LogFlowFuncLeaveRC(rc); + return rc; +} + + +int VBoxNetBaseService::abortWait() +{ + INTNETIFABORTWAITREQ AbortReq; + LogFlowFunc(("ENTER:\n")); + AbortReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + AbortReq.Hdr.cbReq = sizeof(AbortReq); + AbortReq.pSession = m->m_pSession; + AbortReq.hIf = m->m_hIf; + AbortReq.fNoMoreWaits = true; + + int rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_ABORT_WAIT, 0, &AbortReq.Hdr); + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/* S/G API */ +int VBoxNetBaseService::sendBufferOnWire(PCINTNETSEG paSegs, size_t cSegs, size_t cbFrame) +{ + /* Allocate frame */ + PINTNETHDR pHdr = NULL; + uint8_t *pbFrame = NULL; + int rc = IntNetRingAllocateFrame(&m->m_pIfBuf->Send, (uint32_t)cbFrame, &pHdr, (void **)&pbFrame); + AssertRCReturn(rc, rc); + + /* Now we fill pvFrame with S/G above */ + size_t offFrame = 0; + for (size_t idxSeg = 0; idxSeg < cSegs; ++idxSeg) + { + memcpy(&pbFrame[offFrame], paSegs[idxSeg].pv, paSegs[idxSeg].cb); + offFrame += paSegs[idxSeg].cb; + } + + /* Commit */ + IntNetRingCommitFrameEx(&m->m_pIfBuf->Send, pHdr, cbFrame); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * forcible ask for send packet on the "wire" + */ +void VBoxNetBaseService::flushWire() +{ + INTNETIFSENDREQ SendReq; + SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + SendReq.Hdr.cbReq = sizeof(SendReq); + SendReq.pSession = m->m_pSession; + SendReq.hIf = m->m_hIf; + int rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SEND, 0, &SendReq.Hdr); + AssertRCReturnVoid(rc); + LogFlowFuncLeave(); +} + + +int VBoxNetBaseService::hlpUDPBroadcast(unsigned uSrcPort, unsigned uDstPort, + void const *pvData, size_t cbData) const +{ + return VBoxNetUDPBroadcast(m->m_pSession, m->m_hIf, m->m_pIfBuf, + m->m_Ipv4Address, &m->m_MacAddress, uSrcPort, + uDstPort, pvData, cbData); + +} + + +const std::string VBoxNetBaseService::getServiceName() const +{ + return m->m_ServiceName; +} + + +void VBoxNetBaseService::setServiceName(const std::string& aName) +{ + m->m_ServiceName = aName; +} + + +const std::string VBoxNetBaseService::getNetworkName() const +{ + return m->m_NetworkName; +} + + +void VBoxNetBaseService::setNetworkName(const std::string& aName) +{ + m->m_NetworkName = aName; +} + + +const RTMAC VBoxNetBaseService::getMacAddress() const +{ + return m->m_MacAddress; +} + + +void VBoxNetBaseService::setMacAddress(const RTMAC& aMac) +{ + m->m_MacAddress = aMac; +} + + +const RTNETADDRIPV4 VBoxNetBaseService::getIpv4Address() const +{ + return m->m_Ipv4Address; +} + + +void VBoxNetBaseService::setIpv4Address(const RTNETADDRIPV4& aAddress) +{ + m->m_Ipv4Address = aAddress; +} + + +const RTNETADDRIPV4 VBoxNetBaseService::getIpv4Netmask() const +{ + return m->m_Ipv4Netmask; +} + + +void VBoxNetBaseService::setIpv4Netmask(const RTNETADDRIPV4& aNetmask) +{ + m->m_Ipv4Netmask = aNetmask; +} + + +uint32_t VBoxNetBaseService::getSendBufSize() const +{ + return m->m_cbSendBuf; +} + + +void VBoxNetBaseService::setSendBufSize(uint32_t cbBuf) +{ + m->m_cbSendBuf = cbBuf; +} + + +uint32_t VBoxNetBaseService::getRecvBufSize() const +{ + return m->m_cbRecvBuf; +} + + +void VBoxNetBaseService::setRecvBufSize(uint32_t cbBuf) +{ + m->m_cbRecvBuf = cbBuf; +} + + +int32_t VBoxNetBaseService::getVerbosityLevel() const +{ + return m->m_cVerbosity; +} + + +void VBoxNetBaseService::setVerbosityLevel(int32_t aVerbosity) +{ + m->m_cVerbosity = aVerbosity; +} + + +void VBoxNetBaseService::addCommandLineOption(PCRTGETOPTDEF optDef) +{ + m->m_vecOptionDefs.push_back(optDef); +} + + +void VBoxNetBaseService::doReceiveLoop() +{ + int rc; + /* Well we're ready */ + PINTNETRINGBUF pRingBuf = &m->m_pIfBuf->Recv; + + for (;;) + { + /* + * Wait for a packet to become available. + */ + rc = waitForIntNetEvent(2000); + if (rc == VERR_SEM_DESTROYED) + break; + + if (RT_FAILURE(rc)) + { + if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED) + { + /* do we want interrupt anyone ??? */ + continue; + } + LogRel(("VBoxNetBaseService: waitForIntNetEvent returned %Rrc\n", rc)); + AssertRCReturnVoid(rc); + } + + /* + * Process the receive buffer. + */ + PCINTNETHDR pHdr; + while ((pHdr = IntNetRingGetNextFrameToRead(pRingBuf)) != NULL) + { + uint8_t const u8Type = pHdr->u8Type; + size_t cbFrame = pHdr->cbFrame; + switch (u8Type) + { + case INTNETHDR_TYPE_FRAME: + { + void *pvFrame = IntNetHdrGetFramePtr(pHdr, m->m_pIfBuf); + rc = processFrame(pvFrame, cbFrame); + if (RT_FAILURE(rc) && rc == VERR_IGNORED) + { + /* XXX: UDP + ARP for DHCP */ + VBOXNETUDPHDRS Hdrs; + size_t cb; + void *pv = VBoxNetUDPMatch(m->m_pIfBuf, RTNETIPV4_PORT_BOOTPS, &m->m_MacAddress, + VBOXNETUDP_MATCH_UNICAST + | VBOXNETUDP_MATCH_BROADCAST + | VBOXNETUDP_MATCH_CHECKSUM + | (m->m_cVerbosity > 2 ? VBOXNETUDP_MATCH_PRINT_STDERR : 0), + &Hdrs, &cb); + if (pv && cb) + processUDP(pv, cb); + else + VBoxNetArpHandleIt(m->m_pSession, m->m_hIf, m->m_pIfBuf, &m->m_MacAddress, m->m_Ipv4Address); + } + break; + } + case INTNETHDR_TYPE_GSO: + { + PCPDMNETWORKGSO pGso = IntNetHdrGetGsoContext(pHdr, m->m_pIfBuf); + rc = processGSO(pGso, cbFrame); + if (RT_FAILURE(rc) && rc == VERR_IGNORED) + break; + break; + } + + case INTNETHDR_TYPE_PADDING: + break; + + default: + break; + } + IntNetRingSkipFrame(&m->m_pIfBuf->Recv); + } /* loop */ + } +} + + +int VBoxNetBaseService::startReceiveThreadAndEnterEventLoop() +{ + AssertMsgReturn(isMainNeeded(), ("It's expected that we need Main"), VERR_INTERNAL_ERROR); + + /* start receiving thread */ + int rc = RTThreadCreate(&m->m_hThrRecv, /* thread handle*/ + &VBoxNetBaseService::Data::recvLoop, /* routine */ + this, /* user data */ + 128 * _1K, /* stack size */ + RTTHREADTYPE_IO, /* type */ + RTTHREADFLAGS_WAITABLE, /* flags */ + "RECV"); + AssertRCReturn(rc, rc); + + m->m_EventQ = com::NativeEventQueue::getMainEventQueue(); + AssertPtrReturn(m->m_EventQ, VERR_INTERNAL_ERROR); + + while (!m->fShutdown) + { + rc = m->m_EventQ->processEventQueue(RT_INDEFINITE_WAIT); + if (rc == VERR_INTERRUPTED) + { + LogFlow(("Event queue processing ended with rc=%Rrc\n", rc)); + break; + } + } + + return VINF_SUCCESS; +} + + +void VBoxNetBaseService::debugPrint(int32_t iMinLevel, bool fMsg, const char *pszFmt, ...) const +{ + if (iMinLevel <= m->m_cVerbosity) + { + va_list va; + va_start(va, pszFmt); + debugPrintV(iMinLevel, fMsg, pszFmt, va); + va_end(va); + } +} + + +/** + * Print debug message depending on the m_cVerbosity level. + * + * @param iMinLevel The minimum m_cVerbosity level for this message. + * @param fMsg Whether to dump parts for the current service message. + * @param pszFmt The message format string. + * @param va Optional arguments. + */ +void VBoxNetBaseService::debugPrintV(int iMinLevel, bool fMsg, const char *pszFmt, va_list va) const +{ + RT_NOREF(fMsg); + if (iMinLevel <= m->m_cVerbosity) + { + va_list vaCopy; /* This dude is *very* special, thus the copy. */ + va_copy(vaCopy, va); + RTStrmPrintf(g_pStdErr, "%s: %s: %N\n", + RTProcShortName(), + iMinLevel >= 2 ? "debug" : "info", + pszFmt, + &vaCopy); + va_end(vaCopy); + } +} + + +PRTGETOPTDEF VBoxNetBaseService::getOptionsPtr() +{ + PRTGETOPTDEF pOptArray = NULL; + pOptArray = (PRTGETOPTDEF)RTMemAlloc(sizeof(RTGETOPTDEF) * m->m_vecOptionDefs.size()); + if (!pOptArray) + return NULL; + for (unsigned int i = 0; i < m->m_vecOptionDefs.size(); ++i) + { + PCRTGETOPTDEF pOpt = m->m_vecOptionDefs[i]; + memcpy(&pOptArray[i], pOpt, sizeof(*pOpt)); + } + return pOptArray; +} diff --git a/src/VBox/NetworkServices/NetLib/VBoxNetBaseService.h b/src/VBox/NetworkServices/NetLib/VBoxNetBaseService.h new file mode 100644 index 00000000..4e2c83ce --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxNetBaseService.h @@ -0,0 +1,159 @@ +/* $Id: VBoxNetBaseService.h $ */ +/** @file + * VBoxNetUDP - IntNet Client Library. + */ + +/* + * Copyright (C) 2009-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 + */ + +#ifndef VBOX_INCLUDED_SRC_NetLib_VBoxNetBaseService_h +#define VBOX_INCLUDED_SRC_NetLib_VBoxNetBaseService_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include + + +class VBoxNetHlpUDPService +{ +public: + virtual ~VBoxNetHlpUDPService() { /* Make VC++ 19.2 happy. */ } + virtual int hlpUDPBroadcast(unsigned uSrcPort, unsigned uDstPort, void const *pvData, size_t cbData) const = 0; +}; + + +class VBoxNetLockee +{ +public: + virtual ~VBoxNetLockee() { /* Make VC++ 19.2 happy. */ } + virtual int syncEnter() = 0; + virtual int syncLeave() = 0; +}; + + +class VBoxNetALock +{ +public: + VBoxNetALock(VBoxNetLockee *a_lck) : m_lck(a_lck) + { + if (m_lck) + m_lck->syncEnter(); + } + + ~VBoxNetALock() + { + if (m_lck) + m_lck->syncLeave(); + } + +private: + VBoxNetLockee *m_lck; +}; + +# ifndef BASE_SERVICES_ONLY +class VBoxNetBaseService : public VBoxNetHlpUDPService, public VBoxNetLockee +{ +public: + VBoxNetBaseService(const std::string& aName, const std::string& aNetworkName); + virtual ~VBoxNetBaseService(); + int parseArgs(int argc, char **argv); + int tryGoOnline(void); + void shutdown(void); + int syncEnter(); + int syncLeave(); + int waitForIntNetEvent(int cMillis); + int abortWait(); + int sendBufferOnWire(PCINTNETSEG paSegs, size_t cSegs, size_t cbBuffer); + void flushWire(); + + virtual int hlpUDPBroadcast(unsigned uSrcPort, unsigned uDstPort, + void const *pvData, size_t cbData) const; + virtual void usage(void) = 0; + virtual int parseOpt(int rc, const RTGETOPTUNION& getOptVal) = 0; + virtual int processFrame(void *, size_t) = 0; + virtual int processGSO(PCPDMNETWORKGSO, size_t) = 0; + virtual int processUDP(void *, size_t) = 0; + + + virtual int init(void); + virtual int run(void); + virtual bool isMainNeeded() const; + +protected: + const std::string getServiceName() const; + void setServiceName(const std::string&); + + const std::string getNetworkName() const; + void setNetworkName(const std::string&); + + const RTMAC getMacAddress() const; + void setMacAddress(const RTMAC&); + + const RTNETADDRIPV4 getIpv4Address() const; + void setIpv4Address(const RTNETADDRIPV4&); + + const RTNETADDRIPV4 getIpv4Netmask() const; + void setIpv4Netmask(const RTNETADDRIPV4&); + + uint32_t getSendBufSize() const; + void setSendBufSize(uint32_t); + + uint32_t getRecvBufSize() const; + void setRecvBufSize(uint32_t); + + int32_t getVerbosityLevel() const; + void setVerbosityLevel(int32_t); + + void addCommandLineOption(PCRTGETOPTDEF); + + /** + * Print debug message depending on the m_cVerbosity level. + * + * @param iMinLevel The minimum m_cVerbosity level for this message. + * @param fMsg Whether to dump parts for the current DHCP message. + * @param pszFmt The message format string. + * @param ... Optional arguments. + */ + void debugPrint(int32_t iMinLevel, bool fMsg, const char *pszFmt, ...) const; + virtual void debugPrintV(int32_t iMinLevel, bool fMsg, const char *pszFmt, va_list va) const; + + private: + void doReceiveLoop(); + + /** starts receiving thread and enter event polling loop. */ + int startReceiveThreadAndEnterEventLoop(); + + protected: + /* VirtualBox instance */ + ComPtr virtualbox; + ComPtr virtualboxClient; + + private: + struct Data; + Data *m; + + private: + PRTGETOPTDEF getOptionsPtr(); +}; +# endif +#endif /* !VBOX_INCLUDED_SRC_NetLib_VBoxNetBaseService_h */ diff --git a/src/VBox/NetworkServices/NetLib/VBoxNetIntIf.cpp b/src/VBox/NetworkServices/NetLib/VBoxNetIntIf.cpp new file mode 100644 index 00000000..eaa02dfc --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxNetIntIf.cpp @@ -0,0 +1,150 @@ +/* $Id: VBoxNetIntIf.cpp $ */ +/** @file + * VBoxNetIntIf - IntNet Interface Client Routines. + */ + +/* + * Copyright (C) 2009-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_DEFAULT +#include "VBoxNetLib.h" +#include +#include +#include +#include +#include +#include + +#include + + + +/** + * Flushes the send buffer. + * + * @returns VBox status code. + * @param pSession The support driver session. + * @param hIf The interface handle to flush. + */ +int VBoxNetIntIfFlush(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf) +{ + INTNETIFSENDREQ SendReq; + SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC; + SendReq.Hdr.cbReq = sizeof(SendReq); + SendReq.pSession = pSession; + SendReq.hIf = hIf; + return SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SEND, 0, &SendReq.Hdr); +} + + +/** + * Copys the SG segments into the specified fram. + * + * @param pvFrame The frame buffer. + * @param cSegs The number of segments. + * @param paSegs The segments. + */ +static void vboxnetIntIfCopySG(void *pvFrame, size_t cSegs, PCINTNETSEG paSegs) +{ + uint8_t *pbDst = (uint8_t *)pvFrame; + for (size_t iSeg = 0; iSeg < cSegs; iSeg++) + { + memcpy(pbDst, paSegs[iSeg].pv, paSegs[iSeg].cb); + pbDst += paSegs[iSeg].cb; + } +} + + +/** + * Writes a frame packet to the buffer. + * + * @returns VBox status code. + * @param pBuf The buffer. + * @param pRingBuf The ring buffer to read from. + * @param cSegs The number of segments. + * @param paSegs The segments. + */ +int VBoxNetIntIfRingWriteFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, size_t cSegs, PCINTNETSEG paSegs) +{ + RT_NOREF(pBuf); + + /* + * Validate input. + */ + AssertPtr(pBuf); + AssertPtr(pRingBuf); + AssertPtr(paSegs); + Assert(cSegs > 0); + + /* + * Calc frame size. + */ + uint32_t cbFrame = 0; + for (size_t iSeg = 0; iSeg < cSegs; iSeg++) + cbFrame += paSegs[iSeg].cb; + Assert(cbFrame >= sizeof(RTMAC) * 2); + + /* + * Allocate a frame, copy the data and commit it. + */ + PINTNETHDR pHdr = NULL; + void *pvFrame = NULL; + int rc = IntNetRingAllocateFrame(pRingBuf, cbFrame, &pHdr, &pvFrame); + if (RT_SUCCESS(rc)) + { + vboxnetIntIfCopySG(pvFrame, cSegs, paSegs); + IntNetRingCommitFrame(pRingBuf, pHdr); + return VINF_SUCCESS; + } + + return rc; +} + + +/** + * Sends a frame + * + * @returns VBox status code. + * @param pSession The support driver session. + * @param hIf The interface handle. + * @param pBuf The interface buffer. + * @param cSegs The number of segments. + * @param paSegs The segments. + * @param fFlush Whether to flush the write. + */ +int VBoxNetIntIfSend(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf, + size_t cSegs, PCINTNETSEG paSegs, bool fFlush) +{ + int rc = VBoxNetIntIfRingWriteFrame(pBuf, &pBuf->Send, cSegs, paSegs); + if (rc == VERR_BUFFER_OVERFLOW) + { + VBoxNetIntIfFlush(pSession, hIf); + rc = VBoxNetIntIfRingWriteFrame(pBuf, &pBuf->Send, cSegs, paSegs); + } + if (RT_SUCCESS(rc) && fFlush) + rc = VBoxNetIntIfFlush(pSession, hIf); + return rc; +} diff --git a/src/VBox/NetworkServices/NetLib/VBoxNetLib.h b/src/VBox/NetworkServices/NetLib/VBoxNetLib.h new file mode 100644 index 00000000..5c92dacb --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxNetLib.h @@ -0,0 +1,82 @@ +/* $Id: VBoxNetLib.h $ */ +/** @file + * VBoxNetUDP - IntNet Client Library. + */ + +/* + * Copyright (C) 2009-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 + */ + +#ifndef VBOX_INCLUDED_SRC_NetLib_VBoxNetLib_h +#define VBOX_INCLUDED_SRC_NetLib_VBoxNetLib_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include +#include + +RT_C_DECLS_BEGIN + + +/** + * Header pointers optionally returned by VBoxNetUDPMatch. + */ +typedef struct VBOXNETUDPHDRS +{ + PCRTNETETHERHDR pEth; /**< Pointer to the ethernet header. */ + PCRTNETIPV4 pIpv4; /**< Pointer to the IPV4 header if IPV4 packet. */ + PCRTNETUDP pUdp; /**< Pointer to the UDP header. */ +} VBOXNETUDPHDRS; +/** Pointer to a VBOXNETUDPHDRS structure. */ +typedef VBOXNETUDPHDRS *PVBOXNETUDPHDRS; + + +/** @name VBoxNetUDPMatch flags. + * @{ */ +#define VBOXNETUDP_MATCH_UNICAST RT_BIT_32(0) +#define VBOXNETUDP_MATCH_BROADCAST RT_BIT_32(1) +#define VBOXNETUDP_MATCH_CHECKSUM RT_BIT_32(2) +#define VBOXNETUDP_MATCH_REQUIRE_CHECKSUM RT_BIT_32(3) +#define VBOXNETUDP_MATCH_PRINT_STDERR RT_BIT_32(31) +/** @} */ + +void * VBoxNetUDPMatch(PINTNETBUF pBuf, unsigned uDstPort, PCRTMAC pDstMac, uint32_t fFlags, PVBOXNETUDPHDRS pHdrs, size_t *pcb); +int VBoxNetUDPUnicast(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf, + RTNETADDRIPV4 SrcIPv4Addr, PCRTMAC SrcMacAddr, unsigned uSrcPort, + RTNETADDRIPV4 DstIPv4Addr, PCRTMAC DstMacAddr, unsigned uDstPort, + void const *pvData, size_t cbData); +int VBoxNetUDPBroadcast(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf, + RTNETADDRIPV4 SrcIPv4Addr, PCRTMAC SrcMacAddr, unsigned uSrcPort, + unsigned uDstPort, + void const *pvData, size_t cbData); + +bool VBoxNetArpHandleIt(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf, PCRTMAC pMacAddr, RTNETADDRIPV4 IPv4Addr); + +int VBoxNetIntIfFlush(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf); +int VBoxNetIntIfRingWriteFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, size_t cSegs, PCINTNETSEG paSegs); +int VBoxNetIntIfSend(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf, size_t cSegs, PCINTNETSEG paSegs, bool fFlush); + + +RT_C_DECLS_END + +#endif /* !VBOX_INCLUDED_SRC_NetLib_VBoxNetLib_h */ + diff --git a/src/VBox/NetworkServices/NetLib/VBoxNetPortForwardString.cpp b/src/VBox/NetworkServices/NetLib/VBoxNetPortForwardString.cpp new file mode 100644 index 00000000..c60d6860 --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxNetPortForwardString.cpp @@ -0,0 +1,382 @@ +/* $Id: VBoxNetPortForwardString.cpp $ */ +/** @file + * VBoxNetPortForwardString - Routines for managing port-forward strings. + */ + +/* + * 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 * +*********************************************************************************************************************************/ +#ifndef RT_OS_WINDOWS +# include +#else +# include +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "VBoxPortForwardString.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define PF_FIELD_SEPARATOR ':' +#define PF_ADDRESS_FIELD_STARTS '[' +#define PF_ADDRESS_FIELD_ENDS ']' + +#define PF_STR_FIELD_SEPARATOR ":" +#define PF_STR_ADDRESS_FIELD_STARTS "[" +#define PF_STR_ADDRESS_FIELD_ENDS "]" + + +static int netPfStrAddressParse(char *pszRaw, size_t cchRaw, + char *pszAddress, int cbAddress, + bool fEmptyAcceptable) +{ + size_t cchField = 0; + + AssertPtrReturn(pszRaw, -1); + AssertPtrReturn(pszAddress, -1); + AssertReturn(pszRaw[0] == PF_ADDRESS_FIELD_STARTS, -1); + + if (pszRaw[0] == PF_ADDRESS_FIELD_STARTS) + { + /* shift pszRaw to next symbol */ + pszRaw++; + cchRaw--; + + + /* we shouldn't face with ending here */ + AssertReturn(cchRaw > 0, VERR_INVALID_PARAMETER); + + char *pszEndOfAddress = RTStrStr(pszRaw, PF_STR_ADDRESS_FIELD_ENDS); + + /* no pair closing sign */ + AssertPtrReturn(pszEndOfAddress, VERR_INVALID_PARAMETER); + + cchField = pszEndOfAddress - pszRaw; + + /* field should be less then the rest of the string */ + AssertReturn(cchField < cchRaw, VERR_INVALID_PARAMETER); + + if (cchField != 0) + RTStrCopy(pszAddress, RT_MIN(cchField + 1, (size_t)cbAddress), pszRaw); + else if (!fEmptyAcceptable) + return -1; + } + + AssertReturn(pszRaw[cchField] == PF_ADDRESS_FIELD_ENDS, -1); + + return (int)cchField + 2; /* length of the field and closing braces */ +} + + +/** + * Parses a port something. + * + * @returns Offset relative to @a pszRaw of the end of the port field. + * -1 on failure. + * @param pszRaw The zero terminated string to parse. Points a field + * separator. + * @param pu16Port Where to store the port number on success. + */ +static int netPfStrPortParse(char *pszRaw, uint16_t *pu16Port) +{ +#if 1 + AssertPtrReturn(pszRaw, -1); + AssertPtrReturn(pu16Port, -1); + AssertReturn(pszRaw[0] == PF_FIELD_SEPARATOR, -1); + + char *pszNext = NULL; + int rc = RTStrToUInt16Ex(&pszRaw[1], &pszNext, 0, pu16Port); + if (rc == VWRN_TRAILING_CHARS) + AssertReturn(*pszNext == PF_FIELD_SEPARATOR, -1); + else if (rc == VINF_SUCCESS) + Assert(*pszNext == '\0'); + else + AssertMsgFailedReturn(("rc=%Rrc\n", rc), -1); + if (*pu16Port == 0) + return -1; + return (int)(pszNext - pszRaw); + +#else /* The same code, just a little more verbose: */ + char *pszEndOfPort = NULL; + uint16_t u16Port = 0; + int idxRaw = 1; /* we increment pszRaw after checks. */ + int cbRest = 0; + size_t cbPort = 0; + + AssertPtrReturn(pszRaw, -1); + AssertPtrReturn(pu16Port, -1); + AssertReturn(pszRaw[0] == PF_FIELD_SEPARATOR, -1); + + pszRaw++; /* skip field separator */ + cchRaw --; + + char *pszEndOfPort = RTStrStr(pszRaw, ":"); + if (!pszEndOfPort) + { + cbRest = strlen(pszRaw); + + Assert(cchRaw == cbRest); + + /* XXX: Assumption that if string is too big, it will be reported by + * RTStrToUint16. + */ + if (cbRest > 0) + { + pszEndOfPort = pszRaw + cbRest; + cbPort = cbRest; + } + else + return -1; + } + else + cbPort = pszEndOfPort - pszRaw; + + + idxRaw += cbPort; + + Assert(cbRest || pszRaw[idxRaw - 1] == PF_FIELD_SEPARATOR); /* we are 1 char ahead */ + + char szPort[10]; + RT_ZERO(szPort); + + Assert(idxRaw > 0); + RTStrCopy(szPort, RT_MIN(sizeof(szPort), (size_t)(cbPort) + 1), pszRaw); + + if (!(u16Port = RTStrToUInt16(szPort))) + return -1; + + *pu16Port = u16Port; + + return idxRaw; +#endif +} + + +static int netPfStrAddressPortPairParse(char *pszRaw, size_t cchRaw, + char *pszAddress, int cbAddress, + bool fEmptyAddressAcceptable, + uint16_t *pu16Port) +{ + int idxRaw = 0; + int idxRawTotal = 0; + + AssertPtrReturn(pszRaw, -1); + AssertPtrReturn(pszAddress, -1); + AssertPtrReturn(pu16Port, -2); + + /* XXX: Here we should check 0 - ':' and 1 - '[' */ + Assert( pszRaw[0] == PF_FIELD_SEPARATOR + && pszRaw[1] == PF_ADDRESS_FIELD_STARTS); + + pszRaw++; /* field separator skip */ + cchRaw--; + AssertReturn(cchRaw > 0, VERR_INVALID_PARAMETER); + + idxRaw = 0; + + if (pszRaw[0] == PF_ADDRESS_FIELD_STARTS) + { + idxRaw += netPfStrAddressParse(pszRaw, + cchRaw - idxRaw, + pszAddress, + cbAddress, + fEmptyAddressAcceptable); + if (idxRaw == -1) + return -1; + + Assert(pszRaw[idxRaw] == PF_FIELD_SEPARATOR); + } + else return -1; + + pszRaw += idxRaw; + idxRawTotal += idxRaw; + cchRaw -= idxRaw; + + AssertReturn(cchRaw > 0, VERR_INVALID_PARAMETER); + + idxRaw = 0; + + Assert(pszRaw[0] == PF_FIELD_SEPARATOR); + + if (pszRaw[0] == PF_FIELD_SEPARATOR) + { + idxRaw = netPfStrPortParse(pszRaw, pu16Port); + + Assert(strlen(&pszRaw[idxRaw]) == 0 || pszRaw[idxRaw] == PF_FIELD_SEPARATOR); + + if (idxRaw == -1) + return -2; + + idxRawTotal += idxRaw; + + return idxRawTotal + 1; + } + else return -1; /* trailing garbage in the address */ +} + +/* XXX: Having fIPv6 we might emprove adress verification comparing address length + * with INET[6]_ADDRLEN + * + */ +int netPfStrToPf(const char *pcszStrPortForward, bool fIPv6, PPORTFORWARDRULE pPfr) +{ +/** r=bird: Redo from scratch? This is very hard to read. And it's going about + * things in a very complicated, potentially leaky (pszRaw) fashion. */ + + int proto; + uint16_t u16HostPort; + uint16_t u16GuestPort; + bool fTcpProto = false; + + int idxRaw = 0; + int cbToken = 0; + + AssertPtrReturn(pcszStrPortForward, VERR_INVALID_PARAMETER); + AssertPtrReturn(pPfr, VERR_INVALID_PARAMETER); + + RT_ZERO(*pPfr); + + char *pszHostAddr = &pPfr->szPfrHostAddr[0]; + char *pszGuestAddr = &pPfr->szPfrGuestAddr[0]; + char *pszName = &pPfr->szPfrName[0]; + + size_t cchRaw = strlen(pcszStrPortForward); + + /* Minimal rule ":tcp:[]:0:[]:0" has got lenght 14 */ + AssertReturn(cchRaw > 14, VERR_INVALID_PARAMETER); + + char *pszRaw = RTStrDup(pcszStrPortForward); + AssertReturn(pszRaw, VERR_NO_MEMORY); + + char *pszRawBegin = pszRaw; + + /* name */ + if (pszRaw[idxRaw] == PF_FIELD_SEPARATOR) + idxRaw = 1; /* begin of the next segment */ + else + { + char *pszEndOfName = RTStrStr(pszRaw + 1, PF_STR_FIELD_SEPARATOR); + if (!pszEndOfName) + goto invalid_parameter; + + cbToken = pszEndOfName - pszRaw; /* don't take : into account */ + /* XXX it's unacceptable to have only name entry in PF */ + AssertReturn(cbToken < (ssize_t)cchRaw, VERR_INVALID_PARAMETER); + + if ( cbToken < 0 + || (size_t)cbToken >= PF_NAMELEN) + goto invalid_parameter; + + RTStrCopy(pszName, + RT_MIN((size_t)cbToken + 1, PF_NAMELEN), + pszRaw); + pszRaw += cbToken; /* move to separator */ + cchRaw -= cbToken; + } + + AssertReturn(pszRaw[0] == PF_FIELD_SEPARATOR, VERR_INVALID_PARAMETER); + /* protocol */ + + pszRaw++; /* skip separator */ + cchRaw--; + idxRaw = 0; + + if ( ( (fTcpProto = (RTStrNICmp(pszRaw, "tcp", 3) == 0)) + || RTStrNICmp(pszRaw, "udp", 3) == 0) + && pszRaw[3] == PF_FIELD_SEPARATOR) + { + proto = (fTcpProto ? IPPROTO_TCP : IPPROTO_UDP); + idxRaw = 3; + } + else + goto invalid_parameter; + + pszRaw += idxRaw; + cchRaw -= idxRaw; + + idxRaw = netPfStrAddressPortPairParse(pszRaw, cchRaw, + pszHostAddr, INET6_ADDRSTRLEN, + true, &u16HostPort); + if (idxRaw < 0) + return VERR_INVALID_PARAMETER; + + pszRaw += idxRaw; + cchRaw -= idxRaw; + + Assert(pszRaw[0] == PF_FIELD_SEPARATOR); + + idxRaw = netPfStrAddressPortPairParse(pszRaw, cchRaw, + pszGuestAddr, INET6_ADDRSTRLEN, + false, &u16GuestPort); + + if (idxRaw < 0) + goto invalid_parameter; + + /* XXX: fill the rule */ + pPfr->fPfrIPv6 = fIPv6; + pPfr->iPfrProto = proto; + + pPfr->u16PfrHostPort = u16HostPort; + + if (*pszGuestAddr == '\0') + goto invalid_parameter; /* guest address should be defined */ + + pPfr->u16PfrGuestPort = u16GuestPort; + + Log(("name: %s\n" + "proto: %d\n" + "host address: %s\n" + "host port: %d\n" + "guest address: %s\n" + "guest port:%d\n", + pszName, proto, + pszHostAddr, u16HostPort, + pszGuestAddr, u16GuestPort)); + + RTStrFree(pszRawBegin); + return VINF_SUCCESS; + +invalid_parameter: + RTStrFree(pszRawBegin); + if (pPfr) + RT_ZERO(*pPfr); + return VERR_INVALID_PARAMETER; +} diff --git a/src/VBox/NetworkServices/NetLib/VBoxNetUDP.cpp b/src/VBox/NetworkServices/NetLib/VBoxNetUDP.cpp new file mode 100644 index 00000000..0981ccec --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxNetUDP.cpp @@ -0,0 +1,314 @@ +/* $Id: VBoxNetUDP.cpp $ */ +/** @file + * VBoxNetUDP - IntNet UDP Client Routines. + */ + +/* + * Copyright (C) 2009-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_DEFAULT +#include "VBoxNetLib.h" +#include +#include +#include +#include +#include +#include + + +/** + * Checks if the head of the receive ring is a UDP packet matching the given + * criteria. + * + * @returns Pointer to the data if it matches. + * @param pBuf The IntNet buffers. + * @param uDstPort The destination port to match. + * @param pDstMac The destination address to match if + * VBOXNETUDP_MATCH_UNICAST is specied. + * @param fFlags Flags indicating what to match and some debug stuff. + * See VBOXNETUDP_MATCH_*. + * @param pHdrs Where to return the pointers to the headers. + * Optional. + * @param pcb Where to return the size of the data on success. + */ +void *VBoxNetUDPMatch(PINTNETBUF pBuf, unsigned uDstPort, PCRTMAC pDstMac, uint32_t fFlags, PVBOXNETUDPHDRS pHdrs, size_t *pcb) +{ + /* + * Clear return values so we can return easier on mismatch. + */ + *pcb = 0; + if (pHdrs) + { + pHdrs->pEth = NULL; + pHdrs->pIpv4 = NULL; + pHdrs->pUdp = NULL; + } + + /* + * Valid IntNet Ethernet frame? + */ + PCINTNETHDR pHdr = IntNetRingGetNextFrameToRead(&pBuf->Recv); + if ( !pHdr + || ( pHdr->u8Type != INTNETHDR_TYPE_FRAME + && pHdr->u8Type != INTNETHDR_TYPE_GSO)) + return NULL; + + size_t cbFrame = pHdr->cbFrame; + const void *pvFrame = IntNetHdrGetFramePtr(pHdr, pBuf); + PCPDMNETWORKGSO pGso = NULL; + if (pHdr->u8Type == INTNETHDR_TYPE_GSO) + { + pGso = (PCPDMNETWORKGSO)pvFrame; + if (!PDMNetGsoIsValid(pGso, cbFrame, cbFrame - sizeof(*pGso))) + return NULL; + /** @todo IPv6 UDP support, goes for this entire function really. Not really + * important yet since this is currently only used by the DHCP server. */ + if (pGso->u8Type != PDMNETWORKGSOTYPE_IPV4_UDP) + return NULL; + pvFrame = pGso + 1; + cbFrame -= sizeof(*pGso); + } + + PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame; + if (pHdrs) + pHdrs->pEth = pEthHdr; + +#ifdef IN_RING3 + /* Dump if to stderr/log if that's wanted. */ + if (fFlags & VBOXNETUDP_MATCH_PRINT_STDERR) + { + RTStrmPrintf(g_pStdErr, "frame: cb=%04x dst=%.6Rhxs src=%.6Rhxs type=%04x%s\n", + cbFrame, &pEthHdr->DstMac, &pEthHdr->SrcMac, RT_BE2H_U16(pEthHdr->EtherType), + !memcmp(&pEthHdr->DstMac, pDstMac, sizeof(*pDstMac)) ? " Mine!" : ""); + } +#endif + + /* + * Ethernet matching. + */ + + /* Ethernet min frame size. */ + if (cbFrame < 64) + return NULL; + + /* Match Ethertype: IPV4? */ + /** @todo VLAN tagging? */ + if (pEthHdr->EtherType != RT_H2BE_U16_C(RTNET_ETHERTYPE_IPV4)) + return NULL; + + /* Match destination address (ethernet) */ + if ( ( !(fFlags & VBOXNETUDP_MATCH_UNICAST) + || memcmp(&pEthHdr->DstMac, pDstMac, sizeof(pEthHdr->DstMac))) + && ( !(fFlags & VBOXNETUDP_MATCH_BROADCAST) + || pEthHdr->DstMac.au16[0] != 0xffff + || pEthHdr->DstMac.au16[1] != 0xffff + || pEthHdr->DstMac.au16[2] != 0xffff)) + return NULL; + + /* + * If we're working on a GSO frame, we need to make sure the length fields + * are set correctly (they are usually set to 0). + */ + if (pGso) + PDMNetGsoPrepForDirectUse(pGso, (void *)pvFrame, cbFrame, PDMNETCSUMTYPE_NONE); + + /* + * IP validation and matching. + */ + PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)(pEthHdr + 1); + if (pHdrs) + pHdrs->pIpv4 = pIpHdr; + + /* Protocol: UDP */ + if (pIpHdr->ip_p != RTNETIPV4_PROT_UDP) + return NULL; + + /* Valid IPv4 header? */ + size_t const offIpHdr = (uintptr_t)pIpHdr - (uintptr_t)pEthHdr; + if (!RTNetIPv4IsHdrValid(pIpHdr, cbFrame - offIpHdr, cbFrame - offIpHdr, !pGso /*fChecksum*/)) + return NULL; + + /* + * UDP matching and validation. + */ + PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint32_t *)pIpHdr + pIpHdr->ip_hl); + if (pHdrs) + pHdrs->pUdp = pUdpHdr; + + /* Destination port */ + if (RT_BE2H_U16(pUdpHdr->uh_dport) != uDstPort) + return NULL; + + if (!pGso) + { + /* Validate the UDP header according to flags. */ + size_t offUdpHdr = (uintptr_t)pUdpHdr - (uintptr_t)pEthHdr; + if (fFlags & (VBOXNETUDP_MATCH_CHECKSUM | VBOXNETUDP_MATCH_REQUIRE_CHECKSUM)) + { + if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbFrame - offUdpHdr, true /*fChecksum*/)) + return NULL; + if ( (fFlags & VBOXNETUDP_MATCH_REQUIRE_CHECKSUM) + && !pUdpHdr->uh_sum) + return NULL; + } + else + { + if (!RTNetIPv4IsUDPSizeValid(pIpHdr, pUdpHdr, cbFrame - offUdpHdr)) + return NULL; + } + } + + /* + * We've got a match! + */ + *pcb = RT_N2H_U16(pUdpHdr->uh_ulen) - sizeof(*pUdpHdr); + return (void *)(pUdpHdr + 1); +} + + +/** Internal worker for VBoxNetUDPUnicast and VBoxNetUDPBroadcast. */ +static int vboxnetudpSend(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf, + RTNETADDRIPV4 SrcIPv4Addr, PCRTMAC pSrcMacAddr, unsigned uSrcPort, + RTNETADDRIPV4 DstIPv4Addr, PCRTMAC pDstMacAddr, unsigned uDstPort, + void const *pvData, size_t cbData) +{ + INTNETSEG aSegs[4]; + + /* the Ethernet header */ + RTNETETHERHDR EtherHdr; + EtherHdr.DstMac = *pDstMacAddr; + EtherHdr.SrcMac = *pSrcMacAddr; + EtherHdr.EtherType = RT_H2BE_U16_C(RTNET_ETHERTYPE_IPV4); + + aSegs[0].pv = &EtherHdr; + aSegs[0].cb = sizeof(EtherHdr); + aSegs[0].Phys = NIL_RTHCPHYS; + + /* the IP header */ + RTNETIPV4 IpHdr; + unsigned cbIdHdr = RT_UOFFSETOF(RTNETIPV4, ip_options); + IpHdr.ip_v = 4; + IpHdr.ip_hl = cbIdHdr >> 2; + IpHdr.ip_tos = 0; + IpHdr.ip_len = RT_H2BE_U16((uint16_t)(cbData + sizeof(RTNETUDP) + cbIdHdr)); + IpHdr.ip_id = (uint16_t)RTRandU32(); + IpHdr.ip_off = 0; + IpHdr.ip_ttl = 255; + IpHdr.ip_p = RTNETIPV4_PROT_UDP; + IpHdr.ip_sum = 0; + IpHdr.ip_src = SrcIPv4Addr; + IpHdr.ip_dst = DstIPv4Addr; + IpHdr.ip_sum = RTNetIPv4HdrChecksum(&IpHdr); + + aSegs[1].pv = &IpHdr; + aSegs[1].cb = cbIdHdr; + aSegs[1].Phys = NIL_RTHCPHYS; + + + /* the UDP bit */ + RTNETUDP UdpHdr; + UdpHdr.uh_sport = RT_H2BE_U16(uSrcPort); + UdpHdr.uh_dport = RT_H2BE_U16(uDstPort); + UdpHdr.uh_ulen = RT_H2BE_U16((uint16_t)(cbData + sizeof(RTNETUDP))); +#if 0 + UdpHdr.uh_sum = 0; /* pretend checksumming is disabled */ +#else + UdpHdr.uh_sum = RTNetIPv4UDPChecksum(&IpHdr, &UdpHdr, pvData); +#endif + + aSegs[2].pv = &UdpHdr; + aSegs[2].cb = sizeof(UdpHdr); + aSegs[2].Phys = NIL_RTHCPHYS; + + /* the payload */ + aSegs[3].pv = (void *)pvData; + aSegs[3].cb = (uint32_t)cbData; + aSegs[3].Phys = NIL_RTHCPHYS; + + + /* send it */ + return VBoxNetIntIfSend(pSession, hIf, pBuf, RT_ELEMENTS(aSegs), &aSegs[0], true /* fFlush */); +} + + +/** + * Sends an unicast UDP packet. + * + * @returns VBox status code. + * @param pSession The support driver session handle. + * @param hIf The interface handle. + * @param pBuf The interface buffer. + * @param SrcIPv4Addr The source IPv4 address. + * @param pSrcMacAddr The source MAC address. + * @param uSrcPort The source port number. + * @param DstIPv4Addr The destination IPv4 address. Can be broadcast. + * @param pDstMacAddr The destination MAC address. + * @param uDstPort The destination port number. + * @param pvData The data payload. + * @param cbData The size of the data payload. + */ +int VBoxNetUDPUnicast(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf, + RTNETADDRIPV4 SrcIPv4Addr, PCRTMAC pSrcMacAddr, unsigned uSrcPort, + RTNETADDRIPV4 DstIPv4Addr, PCRTMAC pDstMacAddr, unsigned uDstPort, + void const *pvData, size_t cbData) +{ + return vboxnetudpSend(pSession, hIf, pBuf, + SrcIPv4Addr, pSrcMacAddr, uSrcPort, + DstIPv4Addr, pDstMacAddr, uDstPort, + pvData, cbData); +} + + +/** + * Sends a broadcast UDP packet. + * + * @returns VBox status code. + * @param pSession The support driver session handle. + * @param hIf The interface handle. + * @param pBuf The interface buffer. + * @param SrcIPv4Addr The source IPv4 address. + * @param pSrcMacAddr The source MAC address. + * @param uSrcPort The source port number. + * @param uDstPort The destination port number. + * @param pvData The data payload. + * @param cbData The size of the data payload. + */ +int VBoxNetUDPBroadcast(PSUPDRVSESSION pSession, INTNETIFHANDLE hIf, PINTNETBUF pBuf, + RTNETADDRIPV4 SrcIPv4Addr, PCRTMAC pSrcMacAddr, unsigned uSrcPort, + unsigned uDstPort, + void const *pvData, size_t cbData) +{ + RTNETADDRIPV4 IPv4AddrBrdCast; + IPv4AddrBrdCast.u = UINT32_C(0xffffffff); + RTMAC MacBrdCast; + MacBrdCast.au16[0] = MacBrdCast.au16[1] = MacBrdCast.au16[2] = UINT16_C(0xffff); + + return vboxnetudpSend(pSession, hIf, pBuf, + SrcIPv4Addr, pSrcMacAddr, uSrcPort, + IPv4AddrBrdCast, &MacBrdCast, uDstPort, + pvData, cbData); +} + diff --git a/src/VBox/NetworkServices/NetLib/VBoxPortForwardString.h b/src/VBox/NetworkServices/NetLib/VBoxPortForwardString.h new file mode 100644 index 00000000..57347a1c --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxPortForwardString.h @@ -0,0 +1,69 @@ +/* $Id: VBoxPortForwardString.h $ */ +/** @file + * VBoxPortForwardString + */ + +/* + * Copyright (C) 2009-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 + */ + +#ifndef VBOX_INCLUDED_SRC_NetLib_VBoxPortForwardString_h +#define VBOX_INCLUDED_SRC_NetLib_VBoxPortForwardString_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include +#include + +RT_C_DECLS_BEGIN + +#define PF_NAMELEN 64 +/* + * TBD: Here is shared implementation of parsing port-forward string + * of format: + * name:[ipv4 or ipv6 address]:host-port:[ipv4 or ipv6 guest addr]:guest port + * + * This code supposed to be used in NetService and Frontend and perhaps in corresponding + * services. + * + * Note: ports are in host format. + */ + +typedef struct PORTFORWARDRULE +{ + char szPfrName[PF_NAMELEN]; + /* true if ipv6 and false otherwise */ + int fPfrIPv6; + /* IPPROTO_{UDP,TCP} */ + int iPfrProto; + char szPfrHostAddr[INET6_ADDRSTRLEN]; + uint16_t u16PfrHostPort; + char szPfrGuestAddr[INET6_ADDRSTRLEN]; + uint16_t u16PfrGuestPort; +} PORTFORWARDRULE, *PPORTFORWARDRULE; + +int netPfStrToPf(const char *pszStrPortForward, bool fIPv6, PPORTFORWARDRULE pPfr); + +RT_C_DECLS_END + +#endif /* !VBOX_INCLUDED_SRC_NetLib_VBoxPortForwardString_h */ + diff --git a/src/VBox/NetworkServices/NetLib/cpp/utils.h b/src/VBox/NetworkServices/NetLib/cpp/utils.h new file mode 100644 index 00000000..04e222ac --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/cpp/utils.h @@ -0,0 +1,57 @@ +/* $Id: utils.h $ */ +/** @file + * NetLib/cpp/utils.h + */ + +/* + * Copyright (C) 2013-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 + */ + +#ifndef VBOX_INCLUDED_SRC_NetLib_cpp_utils_h +#define VBOX_INCLUDED_SRC_NetLib_cpp_utils_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include + +/** less operator for IPv4 addresess */ +DECLINLINE(bool) operator <(const RTNETADDRIPV4 &lhs, const RTNETADDRIPV4 &rhs) +{ + return RT_N2H_U32(lhs.u) < RT_N2H_U32(rhs.u); +} + +/** greater operator for IPv4 addresess */ +DECLINLINE(bool) operator >(const RTNETADDRIPV4 &lhs, const RTNETADDRIPV4 &rhs) +{ + return RT_N2H_U32(lhs.u) > RT_N2H_U32(rhs.u); +} + +/** Compares MAC addresses */ +DECLINLINE(bool) operator== (const RTMAC &lhs, const RTMAC &rhs) +{ + return lhs.au16[0] == rhs.au16[0] + && lhs.au16[1] == rhs.au16[1] + && lhs.au16[2] == rhs.au16[2]; +} + +#endif /* !VBOX_INCLUDED_SRC_NetLib_cpp_utils_h */ + diff --git a/src/VBox/NetworkServices/NetLib/shared_ptr.h b/src/VBox/NetworkServices/NetLib/shared_ptr.h new file mode 100644 index 00000000..a7a488a7 --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/shared_ptr.h @@ -0,0 +1,112 @@ +/* $Id: shared_ptr.h $ */ +/** @file + * Simplified shared pointer. + */ + +/* + * Copyright (C) 2013-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 + */ + +#ifndef VBOX_INCLUDED_SRC_NetLib_shared_ptr_h +#define VBOX_INCLUDED_SRC_NetLib_shared_ptr_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#ifdef __cplusplus +template +class SharedPtr +{ + struct imp + { + imp(T *pTrg = NULL, int cnt = 1): ptr(pTrg),refcnt(cnt){} + ~imp() { if (ptr) delete ptr;} + + T *ptr; + int refcnt; + }; + + + public: + SharedPtr(T *t = NULL):p(NULL) + { + p = new imp(t); + } + + ~SharedPtr() + { + p->refcnt--; + + if (p->refcnt == 0) + delete p; + } + + + SharedPtr(const SharedPtr& rhs) + { + p = rhs.p; + p->refcnt++; + } + + const SharedPtr& operator= (const SharedPtr& rhs) + { + if (p == rhs.p) return *this; + + p->refcnt--; + if (p->refcnt == 0) + delete p; + + p = rhs.p; + p->refcnt++; + + return *this; + } + + + T *get() const + { + return p->ptr; + } + + + T *operator->() + { + return p->ptr; + } + + + const T*operator->() const + { + return p->ptr; + } + + + int use_count() + { + return p->refcnt; + } + + private: + imp *p; +}; +#endif + +#endif /* !VBOX_INCLUDED_SRC_NetLib_shared_ptr_h */ -- cgit v1.2.3