diff options
Diffstat (limited to 'src/VBox/Main/src-server/NATNetworkImpl.cpp')
-rw-r--r-- | src/VBox/Main/src-server/NATNetworkImpl.cpp | 1115 |
1 files changed, 1115 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/NATNetworkImpl.cpp b/src/VBox/Main/src-server/NATNetworkImpl.cpp new file mode 100644 index 00000000..65327250 --- /dev/null +++ b/src/VBox/Main/src-server/NATNetworkImpl.cpp @@ -0,0 +1,1115 @@ +/* $Id: NATNetworkImpl.cpp $ */ +/** @file + * INATNetwork implementation. + */ + +/* + * Copyright (C) 2013 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. + */ + +#include <string> +#include "NetworkServiceRunner.h" +#include "DHCPServerImpl.h" +#include "NATNetworkImpl.h" +#include "AutoCaller.h" +#include "Logging.h" + +#include <iprt/asm.h> +#include <iprt/cpp/utils.h> +#include <iprt/cidr.h> +#include <iprt/net.h> +#include <VBox/com/array.h> +#include <VBox/com/ptr.h> +#include <VBox/settings.h> + +#include "EventImpl.h" +#include "VBoxEvents.h" + +#include "VirtualBoxImpl.h" +#include <algorithm> +#include <list> + +#ifndef RT_OS_WINDOWS +# include <netinet/in.h> +#else +# define IN_LOOPBACKNET 127 +#endif + + +// constructor / destructor +///////////////////////////////////////////////////////////////////////////// + +struct NATNetwork::Data +{ + Data() + : fEnabled(TRUE) + , fIPv6Enabled(FALSE) + , fAdvertiseDefaultIPv6Route(FALSE) + , fNeedDhcpServer(TRUE) + , u32LoopbackIp6(0) + , offGateway(0) + , offDhcp(0) + { + IPv4Gateway.setNull(); + IPv4NetworkCidr.setNull(); + IPv6Prefix.setNull(); + IPv4DhcpServer.setNull(); + IPv4NetworkMask.setNull(); + IPv4DhcpServerLowerIp.setNull(); + IPv4DhcpServerUpperIp.setNull(); + } + virtual ~Data(){} + const ComObjPtr<EventSource> pEventSource; +#ifdef VBOX_WITH_NAT_SERVICE + NATNetworkServiceRunner NATRunner; + ComObjPtr<IDHCPServer> dhcpServer; +#endif + Bstr IPv4Gateway; + Bstr IPv4NetworkCidr; + Bstr IPv4NetworkMask; + Bstr IPv4DhcpServer; + Bstr IPv4DhcpServerLowerIp; + Bstr IPv4DhcpServerUpperIp; + BOOL fEnabled; + BOOL fIPv6Enabled; + Bstr IPv6Prefix; + BOOL fAdvertiseDefaultIPv6Route; + BOOL fNeedDhcpServer; + NATRuleMap mapName2PortForwardRule4; + NATRuleMap mapName2PortForwardRule6; + settings::NATLoopbackOffsetList llNATLoopbackOffsetList; + uint32_t u32LoopbackIp6; + uint32_t offGateway; + uint32_t offDhcp; +}; + + +NATNetwork::NATNetwork() + : mVirtualBox(NULL) +{ +} + + +NATNetwork::~NATNetwork() +{ +} + + +HRESULT NATNetwork::FinalConstruct() +{ + return BaseFinalConstruct(); +} + + +void NATNetwork::FinalRelease() +{ + uninit (); + + BaseFinalRelease(); +} + + +void NATNetwork::uninit() +{ + /* Enclose the state transition Ready->InUninit->NotReady */ + AutoUninitSpan autoUninitSpan(this); + if (autoUninitSpan.uninitDone()) + return; + delete m; + m = NULL; + unconst(mVirtualBox) = NULL; +} + + +HRESULT NATNetwork::init(VirtualBox *aVirtualBox, IN_BSTR aName) +{ + AssertReturn(aName != NULL, E_INVALIDARG); + + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), E_FAIL); + + /* share VirtualBox weakly (parent remains NULL so far) */ + unconst(mVirtualBox) = aVirtualBox; + unconst(mName) = aName; + m = new Data(); + m->offGateway = 1; + m->IPv4NetworkCidr = "10.0.2.0/24"; + m->IPv6Prefix = "fe80::/64"; + + settings::NATHostLoopbackOffset off; + off.strLoopbackHostAddress = "127.0.0.1"; + off.u32Offset = (uint32_t)2; + m->llNATLoopbackOffsetList.push_back(off); + + recalculateIpv4AddressAssignments(); + + HRESULT hrc = unconst(m->pEventSource).createObject(); + if (FAILED(hrc)) throw hrc; + + hrc = m->pEventSource->init(); + if (FAILED(hrc)) throw hrc; + + /* Confirm a successful initialization */ + autoInitSpan.setSucceeded(); + + return S_OK; +} + + +HRESULT NATNetwork::init(VirtualBox *aVirtualBox, + const settings::NATNetwork &data) +{ + /* Enclose the state transition NotReady->InInit->Ready */ + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), E_FAIL); + + /* share VirtualBox weakly (parent remains NULL so far) */ + unconst(mVirtualBox) = aVirtualBox; + + unconst(mName) = data.strNetworkName; + m = new Data(); + m->IPv4NetworkCidr = data.strNetwork; + m->fEnabled = data.fEnabled; + m->fAdvertiseDefaultIPv6Route = data.fAdvertiseDefaultIPv6Route; + m->fNeedDhcpServer = data.fNeedDhcpServer; + m->fIPv6Enabled = data.fIPv6; + + m->u32LoopbackIp6 = data.u32HostLoopback6Offset; + + m->llNATLoopbackOffsetList.clear(); + m->llNATLoopbackOffsetList.assign(data.llHostLoopbackOffsetList.begin(), + data.llHostLoopbackOffsetList.end()); + + recalculateIpv4AddressAssignments(); + + /* IPv4 port-forward rules */ + m->mapName2PortForwardRule4.clear(); + for (settings::NATRuleList::const_iterator it = data.llPortForwardRules4.begin(); + it != data.llPortForwardRules4.end(); ++it) + { + m->mapName2PortForwardRule4.insert(std::make_pair(it->strName.c_str(), *it)); + } + + /* IPv6 port-forward rules */ + m->mapName2PortForwardRule6.clear(); + for (settings::NATRuleList::const_iterator it = data.llPortForwardRules6.begin(); + it != data.llPortForwardRules6.end(); ++it) + { + m->mapName2PortForwardRule6.insert(std::make_pair(it->strName, *it)); + } + + HRESULT hrc = unconst(m->pEventSource).createObject(); + if (FAILED(hrc)) throw hrc; + + hrc = m->pEventSource->init(); + if (FAILED(hrc)) throw hrc; + + autoInitSpan.setSucceeded(); + + return S_OK; +} + + +HRESULT NATNetwork::saveSettings(settings::NATNetwork &data) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + data.strNetworkName = mName; + data.strNetwork = m->IPv4NetworkCidr; + data.fEnabled = RT_BOOL(m->fEnabled); + data.fAdvertiseDefaultIPv6Route = RT_BOOL(m->fAdvertiseDefaultIPv6Route); + data.fNeedDhcpServer = RT_BOOL(m->fNeedDhcpServer); + data.fIPv6 = RT_BOOL(m->fIPv6Enabled); + data.strIPv6Prefix = m->IPv6Prefix; + + /* saving ipv4 port-forward Rules*/ + data.llPortForwardRules4.clear(); + for (NATRuleMap::iterator it = m->mapName2PortForwardRule4.begin(); + it != m->mapName2PortForwardRule4.end(); ++it) + data.llPortForwardRules4.push_back(it->second); + + /* saving ipv6 port-forward Rules*/ + data.llPortForwardRules6.clear(); + for (NATRuleMap::iterator it = m->mapName2PortForwardRule6.begin(); + it != m->mapName2PortForwardRule6.end(); ++it) + data.llPortForwardRules6.push_back(it->second); + + data.u32HostLoopback6Offset = m->u32LoopbackIp6; + + data.llHostLoopbackOffsetList.clear(); + data.llHostLoopbackOffsetList.assign(m->llNATLoopbackOffsetList.begin(), + m->llNATLoopbackOffsetList.end()); + + mVirtualBox->onNATNetworkSetting(mName.raw(), + data.fEnabled ? TRUE : FALSE, + m->IPv4NetworkCidr.raw(), + m->IPv4Gateway.raw(), + data.fAdvertiseDefaultIPv6Route ? TRUE : FALSE, + data.fNeedDhcpServer ? TRUE : FALSE); + + /* Notify listerners listening on this network only */ + fireNATNetworkSettingEvent(m->pEventSource, + mName.raw(), + data.fEnabled ? TRUE : FALSE, + m->IPv4NetworkCidr.raw(), + m->IPv4Gateway.raw(), + data.fAdvertiseDefaultIPv6Route ? TRUE : FALSE, + data.fNeedDhcpServer ? TRUE : FALSE); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(EventSource)(IEventSource ** aEventSource) +{ + CheckComArgOutPointerValid(aEventSource); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + /* event source is const, no need to lock */ + m->pEventSource.queryInterfaceTo(aEventSource); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(NetworkName)(BSTR *aName) +{ + CheckComArgOutPointerValid(aName); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + mName.cloneTo(aName); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(NetworkName)(IN_BSTR aName) +{ + CheckComArgOutPointerValid(aName); + AutoCaller autoCaller(this); + + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aName == mName) + return S_OK; + + unconst(mName) = aName; + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(Enabled)(BOOL *aEnabled) +{ + CheckComArgOutPointerValid(aEnabled); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + *aEnabled = m->fEnabled; + recalculateIpv4AddressAssignments(); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(Enabled)(BOOL aEnabled) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aEnabled == m->fEnabled) + return S_OK; + + m->fEnabled = aEnabled; + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(Gateway)(BSTR *aIPv4Gateway) +{ + CheckComArgOutPointerValid(aIPv4Gateway); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + m->IPv4Gateway.cloneTo(aIPv4Gateway); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(Network)(BSTR *aIPv4NetworkCidr) +{ + CheckComArgOutPointerValid(aIPv4NetworkCidr); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + m->IPv4NetworkCidr.cloneTo(aIPv4NetworkCidr); + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(Network)(IN_BSTR aIPv4NetworkCidr) +{ + CheckComArgOutPointerValid(aIPv4NetworkCidr); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aIPv4NetworkCidr == m->IPv4NetworkCidr) + return S_OK; + + /* silently ignore network cidr update for now. + * todo: keep internally guest address of port forward rule + * as offset from network id. + */ + if (!m->mapName2PortForwardRule4.empty()) + return S_OK; + + unconst(m->IPv4NetworkCidr) = Bstr(aIPv4NetworkCidr); + recalculateIpv4AddressAssignments(); + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(IPv6Enabled)(BOOL *aIPv6Enabled) +{ + CheckComArgOutPointerValid(aIPv6Enabled); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + *aIPv6Enabled = m->fIPv6Enabled; + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(IPv6Enabled)(BOOL aIPv6Enabled) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aIPv6Enabled == m->fIPv6Enabled) + return S_OK; + + m->fIPv6Enabled = aIPv6Enabled; + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(IPv6Prefix) (BSTR *aIPv6Prefix) +{ + CheckComArgOutPointerValid(aIPv6Prefix); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + m->IPv6Prefix.cloneTo(aIPv6Prefix); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(IPv6Prefix) (IN_BSTR aIPv6Prefix) +{ + CheckComArgOutPointerValid(aIPv6Prefix); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aIPv6Prefix == m->IPv6Prefix) + return S_OK; + + /* silently ignore network IPv6 prefix update. + * todo: see similar todo in NATNetwork::COMSETTER(Network)(IN_BSTR) + */ + if (!m->mapName2PortForwardRule6.empty()) + return S_OK; + + unconst(m->IPv6Prefix) = Bstr(aIPv6Prefix); + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(AdvertiseDefaultIPv6RouteEnabled)(BOOL *aAdvertiseDefaultIPv6Route) +{ + CheckComArgOutPointerValid(aAdvertiseDefaultIPv6Route); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + *aAdvertiseDefaultIPv6Route = m->fAdvertiseDefaultIPv6Route; + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(AdvertiseDefaultIPv6RouteEnabled)(BOOL aAdvertiseDefaultIPv6Route) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aAdvertiseDefaultIPv6Route == m->fAdvertiseDefaultIPv6Route) + return S_OK; + + m->fAdvertiseDefaultIPv6Route = aAdvertiseDefaultIPv6Route; + + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(NeedDhcpServer)(BOOL *aNeedDhcpServer) +{ + CheckComArgOutPointerValid(aNeedDhcpServer); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + *aNeedDhcpServer = m->fNeedDhcpServer; + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(NeedDhcpServer)(BOOL aNeedDhcpServer) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aNeedDhcpServer == m->fNeedDhcpServer) + return S_OK; + + m->fNeedDhcpServer = aNeedDhcpServer; + + recalculateIpv4AddressAssignments(); + + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(LocalMappings)(ComSafeArrayOut(BSTR, aLocalMappings)) +{ + CheckComArgOutSafeArrayPointerValid(aLocalMappings); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + com::SafeArray<BSTR> sf(m->llNATLoopbackOffsetList.size()); + + size_t i = 0; + settings::NATLoopbackOffsetList::const_iterator it; + + for (it = m->llNATLoopbackOffsetList.begin(); + it != m->llNATLoopbackOffsetList.end(); ++it, ++i) + { + BstrFmt bstr("%s=%d", + (*it).strLoopbackHostAddress.c_str(), + (*it).u32Offset); + bstr.detachTo(&sf[i]); + } + sf.detachTo(ComSafeArrayOutArg(aLocalMappings)); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::AddLocalMapping(IN_BSTR aHostId, LONG aOffset) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + //AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + RTNETADDRIPV4 addr, net, mask; + + int rc = RTNetStrToIPv4Addr(Utf8Str(aHostId).c_str(), &addr); + if (RT_FAILURE(rc)) + return E_INVALIDARG; + + /* check against 127/8 */ + if ((RT_N2H_U32(addr.u) >> IN_CLASSA_NSHIFT) != IN_LOOPBACKNET) + return E_INVALIDARG; + + /* check against networkid vs network mask */ + rc = RTCidrStrToIPv4(Utf8Str(m->IPv4NetworkCidr).c_str(), &net, &mask); + if (RT_FAILURE(rc)) + return E_INVALIDARG; + + if (((net.u + aOffset) & mask.u) != net.u) + return E_INVALIDARG; + + settings::NATLoopbackOffsetList::iterator it; + + it = std::find(m->llNATLoopbackOffsetList.begin(), + m->llNATLoopbackOffsetList.end(), + Utf8Str(aHostId).c_str()); + + if (it != m->llNATLoopbackOffsetList.end()) + { + if (aOffset == 0) /* erase */ + m->llNATLoopbackOffsetList.erase(it, it); + else /* modify */ + { + settings::NATLoopbackOffsetList::iterator it1; + it1 = std::find(m->llNATLoopbackOffsetList.begin(), + m->llNATLoopbackOffsetList.end(), + (uint32_t)aOffset); + if (it1 != m->llNATLoopbackOffsetList.end()) + return E_INVALIDARG; /* this offset is already registered. */ + + (*it).u32Offset = aOffset; + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + return mVirtualBox->saveSettings(); + } + + /* injection */ + it = std::find(m->llNATLoopbackOffsetList.begin(), + m->llNATLoopbackOffsetList.end(), + (uint32_t)aOffset); + + if (it != m->llNATLoopbackOffsetList.end()) + return E_INVALIDARG; /* offset is already registered. */ + + settings::NATHostLoopbackOffset off; + off.strLoopbackHostAddress = aHostId; + off.u32Offset = (uint32_t)aOffset; + m->llNATLoopbackOffsetList.push_back(off); + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + return mVirtualBox->saveSettings(); +} + + +STDMETHODIMP NATNetwork::COMGETTER(LoopbackIp6)(LONG *aLoopbackIp6) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + *aLoopbackIp6 = m->u32LoopbackIp6; + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(LoopbackIp6)(LONG aLoopbackIp6) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aLoopbackIp6 < 0) + return E_INVALIDARG; + + if (static_cast<uint32_t>(aLoopbackIp6) == m->u32LoopbackIp6) + return S_OK; + + m->u32LoopbackIp6 = aLoopbackIp6; + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + return mVirtualBox->saveSettings(); +} + + +STDMETHODIMP NATNetwork::COMGETTER(PortForwardRules4)(ComSafeArrayOut(BSTR, aPortForwardRules4)) +{ + CheckComArgOutSafeArrayPointerValid(aPortForwardRules4); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + GetPortForwardRulesFromMap(ComSafeArrayInArg(aPortForwardRules4), + m->mapName2PortForwardRule4); + return S_OK; +} + +STDMETHODIMP NATNetwork::COMGETTER(PortForwardRules6)(ComSafeArrayOut(BSTR, + aPortForwardRules6)) +{ + CheckComArgOutSafeArrayPointerValid(aPortForwardRules6); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + GetPortForwardRulesFromMap(ComSafeArrayInArg(aPortForwardRules6), m->mapName2PortForwardRule6); + return S_OK; +} + + +STDMETHODIMP NATNetwork::AddPortForwardRule(BOOL aIsIpv6, + IN_BSTR aPortForwardRuleName, + NATProtocol_T aProto, + IN_BSTR aHostIp, + USHORT aHostPort, + IN_BSTR aGuestIp, + USHORT aGuestPort) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + Utf8Str name = aPortForwardRuleName; + Utf8Str proto; + settings::NATRule r; + NATRuleMap& mapRules = aIsIpv6 ? m->mapName2PortForwardRule6 : m->mapName2PortForwardRule4; + switch (aProto) + { + case NATProtocol_TCP: + proto = "tcp"; + break; + case NATProtocol_UDP: + proto = "udp"; + break; + default: + return E_INVALIDARG; + } + if (name.isEmpty()) + name = Utf8StrFmt("%s_[%s]%%%d_[%s]%%%d", proto.c_str(), + Utf8Str(aHostIp).c_str(), aHostPort, + Utf8Str(aGuestIp).c_str(), aGuestPort); + + NATRuleMap::iterator it; + + for (it = mapRules.begin(); it != 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 == Utf8Str(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")); + } + + r.strName = name.c_str(); + r.proto = aProto; + r.strHostIP = aHostIp; + r.u16HostPort = aHostPort; + r.strGuestIP = aGuestIp; + r.u16GuestPort = aGuestPort; + mapRules.insert(std::make_pair(name, r)); + } + + { + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + } + + mVirtualBox->onNATNetworkPortForward(mName.raw(), TRUE, aIsIpv6, + aPortForwardRuleName, aProto, + aHostIp, aHostPort, + aGuestIp, aGuestPort); + + /* Notify listerners listening on this network only */ + fireNATNetworkPortForwardEvent(m->pEventSource, mName.raw(), TRUE, + aIsIpv6, aPortForwardRuleName, aProto, + aHostIp, aHostPort, + aGuestIp, aGuestPort); + return S_OK; +} + + +STDMETHODIMP NATNetwork::RemovePortForwardRule(BOOL aIsIpv6, IN_BSTR aPortForwardRuleName) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + Utf8Str strHostIP; + Utf8Str strGuestIP; + uint16_t u16HostPort; + uint16_t u16GuestPort; + NATProtocol_T proto; + + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + NATRuleMap& mapRules = aIsIpv6 ? m->mapName2PortForwardRule6 + : m->mapName2PortForwardRule4; + + NATRuleMap::iterator it = mapRules.find(aPortForwardRuleName); + + if (it == mapRules.end()) + return E_INVALIDARG; + + strHostIP = it->second.strHostIP; + strGuestIP = it->second.strGuestIP; + u16HostPort = it->second.u16HostPort; + u16GuestPort = it->second.u16GuestPort; + proto = it->second.proto; + + mapRules.erase(it); + } + + { + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + } + + mVirtualBox->onNATNetworkPortForward(mName.raw(), FALSE, aIsIpv6, + aPortForwardRuleName, proto, + Bstr(strHostIP).raw(), u16HostPort, + Bstr(strGuestIP).raw(), u16GuestPort); + + /* Notify listerners listening on this network only */ + fireNATNetworkPortForwardEvent(m->pEventSource, mName.raw(), FALSE, + aIsIpv6, aPortForwardRuleName, proto, + Bstr(strHostIP).raw(), u16HostPort, + Bstr(strGuestIP).raw(), u16GuestPort); + return S_OK; +} + + +STDMETHODIMP NATNetwork::Start(IN_BSTR aTrunkType) +{ +#ifdef VBOX_WITH_NAT_SERVICE + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + if (!m->fEnabled) return S_OK; + + m->NATRunner.setOption(NetworkServiceRunner::kNsrKeyNetwork, Utf8Str(mName).c_str()); + m->NATRunner.setOption(NetworkServiceRunner::kNsrKeyTrunkType, Utf8Str(aTrunkType).c_str()); + m->NATRunner.setOption(NetworkServiceRunner::kNsrIpAddress, Utf8Str(m->IPv4Gateway).c_str()); + m->NATRunner.setOption(NetworkServiceRunner::kNsrIpNetmask, Utf8Str(m->IPv4NetworkMask).c_str()); + + /* No portforwarding rules from command-line, all will be fetched via API */ + + if (m->fNeedDhcpServer) + { + /* + * Just to as idea... via API (on creation user pass the cidr of network and) + * and we calculate it's addreses (mutable?). + */ + + /* + * Configuration and running DHCP server: + * 1. find server first createDHCPServer + * 2. if return status is E_INVALARG => server already exists just find and start. + * 3. if return status neither E_INVALRG nor S_OK => return E_FAIL + * 4. if return status S_OK proceed to DHCP server configuration + * 5. call setConfiguration() and pass all required parameters + * 6. start dhcp server. + */ + HRESULT hrc = mVirtualBox->FindDHCPServerByNetworkName(mName.raw(), + m->dhcpServer.asOutParam()); + switch (hrc) + { + case E_INVALIDARG: + /* server haven't beeen found let create it then */ + hrc = mVirtualBox->CreateDHCPServer(mName.raw(), + m->dhcpServer.asOutParam()); + if (FAILED(hrc)) + return E_FAIL; + /* breakthrough */ + + { + LogFunc(("gateway: %s, dhcpserver:%s, dhcplowerip:%s, dhcpupperip:%s\n", + Utf8Str(m->IPv4Gateway.raw()).c_str(), + Utf8Str(m->IPv4DhcpServer.raw()).c_str(), + Utf8Str(m->IPv4DhcpServerLowerIp.raw()).c_str(), + Utf8Str(m->IPv4DhcpServerUpperIp.raw()).c_str())); + + hrc = m->dhcpServer->COMSETTER(Enabled)(true); + + BSTR dhcpip = NULL; + BSTR netmask = NULL; + BSTR lowerip = NULL; + BSTR upperip = NULL; + + m->IPv4DhcpServer.cloneTo(&dhcpip); + m->IPv4NetworkMask.cloneTo(&netmask); + m->IPv4DhcpServerLowerIp.cloneTo(&lowerip); + m->IPv4DhcpServerUpperIp.cloneTo(&upperip); + hrc = m->dhcpServer->SetConfiguration(dhcpip, + netmask, + lowerip, + upperip); + } + case S_OK: + break; + + default: + return E_FAIL; + } + + /* XXX: AddGlobalOption(DhcpOpt_Router,) - enables attachement of DhcpServer to Main. */ + m->dhcpServer->AddGlobalOption(DhcpOpt_Router, m->IPv4Gateway.raw()); + + hrc = m->dhcpServer->Start(mName.raw(), Bstr("").raw(), aTrunkType); + if (FAILED(hrc)) + { + m->dhcpServer.setNull(); + return E_FAIL; + } + } + + if (RT_SUCCESS(m->NATRunner.start())) + { + mVirtualBox->onNATNetworkStartStop(mName.raw(), TRUE); + return S_OK; + } + /** @todo missing setError()! */ + return E_FAIL; +#else + NOREF(aTrunkType); + ReturnComNotImplemented(); +#endif +} + + +STDMETHODIMP NATNetwork::Stop() +{ +#ifdef VBOX_WITH_NAT_SERVICE + if (!m->dhcpServer.isNull()) + m->dhcpServer->Stop(); + + if (RT_SUCCESS(m->NATRunner.stop())) + { + mVirtualBox->onNATNetworkStartStop(mName.raw(), FALSE); + return S_OK; + } + /** @todo missing setError()! */ + return E_FAIL; +#else + ReturnComNotImplemented(); +#endif +} + + +void NATNetwork::GetPortForwardRulesFromMap(ComSafeArrayOut(BSTR, aPortForwardRules), NATRuleMap& aRules) +{ + com::SafeArray<BSTR> sf(aRules.size()); + size_t i = 0; + NATRuleMap::const_iterator it; + for (it = aRules.begin(); + it != aRules.end(); ++it, ++i) + { + settings::NATRule r = it->second; + BstrFmt bstr("%s:%s:[%s]:%d:[%s]:%d", + r.strName.c_str(), + (r.proto == NATProtocol_TCP? "tcp" : "udp"), + r.strHostIP.c_str(), + r.u16HostPort, + r.strGuestIP.c_str(), + r.u16GuestPort); + bstr.detachTo(&sf[i]); + } + sf.detachTo(ComSafeArrayOutArg(aPortForwardRules)); +} + + +int NATNetwork::findFirstAvailableOffset(ADDRESSLOOKUPTYPE addrType, uint32_t *poff) +{ + RTNETADDRIPV4 network, netmask; + + int rc = RTCidrStrToIPv4(Utf8Str(m->IPv4NetworkCidr.raw()).c_str(), + &network, + &netmask); + AssertRCReturn(rc, rc); + + uint32_t off; + settings::NATLoopbackOffsetList::iterator it; + for (off = 1; off < ~netmask.u; ++off) + { + + bool skip = false; + for (it = m->llNATLoopbackOffsetList.begin(); + it != m->llNATLoopbackOffsetList.end(); + ++it) + { + if ((*it).u32Offset == off) + { + skip = true; + break; + } + + } + + if (skip) + continue; + + if (off == m->offGateway) + { + if (addrType == ADDR_GATEWAY) + break; + else + continue; + } + + if (off == m->offDhcp) + { + if (addrType == ADDR_DHCP) + break; + else + continue; + } + + if (!skip) + break; + } + + if (poff) + *poff = off; + + return VINF_SUCCESS; +} + + +int NATNetwork::recalculateIpv4AddressAssignments() +{ + RTNETADDRIPV4 network, netmask; + int rc = RTCidrStrToIPv4(Utf8Str(m->IPv4NetworkCidr.raw()).c_str(), + &network, + &netmask); + AssertRCReturn(rc, rc); + + findFirstAvailableOffset(ADDR_GATEWAY, &m->offGateway); + if (m->fNeedDhcpServer) + findFirstAvailableOffset(ADDR_DHCP, &m->offDhcp); + + /* I don't remember the reason CIDR calculated on the host. */ + RTNETADDRIPV4 gateway = network; + gateway.u += m->offGateway; + gateway.u = RT_H2N_U32(gateway.u); + char szTmpIp[16]; + RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", gateway); + m->IPv4Gateway = szTmpIp; + + if (m->fNeedDhcpServer) + { + RTNETADDRIPV4 dhcpserver = network; + dhcpserver.u += m->offDhcp; + + /* XXX: adding more services should change the math here */ + RTNETADDRIPV4 dhcplowerip = network; + uint32_t offDhcpLowerIp; + findFirstAvailableOffset(ADDR_DHCPLOWERIP, &offDhcpLowerIp); + dhcplowerip.u = RT_H2N_U32(dhcplowerip.u + offDhcpLowerIp); + + RTNETADDRIPV4 dhcpupperip; + dhcpupperip.u = RT_H2N_U32((network.u | ~netmask.u) - 1); + + dhcpserver.u = RT_H2N_U32(dhcpserver.u); + network.u = RT_H2N_U32(network.u); + + RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcpserver); + m->IPv4DhcpServer = szTmpIp; + RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcplowerip); + m->IPv4DhcpServerLowerIp = szTmpIp; + RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcpupperip); + m->IPv4DhcpServerUpperIp = szTmpIp; + + LogFunc(("network:%RTnaipv4, dhcpserver:%RTnaipv4, dhcplowerip:%RTnaipv4, dhcpupperip:%RTnaipv4\n", + network, dhcpserver, dhcplowerip, dhcpupperip)); + } + + /* we need IPv4NetworkMask for NAT's gw service start */ + netmask.u = RT_H2N_U32(netmask.u); + RTStrPrintf(szTmpIp, 16, "%RTnaipv4", netmask); + m->IPv4NetworkMask = szTmpIp; + + LogFlowFunc(("getaway:%RTnaipv4, netmask:%RTnaipv4\n", gateway, netmask)); + return VINF_SUCCESS; +} |