summaryrefslogtreecommitdiffstats
path: root/src/VBox/NetworkServices/NetLib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/NetworkServices/NetLib
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/NetworkServices/NetLib')
-rw-r--r--src/VBox/NetworkServices/NetLib/IntNetIf.cpp575
-rw-r--r--src/VBox/NetworkServices/NetLib/IntNetIf.h112
-rw-r--r--src/VBox/NetworkServices/NetLib/Makefile.kup0
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxNetARP.cpp165
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxNetBaseService.cpp858
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxNetBaseService.h159
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxNetIntIf.cpp150
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxNetLib.h82
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxNetPortForwardString.cpp382
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxNetUDP.cpp314
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxPortForwardString.h69
-rw-r--r--src/VBox/NetworkServices/NetLib/cpp/utils.h57
-rw-r--r--src/VBox/NetworkServices/NetLib/shared_ptr.h112
13 files changed, 3035 insertions, 0 deletions
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 <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
+# if defined(RT_OS_DARWIN)
+# include <xpc/xpc.h> /* 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 <iprt/cdefs.h>
+#include <iprt/path.h>
+#include <iprt/semaphore.h>
+
+#include <VBox/err.h>
+#include <VBox/sup.h>
+#include <VBox/intnetinline.h>
+#include <VBox/vmm/pdmnetinline.h>
+
+#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 <https://www.gnu.org/licenses>.
+ *
+ * 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 <iprt/cdefs.h>
+
+#include <iprt/initterm.h>
+#include <iprt/cpp/ministring.h>
+
+#include <VBox/sup.h>
+#include <VBox/vmm/vmm.h>
+#include <VBox/intnet.h>
+
+
+
+/**
+ * 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
--- /dev/null
+++ b/src/VBox/NetworkServices/NetLib/Makefile.kup
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 <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEFAULT
+#include "VBoxNetLib.h"
+#include <iprt/string.h>
+#include <VBox/intnetinline.h>
+#include <VBox/log.h>
+
+
+/**
+ * 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 <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_SERVICE
+
+#include <VBox/com/com.h>
+#include <VBox/com/listeners.h>
+#include <VBox/com/string.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/array.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+#include <VBox/com/VirtualBox.h>
+#include <VBox/com/NativeEventQueue.h>
+
+#include <iprt/alloca.h>
+#include <iprt/buildconfig.h>
+#include <iprt/err.h>
+#include <iprt/net.h> /* must come before getopt.h. */
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+
+#include <VBox/sup.h>
+#include <VBox/intnet.h>
+#include <VBox/intnetinline.h>
+#include <VBox/vmm/vmm.h>
+#include <VBox/version.h>
+
+#include <vector>
+#include <iprt/sanitized/string>
+
+#include <VBox/err.h>
+#include <VBox/log.h>
+
+#include "VBoxNetLib.h"
+#include "VBoxNetBaseService.h"
+
+#ifdef RT_OS_WINDOWS /* WinMain */
+# include <iprt/win/windows.h>
+# include <stdlib.h>
+#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<PCRTGETOPTDEF> 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<VBoxNetBaseService *>(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 <options>\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 <https://www.gnu.org/licenses>.
+ *
+ * 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 <iprt/critsect.h>
+
+
+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<IVirtualBox> virtualbox;
+ ComPtr<IVirtualBoxClient> 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 <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEFAULT
+#include "VBoxNetLib.h"
+#include <VBox/intnet.h>
+#include <VBox/intnetinline.h>
+#include <VBox/sup.h>
+#include <VBox/vmm/vmm.h>
+#include <iprt/errcore.h>
+#include <VBox/log.h>
+
+#include <iprt/string.h>
+
+
+
+/**
+ * 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 <https://www.gnu.org/licenses>.
+ *
+ * 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 <iprt/net.h>
+#include <VBox/intnet.h>
+
+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 <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifndef RT_OS_WINDOWS
+# include <netinet/in.h>
+#else
+# include <iprt/win/winsock2.h>
+# include <Ws2ipdef.h>
+#endif
+
+#include <iprt/cdefs.h>
+#include <iprt/cidr.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/getopt.h>
+#include <iprt/net.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+
+#include <VBox/log.h>
+
+#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 <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEFAULT
+#include "VBoxNetLib.h"
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/rand.h>
+#include <VBox/log.h>
+#include <VBox/vmm/pdmnetinline.h>
+#include <VBox/intnetinline.h>
+
+
+/**
+ * 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 <https://www.gnu.org/licenses>.
+ *
+ * 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 <iprt/net.h>
+#include <VBox/intnet.h>
+
+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 <https://www.gnu.org/licenses>.
+ *
+ * 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 <iprt/types.h>
+
+/** 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 <https://www.gnu.org/licenses>.
+ *
+ * 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<typename T>
+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 */