diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
commit | f8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch) | |
tree | 26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/NetworkServices/NetLib | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/NetworkServices/NetLib')
-rw-r--r-- | src/VBox/NetworkServices/NetLib/ComHostUtils.cpp | 230 | ||||
-rw-r--r-- | src/VBox/NetworkServices/NetLib/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/NetworkServices/NetLib/VBoxNetARP.cpp | 155 | ||||
-rw-r--r-- | src/VBox/NetworkServices/NetLib/VBoxNetBaseService.cpp | 849 | ||||
-rw-r--r-- | src/VBox/NetworkServices/NetLib/VBoxNetBaseService.h | 148 | ||||
-rw-r--r-- | src/VBox/NetworkServices/NetLib/VBoxNetIntIf.cpp | 140 | ||||
-rw-r--r-- | src/VBox/NetworkServices/NetLib/VBoxNetLib.h | 72 | ||||
-rw-r--r-- | src/VBox/NetworkServices/NetLib/VBoxNetPortForwardString.cpp | 372 | ||||
-rw-r--r-- | src/VBox/NetworkServices/NetLib/VBoxNetUDP.cpp | 304 | ||||
-rw-r--r-- | src/VBox/NetworkServices/NetLib/VBoxPortForwardString.h | 59 | ||||
-rw-r--r-- | src/VBox/NetworkServices/NetLib/cpp/utils.h | 47 | ||||
-rw-r--r-- | src/VBox/NetworkServices/NetLib/shared_ptr.h | 102 | ||||
-rw-r--r-- | src/VBox/NetworkServices/NetLib/utils.h | 142 |
13 files changed, 2620 insertions, 0 deletions
diff --git a/src/VBox/NetworkServices/NetLib/ComHostUtils.cpp b/src/VBox/NetworkServices/NetLib/ComHostUtils.cpp new file mode 100644 index 00000000..8ee2e19a --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/ComHostUtils.cpp @@ -0,0 +1,230 @@ +/* $Id: ComHostUtils.cpp $ */ +/** @file + * ComHostUtils.cpp + */ + +/* + * Copyright (C) 2013-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#if defined(RT_OS_WINDOWS) && !defined(VBOX_COM_OUTOFPROC_MODULE) +# define VBOX_COM_OUTOFPROC_MODULE +#endif +#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/EventQueue.h> +#include <VBox/com/VirtualBox.h> + +#include <iprt/alloca.h> +#include <iprt/buildconfig.h> +#include <iprt/errcore.h> +#include <iprt/net.h> /* must come before getopt */ +#include <iprt/getopt.h> +#include <iprt/initterm.h> +#include <iprt/message.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/stream.h> +#include <iprt/time.h> +#include <iprt/string.h> + + +#include "../NetLib/VBoxNetLib.h" +#include "../NetLib/shared_ptr.h" + +#include <vector> +#include <list> +#include <string> +#include <map> + +#include "../NetLib/VBoxNetBaseService.h" + +#ifdef RT_OS_WINDOWS /* WinMain */ +# include <iprt/win/windows.h> +# include <stdlib.h> +# ifdef INET_ADDRSTRLEN +/* On Windows INET_ADDRSTRLEN defined as 22 Ws2ipdef.h, because it include port number */ +# undef INET_ADDRSTRLEN +# endif +# define INET_ADDRSTRLEN 16 +#else +# include <netinet/in.h> +#endif + +#include "utils.h" + + +VBOX_LISTENER_DECLARE(NATNetworkListenerImpl) + + +int localMappings(const ComNatPtr& nat, AddressToOffsetMapping& mapping) +{ + mapping.clear(); + + ComBstrArray strs; + size_t cStrs; + HRESULT hrc = nat->COMGETTER(LocalMappings)(ComSafeArrayAsOutParam(strs)); + if ( SUCCEEDED(hrc) + && (cStrs = strs.size())) + { + for (size_t i = 0; i < cStrs; ++i) + { + char szAddr[17]; + RTNETADDRIPV4 ip4addr; + char *pszTerm; + uint32_t u32Off; + com::Utf8Str strLo2Off(strs[i]); + const char *pszLo2Off = strLo2Off.c_str(); + + RT_ZERO(szAddr); + + pszTerm = RTStrStr(pszLo2Off, "="); + + if ( pszTerm + && (pszTerm - pszLo2Off) <= INET_ADDRSTRLEN) + { + memcpy(szAddr, pszLo2Off, (pszTerm - pszLo2Off)); + int rc = RTNetStrToIPv4Addr(szAddr, &ip4addr); + if (RT_SUCCESS(rc)) + { + u32Off = RTStrToUInt32(pszTerm + 1); + if (u32Off != 0) + mapping.insert( + AddressToOffsetMapping::value_type(ip4addr, u32Off)); + } + } + } + } + else + return VERR_NOT_FOUND; + + return VINF_SUCCESS; +} + + +int hostDnsSearchList(const ComHostPtr& host, std::vector<std::string>& strings) +{ + strings.clear(); + + ComBstrArray strs; + if (SUCCEEDED(host->COMGETTER(SearchStrings)(ComSafeArrayAsOutParam(strs)))) + { + for (unsigned int i = 0; i < strs.size(); ++i) + { + strings.push_back(com::Utf8Str(strs[i]).c_str()); + } + } + else + return VERR_NOT_FOUND; + + return VINF_SUCCESS; +} + + +int hostDnsDomain(const ComHostPtr& host, std::string& domainStr) +{ + com::Bstr domain; + if (SUCCEEDED(host->COMGETTER(DomainName)(domain.asOutParam()))) + { + domainStr = com::Utf8Str(domain).c_str(); + return VINF_SUCCESS; + } + + return VERR_NOT_FOUND; +} + + +int createNatListener(ComNatListenerPtr& listener, const ComVirtualBoxPtr& vboxptr, + NATNetworkEventAdapter *adapter, /* const */ ComEventTypeArray& events) +{ + ComObjPtr<NATNetworkListenerImpl> obj; + HRESULT hrc = obj.createObject(); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + + hrc = obj->init(new NATNetworkListener(), adapter); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + + ComPtr<IEventSource> esVBox; + hrc = vboxptr->COMGETTER(EventSource)(esVBox.asOutParam()); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + + listener = obj; + + hrc = esVBox->RegisterListener(listener, ComSafeArrayAsInParam(events), true); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + + return VINF_SUCCESS; +} + +int destroyNatListener(ComNatListenerPtr& listener, const ComVirtualBoxPtr& vboxptr) +{ + if (listener) + { + ComPtr<IEventSource> esVBox; + HRESULT hrc = vboxptr->COMGETTER(EventSource)(esVBox.asOutParam()); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + if (!esVBox.isNull()) + { + hrc = esVBox->UnregisterListener(listener); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + } + listener.setNull(); + } + return VINF_SUCCESS; +} + +int createClientListener(ComNatListenerPtr& listener, const ComVirtualBoxClientPtr& vboxclientptr, + NATNetworkEventAdapter *adapter, /* const */ ComEventTypeArray& events) +{ + ComObjPtr<NATNetworkListenerImpl> obj; + HRESULT hrc = obj.createObject(); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + + hrc = obj->init(new NATNetworkListener(), adapter); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + + ComPtr<IEventSource> esVBox; + hrc = vboxclientptr->COMGETTER(EventSource)(esVBox.asOutParam()); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + + listener = obj; + + hrc = esVBox->RegisterListener(listener, ComSafeArrayAsInParam(events), true); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + + return VINF_SUCCESS; +} + +int destroyClientListener(ComNatListenerPtr& listener, const ComVirtualBoxClientPtr& vboxclientptr) +{ + if (listener) + { + ComPtr<IEventSource> esVBox; + HRESULT hrc = vboxclientptr->COMGETTER(EventSource)(esVBox.asOutParam()); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + if (!esVBox.isNull()) + { + hrc = esVBox->UnregisterListener(listener); + AssertComRCReturn(hrc, VERR_INTERNAL_ERROR); + } + listener.setNull(); + } + return VINF_SUCCESS; +} 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..c3a7782f --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxNetARP.cpp @@ -0,0 +1,155 @@ +/* $Id: VBoxNetARP.cpp $ */ +/** @file + * VBoxNetARP - IntNet ARP Client Routines. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* 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..a4118fa8 --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxNetBaseService.cpp @@ -0,0 +1,849 @@ +/* $Id: VBoxNetBaseService.cpp $ */ +/** @file + * VBoxNetBaseService - common services for VBoxNetDHCP and VBoxNetNAT. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* 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 <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<PRTGETOPTDEF> 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" + "(C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n" + "All rights reserved.\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")); + 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(const PRTGETOPTDEF 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) + { + PRTGETOPTDEF 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..7aa5a438 --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxNetBaseService.h @@ -0,0 +1,148 @@ +/* $Id: VBoxNetBaseService.h $ */ +/** @file + * VBoxNetUDP - IntNet Client Library. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#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 int hlpUDPBroadcast(unsigned uSrcPort, unsigned uDstPort, + void const *pvData, size_t cbData) const = 0; +}; + + +class VBoxNetLockee +{ +public: + 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(const PRTGETOPTDEF); + + /** + * 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..9e7a695f --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxNetIntIf.cpp @@ -0,0 +1,140 @@ +/* $Id: VBoxNetIntIf.cpp $ */ +/** @file + * VBoxNetIntIf - IntNet Interface Client Routines. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* 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..7b057274 --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxNetLib.h @@ -0,0 +1,72 @@ +/* $Id: VBoxNetLib.h $ */ +/** @file + * VBoxNetUDP - IntNet Client Library. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#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..7702ceff --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxNetPortForwardString.cpp @@ -0,0 +1,372 @@ +/* $Id: VBoxNetPortForwardString.cpp $ */ +/** @file + * VBoxNetPortForwardString - Routines for managing port-forward strings. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* 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..5dbbdbf2 --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxNetUDP.cpp @@ -0,0 +1,304 @@ +/* $Id: VBoxNetUDP.cpp $ */ +/** @file + * VBoxNetUDP - IntNet UDP Client Routines. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* 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..7ca8abc5 --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/VBoxPortForwardString.h @@ -0,0 +1,59 @@ +/* $Id: VBoxPortForwardString.h $ */ +/** @file + * VBoxPortForwardString + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#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..31128d02 --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/cpp/utils.h @@ -0,0 +1,47 @@ +/* $Id: utils.h $ */ +/** @file + * NetLib/cpp/utils.h + */ + +/* + * Copyright (C) 2013-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#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..42d32fb0 --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/shared_ptr.h @@ -0,0 +1,102 @@ +/* $Id: shared_ptr.h $ */ +/** @file + * Simplified shared pointer. + */ + +/* + * Copyright (C) 2013-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#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 */ diff --git a/src/VBox/NetworkServices/NetLib/utils.h b/src/VBox/NetworkServices/NetLib/utils.h new file mode 100644 index 00000000..ccd5cadf --- /dev/null +++ b/src/VBox/NetworkServices/NetLib/utils.h @@ -0,0 +1,142 @@ +/* $Id: utils.h $ */ +/** @file + * ComHostUtils.cpp + */ + +/* + * Copyright (C) 2013-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ + +#ifndef VBOX_INCLUDED_SRC_NetLib_utils_h +#define VBOX_INCLUDED_SRC_NetLib_utils_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "cpp/utils.h" + +typedef ComPtr<IVirtualBox> ComVirtualBoxPtr; +typedef ComPtr<IVirtualBoxClient> ComVirtualBoxClientPtr; +typedef ComPtr<IDHCPServer> ComDhcpServerPtr; +typedef ComPtr<IHost> ComHostPtr; +typedef ComPtr<INATNetwork> ComNatPtr; +typedef com::SafeArray<BSTR> ComBstrArray; + +typedef std::vector<RTNETADDRIPV4> AddressList; +typedef std::map<RTNETADDRIPV4, int> AddressToOffsetMapping; + + +inline bool isDhcpRequired(const ComNatPtr& nat) +{ + BOOL fNeedDhcpServer = false; + if (FAILED(nat->COMGETTER(NeedDhcpServer)(&fNeedDhcpServer))) + return false; + + return RT_BOOL(fNeedDhcpServer); +} + + +inline int findDhcpServer(const ComVirtualBoxPtr& vbox, const std::string& name, ComDhcpServerPtr& dhcp) +{ + HRESULT hrc = vbox->FindDHCPServerByNetworkName(com::Bstr(name.c_str()).raw(), + dhcp.asOutParam()); + AssertComRCReturn(hrc, VERR_NOT_FOUND); + + return VINF_SUCCESS; +} + + +inline int findNatNetwork(const ComVirtualBoxPtr& vbox, const std::string& name, ComNatPtr& nat) +{ + HRESULT hrc = vbox->FindNATNetworkByName(com::Bstr(name.c_str()).raw(), + nat.asOutParam()); + + AssertComRCReturn(hrc, VERR_NOT_FOUND); + + return VINF_SUCCESS; +} + + +inline RTNETADDRIPV4 networkid(const RTNETADDRIPV4& addr, const RTNETADDRIPV4& netmask) +{ + RTNETADDRIPV4 netid; + netid.u = addr.u & netmask.u; + return netid; +} + + +int localMappings(const ComNatPtr&, AddressToOffsetMapping&); +int hostDnsSearchList(const ComHostPtr&, std::vector<std::string>&); +int hostDnsDomain(const ComHostPtr&, std::string& domainStr); + + +class NATNetworkEventAdapter +{ + public: + virtual HRESULT HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent) = 0; +}; + + +class NATNetworkListener +{ + public: + NATNetworkListener():m_pNAT(NULL){} + + HRESULT init(NATNetworkEventAdapter *pNAT) + { + AssertPtrReturn(pNAT, E_INVALIDARG); + + m_pNAT = pNAT; + return S_OK; + } + + HRESULT init() + { + m_pNAT = NULL; + return S_OK; + } + + void uninit() { m_pNAT = NULL; } + + HRESULT HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent) + { + if (m_pNAT) + return m_pNAT->HandleEvent(aEventType, pEvent); + else + return E_FAIL; + } + + private: + NATNetworkEventAdapter *m_pNAT; +}; +typedef ListenerImpl<NATNetworkListener, NATNetworkEventAdapter*> NATNetworkListenerImpl; + +# ifdef VBOX_WITH_XPCOM +class NS_CLASSINFO_NAME(NATNetworkListenerImpl); +# endif + +typedef ComPtr<NATNetworkListenerImpl> ComNatListenerPtr; +typedef com::SafeArray<VBoxEventType_T> ComEventTypeArray; + +/* XXX: const is commented out because of compilation erro on Windows host, but it's intended that this function + isn't modify event type array */ +int createNatListener(ComNatListenerPtr& listener, const ComVirtualBoxPtr& vboxptr, + NATNetworkEventAdapter *adapter, /* const */ ComEventTypeArray& events); +int destroyNatListener(ComNatListenerPtr& listener, const ComVirtualBoxPtr& vboxptr); +int createClientListener(ComNatListenerPtr& listener, const ComVirtualBoxClientPtr& vboxclientptr, + NATNetworkEventAdapter *adapter, /* const */ ComEventTypeArray& events); +int destroyClientListener(ComNatListenerPtr& listener, const ComVirtualBoxClientPtr& vboxclientptr); + +#endif /* !VBOX_INCLUDED_SRC_NetLib_utils_h */ |