diff options
Diffstat (limited to 'src/VBox/Main/src-server/NATEngineImpl.cpp')
-rw-r--r-- | src/VBox/Main/src-server/NATEngineImpl.cpp | 627 |
1 files changed, 627 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/NATEngineImpl.cpp b/src/VBox/Main/src-server/NATEngineImpl.cpp new file mode 100644 index 00000000..20189fbb --- /dev/null +++ b/src/VBox/Main/src-server/NATEngineImpl.cpp @@ -0,0 +1,627 @@ +/* $Id: NATEngineImpl.cpp $ */ +/** @file + * Implementation of INATEngine in VBoxSVC. + */ + +/* + * Copyright (C) 2010-2022 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 + */ + +#define LOG_GROUP LOG_GROUP_MAIN_NATENGINE +#include "NATEngineImpl.h" +#include "AutoCaller.h" +#include "LoggingNew.h" +#include "MachineImpl.h" + +#include <iprt/string.h> +#include <iprt/cpp/utils.h> + +#include <iprt/errcore.h> +#include <VBox/settings.h> +#include <VBox/com/array.h> + +struct NATEngine::Data +{ + Backupable<settings::NAT> m; +}; + + +// constructor / destructor +//////////////////////////////////////////////////////////////////////////////// + +NATEngine::NATEngine():mData(NULL), mParent(NULL), mAdapter(NULL) {} +NATEngine::~NATEngine(){} + +HRESULT NATEngine::FinalConstruct() +{ + return BaseFinalConstruct(); +} + +void NATEngine::FinalRelease() +{ + uninit(); + BaseFinalRelease(); +} + + +HRESULT NATEngine::init(Machine *aParent, INetworkAdapter *aAdapter) +{ + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), E_FAIL); + autoInitSpan.setSucceeded(); + mData = new Data(); + mData->m.allocate(); + mData->m->strNetwork.setNull(); + mData->m->strBindIP.setNull(); + unconst(mParent) = aParent; + unconst(mAdapter) = aAdapter; + return S_OK; +} + +HRESULT NATEngine::init(Machine *aParent, INetworkAdapter *aAdapter, NATEngine *aThat) +{ + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), E_FAIL); + Log(("init that:%p this:%p\n", aThat, this)); + + AutoCaller thatCaller(aThat); + AssertComRCReturnRC(thatCaller.rc()); + + AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS); + + mData = new Data(); + mData->m.share(aThat->mData->m); + unconst(mParent) = aParent; + unconst(mAdapter) = aAdapter; + unconst(mPeer) = aThat; + autoInitSpan.setSucceeded(); + return S_OK; +} + +HRESULT NATEngine::initCopy(Machine *aParent, INetworkAdapter *aAdapter, NATEngine *aThat) +{ + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), E_FAIL); + + Log(("initCopy that:%p this:%p\n", aThat, this)); + + AutoCaller thatCaller(aThat); + AssertComRCReturnRC(thatCaller.rc()); + + AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS); + + mData = new Data(); + mData->m.attachCopy(aThat->mData->m); + unconst(mAdapter) = aAdapter; + unconst(mParent) = aParent; + autoInitSpan.setSucceeded(); + + return S_OK; +} + + +void NATEngine::uninit() +{ + AutoUninitSpan autoUninitSpan(this); + if (autoUninitSpan.uninitDone()) + return; + + mData->m.free(); + delete mData; + mData = NULL; + unconst(mPeer) = NULL; + unconst(mParent) = NULL; +} + +bool NATEngine::i_isModified() +{ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + bool fModified = mData->m.isBackedUp(); + return fModified; +} + +void NATEngine::i_rollback() +{ + AutoCaller autoCaller(this); + AssertComRCReturnVoid(autoCaller.rc()); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + mData->m.rollback(); +} + +void NATEngine::i_commit() +{ + AutoCaller autoCaller(this); + AssertComRCReturnVoid(autoCaller.rc()); + + /* sanity too */ + AutoCaller peerCaller(mPeer); + AssertComRCReturnVoid(peerCaller.rc()); + + /* lock both for writing since we modify both (mPeer is "master" so locked + * first) */ + AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS); + if (mData->m.isBackedUp()) + { + mData->m.commit(); + if (mPeer) + mPeer->mData->m.attach(mData->m); + } +} + +void NATEngine::i_copyFrom(NATEngine *aThat) +{ + AssertReturnVoid(aThat != NULL); + + /* sanity */ + AutoCaller autoCaller(this); + AssertComRCReturnVoid(autoCaller.rc()); + + /* sanity too */ + AutoCaller thatCaller(aThat); + AssertComRCReturnVoid(thatCaller.rc()); + + /* peer is not modified, lock it for reading (aThat is "master" so locked + * first) */ + AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS); + AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS); + + /* this will back up current data */ + mData->m.assignCopy(aThat->mData->m); +} + +void NATEngine::i_applyDefaults() +{ + /* sanity */ + AutoCaller autoCaller(this); + AssertComRCReturnVoid(autoCaller.rc()); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + mData->m->fLocalhostReachable = false; /* Applies to new VMs only, see @bugref{9896} */ +} + +bool NATEngine::i_hasDefaults() +{ + /* sanity */ + AutoCaller autoCaller(this); + AssertComRCReturn(autoCaller.rc(), true); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + return mData->m->areDefaultSettings(mParent->i_getSettingsVersion()); +} + +HRESULT NATEngine::getNetworkSettings(ULONG *aMtu, ULONG *aSockSnd, ULONG *aSockRcv, ULONG *aTcpWndSnd, ULONG *aTcpWndRcv) +{ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + if (aMtu) + *aMtu = mData->m->u32Mtu; + if (aSockSnd) + *aSockSnd = mData->m->u32SockSnd; + if (aSockRcv) + *aSockRcv = mData->m->u32SockRcv; + if (aTcpWndSnd) + *aTcpWndSnd = mData->m->u32TcpSnd; + if (aTcpWndRcv) + *aTcpWndRcv = mData->m->u32TcpRcv; + + return S_OK; +} + +HRESULT NATEngine::setNetworkSettings(ULONG aMtu, ULONG aSockSnd, ULONG aSockRcv, ULONG aTcpWndSnd, ULONG aTcpWndRcv) +{ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + if ( aMtu || aSockSnd || aSockRcv + || aTcpWndSnd || aTcpWndRcv) + { + mData->m.backup(); + mParent->i_setModified(Machine::IsModified_NetworkAdapters); + } + if (aMtu) + mData->m->u32Mtu = aMtu; + if (aSockSnd) + mData->m->u32SockSnd = aSockSnd; + if (aSockRcv) + mData->m->u32SockRcv = aSockSnd; + if (aTcpWndSnd) + mData->m->u32TcpSnd = aTcpWndSnd; + if (aTcpWndRcv) + mData->m->u32TcpRcv = aTcpWndRcv; + + return S_OK; +} + + +HRESULT NATEngine::getRedirects(std::vector<com::Utf8Str> &aRedirects) +{ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + aRedirects.resize(mData->m->mapRules.size()); + size_t i = 0; + settings::NATRulesMap::const_iterator it; + for (it = mData->m->mapRules.begin(); it != mData->m->mapRules.end(); ++it, ++i) + { + settings::NATRule r = it->second; + aRedirects[i] = Utf8StrFmt("%s,%d,%s,%d,%s,%d", + r.strName.c_str(), + r.proto, + r.strHostIP.c_str(), + r.u16HostPort, + r.strGuestIP.c_str(), + r.u16GuestPort); + } + return S_OK; +} + +HRESULT NATEngine::addRedirect(const com::Utf8Str &aName, NATProtocol_T aProto, const com::Utf8Str &aHostIP, + USHORT aHostPort, const com::Utf8Str &aGuestIP, USHORT aGuestPort) +{ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + Utf8Str name = aName; + settings::NATRule r; + const char *proto; + switch (aProto) + { + case NATProtocol_TCP: + proto = "tcp"; + break; + case NATProtocol_UDP: + proto = "udp"; + break; + default: + return E_INVALIDARG; + } + + if (name.isEmpty()) + name = Utf8StrFmt("%s_%d_%d", proto, aHostPort, aGuestPort); + else + { + const char *s; + char c; + + for (s = name.c_str(); (c = *s) != '\0'; ++s) + { + if (c == ',') /* we use csv in several places e.g. GetRedirects or natpf<N> argument */ + return setError(E_INVALIDARG, + tr("'%c' - invalid character in NAT rule name"), c); + } + } + + settings::NATRulesMap::iterator it; + for (it = mData->m->mapRules.begin(); it != mData->m->mapRules.end(); ++it) + { + r = it->second; + if (it->first == name) + return setError(E_INVALIDARG, + tr("A NAT rule of this name already exists")); + if ( r.strHostIP == aHostIP + && r.u16HostPort == aHostPort + && r.proto == aProto) + return setError(E_INVALIDARG, + tr("A NAT rule for this host port and this host IP already exists")); + } + + mData->m.backup(); + r.strName = name.c_str(); + r.proto = aProto; + r.strHostIP = aHostIP; + r.u16HostPort = aHostPort; + r.strGuestIP = aGuestIP; + r.u16GuestPort = aGuestPort; + mData->m->mapRules.insert(std::make_pair(name, r)); + mParent->i_setModified(Machine::IsModified_NetworkAdapters); + + ULONG ulSlot; + mAdapter->COMGETTER(Slot)(&ulSlot); + + alock.release(); + mParent->i_onNATRedirectRuleChanged(ulSlot, FALSE, name, aProto, r.strHostIP, r.u16HostPort, r.strGuestIP, r.u16GuestPort); + return S_OK; +} + +HRESULT NATEngine::removeRedirect(const com::Utf8Str &aName) +{ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + settings::NATRulesMap::iterator it = mData->m->mapRules.find(aName); + if (it == mData->m->mapRules.end()) + return E_INVALIDARG; + mData->m.backup(); + /* + * NB: "it" may now point to the backup! In that case it's ok to + * get data from the backup copy of s.mapRules via it, but we can't + * erase(it) from potentially new s.mapRules. + */ + settings::NATRule r = it->second; + ULONG ulSlot; + mAdapter->COMGETTER(Slot)(&ulSlot); + + mData->m->mapRules.erase(aName); /* NB: erase by key, "it" may not be valid */ + mParent->i_setModified(Machine::IsModified_NetworkAdapters); + alock.release(); + mParent->i_onNATRedirectRuleChanged(ulSlot, TRUE, aName, r.proto, r.strHostIP, r.u16HostPort, r.strGuestIP, r.u16GuestPort); + return S_OK; +} + +HRESULT NATEngine::i_loadSettings(const settings::NAT &data) +{ + AutoCaller autoCaller(this); + AssertComRCReturnRC(autoCaller.rc()); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + mData->m.assignCopy(&data); + return S_OK; +} + + +HRESULT NATEngine::i_saveSettings(settings::NAT &data) +{ + AutoCaller autoCaller(this); + AssertComRCReturnRC(autoCaller.rc()); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + HRESULT rc = S_OK; + data = *mData->m.data(); + return rc; +} + +HRESULT NATEngine::setNetwork(const com::Utf8Str &aNetwork) +{ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + if (mData->m->strNetwork != aNetwork) + { + mData->m.backup(); + mData->m->strNetwork = aNetwork; + mParent->i_setModified(Machine::IsModified_NetworkAdapters); + } + return S_OK; +} + + +HRESULT NATEngine::getNetwork(com::Utf8Str &aNetwork) +{ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + if (!mData->m->strNetwork.isEmpty()) + { + aNetwork = mData->m->strNetwork; + Log(("Getter (this:%p) Network: %s\n", this, mData->m->strNetwork.c_str())); + } + return S_OK; +} + +HRESULT NATEngine::setHostIP(const com::Utf8Str &aHostIP) +{ + if (aHostIP.isNotEmpty()) + { + RTNETADDRIPV4 addr; + + /* parses as an IPv4 address */ + int rc = RTNetStrToIPv4Addr(aHostIP.c_str(), &addr); + if (RT_FAILURE(rc)) + return setError(E_INVALIDARG, "Invalid IPv4 address \"%s\"", aHostIP.c_str()); + + /* is a unicast address */ + if ((addr.u & RT_N2H_U32_C(0xe0000000)) == RT_N2H_U32_C(0xe0000000)) + return setError(E_INVALIDARG, "Cannot bind to a multicast address %s", aHostIP.c_str()); + } + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + if (mData->m->strBindIP != aHostIP) + { + mData->m.backup(); + mData->m->strBindIP = aHostIP; + mParent->i_setModified(Machine::IsModified_NetworkAdapters); + } + return S_OK; +} + +HRESULT NATEngine::getHostIP(com::Utf8Str &aBindIP) +{ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (!mData->m->strBindIP.isEmpty()) + aBindIP = mData->m->strBindIP; + return S_OK; +} + +HRESULT NATEngine::setLocalhostReachable(BOOL fLocalhostReachable) +{ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (mData->m->fLocalhostReachable != RT_BOOL(fLocalhostReachable)) + { + mData->m.backup(); + mData->m->fLocalhostReachable = RT_BOOL(fLocalhostReachable); + mParent->i_setModified(Machine::IsModified_NetworkAdapters); + } + return S_OK; +} + +HRESULT NATEngine::getLocalhostReachable(BOOL *pfLocalhostReachable) +{ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + *pfLocalhostReachable = mData->m->fLocalhostReachable; + return S_OK; +} + +HRESULT NATEngine::setTFTPPrefix(const com::Utf8Str &aTFTPPrefix) +{ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + if (mData->m->strTFTPPrefix != aTFTPPrefix) + { + mData->m.backup(); + mData->m->strTFTPPrefix = aTFTPPrefix; + mParent->i_setModified(Machine::IsModified_NetworkAdapters); + } + return S_OK; +} + + +HRESULT NATEngine::getTFTPPrefix(com::Utf8Str &aTFTPPrefix) +{ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (!mData->m->strTFTPPrefix.isEmpty()) + { + aTFTPPrefix = mData->m->strTFTPPrefix; + Log(("Getter (this:%p) TFTPPrefix: %s\n", this, mData->m->strTFTPPrefix.c_str())); + } + return S_OK; +} + +HRESULT NATEngine::setTFTPBootFile(const com::Utf8Str &aTFTPBootFile) +{ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + if (mData->m->strTFTPBootFile != aTFTPBootFile) + { + mData->m.backup(); + mData->m->strTFTPBootFile = aTFTPBootFile; + mParent->i_setModified(Machine::IsModified_NetworkAdapters); + } + return S_OK; +} + + +HRESULT NATEngine::getTFTPBootFile(com::Utf8Str &aTFTPBootFile) +{ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + if (!mData->m->strTFTPBootFile.isEmpty()) + { + aTFTPBootFile = mData->m->strTFTPBootFile; + Log(("Getter (this:%p) BootFile: %s\n", this, mData->m->strTFTPBootFile.c_str())); + } + return S_OK; +} + + +HRESULT NATEngine::setTFTPNextServer(const com::Utf8Str &aTFTPNextServer) +{ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + if (mData->m->strTFTPNextServer != aTFTPNextServer) + { + mData->m.backup(); + mData->m->strTFTPNextServer = aTFTPNextServer; + mParent->i_setModified(Machine::IsModified_NetworkAdapters); + } + return S_OK; +} + +HRESULT NATEngine::getTFTPNextServer(com::Utf8Str &aTFTPNextServer) +{ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + if (!mData->m->strTFTPNextServer.isEmpty()) + { + aTFTPNextServer = mData->m->strTFTPNextServer; + Log(("Getter (this:%p) NextServer: %s\n", this, mData->m->strTFTPNextServer.c_str())); + } + return S_OK; +} + +/* DNS */ +HRESULT NATEngine::setDNSPassDomain(BOOL aDNSPassDomain) +{ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (mData->m->fDNSPassDomain != RT_BOOL(aDNSPassDomain)) + { + mData->m.backup(); + mData->m->fDNSPassDomain = RT_BOOL(aDNSPassDomain); + mParent->i_setModified(Machine::IsModified_NetworkAdapters); + } + return S_OK; +} + +HRESULT NATEngine::getDNSPassDomain(BOOL *aDNSPassDomain) +{ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + *aDNSPassDomain = mData->m->fDNSPassDomain; + return S_OK; +} + + +HRESULT NATEngine::setDNSProxy(BOOL aDNSProxy) +{ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (mData->m->fDNSProxy != RT_BOOL(aDNSProxy)) + { + mData->m.backup(); + mData->m->fDNSProxy = RT_BOOL(aDNSProxy); + mParent->i_setModified(Machine::IsModified_NetworkAdapters); + } + return S_OK; +} + +HRESULT NATEngine::getDNSProxy(BOOL *aDNSProxy) +{ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + *aDNSProxy = mData->m->fDNSProxy; + return S_OK; +} + + +HRESULT NATEngine::getDNSUseHostResolver(BOOL *aDNSUseHostResolver) +{ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + *aDNSUseHostResolver = mData->m->fDNSUseHostResolver; + return S_OK; +} + + +HRESULT NATEngine::setDNSUseHostResolver(BOOL aDNSUseHostResolver) +{ + if (mData->m->fDNSUseHostResolver != RT_BOOL(aDNSUseHostResolver)) + { + mData->m.backup(); + mData->m->fDNSUseHostResolver = RT_BOOL(aDNSUseHostResolver); + mParent->i_setModified(Machine::IsModified_NetworkAdapters); + } + return S_OK; +} + +HRESULT NATEngine::setAliasMode(ULONG aAliasMode) +{ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + ULONG uAliasMode = (mData->m->fAliasUseSamePorts ? NATAliasMode_AliasUseSamePorts : 0); + uAliasMode |= (mData->m->fAliasLog ? NATAliasMode_AliasLog : 0); + uAliasMode |= (mData->m->fAliasProxyOnly ? NATAliasMode_AliasProxyOnly : 0); + if (uAliasMode != aAliasMode) + { + mData->m.backup(); + mData->m->fAliasUseSamePorts = RT_BOOL(aAliasMode & NATAliasMode_AliasUseSamePorts); + mData->m->fAliasLog = RT_BOOL(aAliasMode & NATAliasMode_AliasLog); + mData->m->fAliasProxyOnly = RT_BOOL(aAliasMode & NATAliasMode_AliasProxyOnly); + mParent->i_setModified(Machine::IsModified_NetworkAdapters); + } + return S_OK; +} + +HRESULT NATEngine::getAliasMode(ULONG *aAliasMode) +{ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + ULONG uAliasMode = (mData->m->fAliasUseSamePorts ? NATAliasMode_AliasUseSamePorts : 0); + uAliasMode |= (mData->m->fAliasLog ? NATAliasMode_AliasLog : 0); + uAliasMode |= (mData->m->fAliasProxyOnly ? NATAliasMode_AliasProxyOnly : 0); + *aAliasMode = uAliasMode; + return S_OK; +} + |