VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/NATNetworkImpl.cpp@ 88539

Last change on this file since 88539 was 87949, checked in by vboxsync, 4 years ago

NAT/Net: Refuse to modify NATNetwork when an instance of it is
running. Use existing infrastructure that keeps track of the number
of VMs that use any given natnet (though it's exposes to natnet in a
somewhat haphazard way). bugref:9909.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.7 KB
Line 
1/* $Id: NATNetworkImpl.cpp 87949 2021-03-04 12:49:43Z vboxsync $ */
2/** @file
3 * INATNetwork implementation.
4 */
5
6/*
7 * Copyright (C) 2013-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_NATNETWORK
19#include "NetworkServiceRunner.h"
20#include "DHCPServerImpl.h"
21#include "NATNetworkImpl.h"
22#include "AutoCaller.h"
23
24#include <iprt/asm.h>
25#include <iprt/cpp/utils.h>
26#include <iprt/net.h>
27#include <iprt/cidr.h>
28#include <iprt/net.h>
29#include <VBox/com/array.h>
30#include <VBox/com/ptr.h>
31#include <VBox/settings.h>
32
33#include "EventImpl.h"
34#include "LoggingNew.h"
35
36#include "VirtualBoxImpl.h"
37#include <algorithm>
38#include <list>
39
40#ifndef RT_OS_WINDOWS
41# include <netinet/in.h>
42#else
43# define IN_LOOPBACKNET 127
44#endif
45
46
47// constructor / destructor
48/////////////////////////////////////////////////////////////////////////////
49struct NATNetwork::Data
50{
51 Data()
52 : pVirtualBox(NULL)
53 , offGateway(0)
54 , offDhcp(0)
55 {
56 }
57 virtual ~Data(){}
58 const ComObjPtr<EventSource> pEventSource;
59#ifdef VBOX_WITH_NAT_SERVICE
60 NATNetworkServiceRunner NATRunner;
61 ComObjPtr<IDHCPServer> dhcpServer;
62#endif
63 /** weak VirtualBox parent */
64 VirtualBox * const pVirtualBox;
65
66 /** NATNetwork settings */
67 settings::NATNetwork s;
68
69 com::Utf8Str IPv4Gateway;
70 com::Utf8Str IPv4NetworkMask;
71 com::Utf8Str IPv4DhcpServer;
72 com::Utf8Str IPv4DhcpServerLowerIp;
73 com::Utf8Str IPv4DhcpServerUpperIp;
74
75 uint32_t offGateway;
76 uint32_t offDhcp;
77
78 void recalculatePortForwarding(const RTNETADDRIPV4 &AddrNew, const RTNETADDRIPV4 &MaskNew);
79};
80
81
82NATNetwork::NATNetwork()
83 : m(NULL)
84{
85}
86
87
88NATNetwork::~NATNetwork()
89{
90}
91
92
93HRESULT NATNetwork::FinalConstruct()
94{
95 return BaseFinalConstruct();
96}
97
98
99void NATNetwork::FinalRelease()
100{
101 uninit();
102
103 BaseFinalRelease();
104}
105
106
107void NATNetwork::uninit()
108{
109 /* Enclose the state transition Ready->InUninit->NotReady */
110 AutoUninitSpan autoUninitSpan(this);
111 if (autoUninitSpan.uninitDone())
112 return;
113 unconst(m->pVirtualBox) = NULL;
114 delete m;
115 m = NULL;
116}
117
118HRESULT NATNetwork::init(VirtualBox *aVirtualBox, com::Utf8Str aName)
119{
120 AutoInitSpan autoInitSpan(this);
121 AssertReturn(autoInitSpan.isOk(), E_FAIL);
122
123 m = new Data();
124 /* share VirtualBox weakly */
125 unconst(m->pVirtualBox) = aVirtualBox;
126 m->s.strNetworkName = aName;
127 m->s.strIPv4NetworkCidr = "10.0.2.0/24";
128 m->offGateway = 1;
129 i_recalculateIPv6Prefix(); /* set m->strIPv6Prefix based on IPv4 */
130
131 settings::NATHostLoopbackOffset off;
132 off.strLoopbackHostAddress = "127.0.0.1";
133 off.u32Offset = (uint32_t)2;
134 m->s.llHostLoopbackOffsetList.push_back(off);
135
136 i_recalculateIpv4AddressAssignments();
137
138 HRESULT hrc = unconst(m->pEventSource).createObject();
139 if (FAILED(hrc)) throw hrc;
140
141 hrc = m->pEventSource->init();
142 if (FAILED(hrc)) throw hrc;
143
144 /* Confirm a successful initialization */
145 autoInitSpan.setSucceeded();
146
147 return S_OK;
148}
149
150
151HRESULT NATNetwork::setErrorBusy()
152{
153 return setError(E_FAIL,
154 "Unable to change settings"
155 " while NATNetwork instance is running");
156}
157
158
159HRESULT NATNetwork::i_loadSettings(const settings::NATNetwork &data)
160{
161 AutoCaller autoCaller(this);
162 AssertComRCReturnRC(autoCaller.rc());
163
164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
165 m->s = data;
166 if ( m->s.strIPv6Prefix.isEmpty()
167 /* also clean up bogus old default */
168 || m->s.strIPv6Prefix == "fe80::/64")
169 i_recalculateIPv6Prefix(); /* set m->strIPv6Prefix based on IPv4 */
170 i_recalculateIpv4AddressAssignments();
171
172 return S_OK;
173}
174
175HRESULT NATNetwork::i_saveSettings(settings::NATNetwork &data)
176{
177 AutoCaller autoCaller(this);
178 if (FAILED(autoCaller.rc())) return autoCaller.rc();
179
180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
181 AssertReturn(!m->s.strNetworkName.isEmpty(), E_FAIL);
182 data = m->s;
183
184 m->pVirtualBox->i_onNATNetworkSetting(m->s.strNetworkName,
185 m->s.fEnabled,
186 m->s.strIPv4NetworkCidr,
187 m->IPv4Gateway,
188 m->s.fAdvertiseDefaultIPv6Route,
189 m->s.fNeedDhcpServer);
190
191 /* Notify listerners listening on this network only */
192 ::FireNATNetworkSettingEvent(m->pEventSource,
193 m->s.strNetworkName,
194 m->s.fEnabled,
195 m->s.strIPv4NetworkCidr,
196 m->IPv4Gateway,
197 m->s.fAdvertiseDefaultIPv6Route,
198 m->s.fNeedDhcpServer);
199
200 return S_OK;
201}
202
203HRESULT NATNetwork::getEventSource(ComPtr<IEventSource> &aEventSource)
204{
205 /* event source is const, no need to lock */
206 m->pEventSource.queryInterfaceTo(aEventSource.asOutParam());
207 return S_OK;
208}
209
210HRESULT NATNetwork::getNetworkName(com::Utf8Str &aNetworkName)
211{
212 AssertReturn(!m->s.strNetworkName.isEmpty(), E_FAIL);
213 aNetworkName = m->s.strNetworkName;
214 return S_OK;
215}
216
217HRESULT NATNetwork::setNetworkName(const com::Utf8Str &aNetworkName)
218{
219 if (aNetworkName.isEmpty())
220 return setError(E_INVALIDARG,
221 tr("Network name cannot be empty"));
222
223 {
224 AutoReadLock alockNatNetList(m->pVirtualBox->i_getNatNetLock() COMMA_LOCKVAL_SRC_POS);
225 if (m->pVirtualBox->i_isNatNetStarted(m->s.strNetworkName))
226 return setErrorBusy();
227
228 /** @todo r=uwe who ensures there's no other network with that name? */
229
230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
231 if (aNetworkName == m->s.strNetworkName)
232 return S_OK;
233
234 m->s.strNetworkName = aNetworkName;
235 }
236
237
238 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
239 HRESULT rc = m->pVirtualBox->i_saveSettings();
240 ComAssertComRCRetRC(rc);
241
242 return S_OK;
243}
244
245HRESULT NATNetwork::getEnabled(BOOL *aEnabled)
246{
247 *aEnabled = m->s.fEnabled;
248
249 i_recalculateIpv4AddressAssignments();
250 return S_OK;
251}
252
253HRESULT NATNetwork::setEnabled(const BOOL aEnabled)
254{
255 {
256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
257 if (RT_BOOL(aEnabled) == m->s.fEnabled)
258 return S_OK;
259 m->s.fEnabled = RT_BOOL(aEnabled);
260 }
261
262 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
263 HRESULT rc = m->pVirtualBox->i_saveSettings();
264 ComAssertComRCRetRC(rc);
265 return S_OK;
266}
267
268HRESULT NATNetwork::getGateway(com::Utf8Str &aIPv4Gateway)
269{
270 aIPv4Gateway = m->IPv4Gateway;
271 return S_OK;
272}
273
274HRESULT NATNetwork::getNetwork(com::Utf8Str &aNetwork)
275{
276 aNetwork = m->s.strIPv4NetworkCidr;
277 return S_OK;
278}
279
280
281HRESULT NATNetwork::setNetwork(const com::Utf8Str &aIPv4NetworkCidr)
282{
283 RTNETADDRIPV4 Net, Mask;
284 int iPrefix;
285 int rc;
286
287 rc = RTNetStrToIPv4Cidr(aIPv4NetworkCidr.c_str(), &Net, &iPrefix);
288 if (RT_FAILURE(rc))
289 return setError(E_FAIL, "%s is not a valid IPv4 CIDR notation",
290 aIPv4NetworkCidr.c_str());
291
292 /*
293 * /32 is a single address, not a network, /31 is the degenerate
294 * point-to-point case, so reject these. Larger values and
295 * non-positive values are already treated as errors by the
296 * conversion.
297 */
298 if (iPrefix > 30)
299 return setError(E_FAIL, "%s network is too small", aIPv4NetworkCidr.c_str());
300
301 rc = RTNetPrefixToMaskIPv4(iPrefix, &Mask);
302 AssertRCReturn(rc, setError(E_FAIL,
303 "%s: internal error: failed to convert prefix %d to netmask: %Rrc",
304 aIPv4NetworkCidr.c_str(), iPrefix, rc));
305
306 if ((Net.u & ~Mask.u) != 0)
307 return setError(E_FAIL,
308 "%s: the specified address is longer than the specified prefix",
309 aIPv4NetworkCidr.c_str());
310
311 /* normalized CIDR notation */
312 com::Utf8StrFmt strCidr("%RTnaipv4/%d", Net.u, iPrefix);
313
314 {
315 AutoReadLock alockNatNetList(m->pVirtualBox->i_getNatNetLock() COMMA_LOCKVAL_SRC_POS);
316 if (m->pVirtualBox->i_isNatNetStarted(m->s.strNetworkName))
317 return setErrorBusy();
318
319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
320
321 if (m->s.strIPv4NetworkCidr == strCidr)
322 return S_OK;
323
324 m->recalculatePortForwarding(Net, Mask);
325
326 m->s.strIPv4NetworkCidr = strCidr;
327 i_recalculateIpv4AddressAssignments();
328 }
329
330 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
331 HRESULT hrc = m->pVirtualBox->i_saveSettings();
332 ComAssertComRCRetRC(hrc);
333 return S_OK;
334}
335
336
337/**
338 * Do best effort attempt at converting existing port forwarding rules
339 * from the old prefix to the new one. This might not be possible if
340 * the new prefix is longer (i.e. the network is smaller) or if a rule
341 * lists destination not from the network (though that rule wouldn't
342 * be terribly useful, at least currently).
343 */
344void NATNetwork::Data::recalculatePortForwarding(const RTNETADDRIPV4 &NetNew,
345 const RTNETADDRIPV4 &MaskNew)
346{
347 RTNETADDRIPV4 NetOld, MaskOld;
348 int iPrefixOld;
349 int rc;
350
351 if (s.mapPortForwardRules4.empty())
352 return; /* nothing to do */
353
354 rc = RTNetStrToIPv4Cidr(s.strIPv4NetworkCidr.c_str(), &NetOld, &iPrefixOld);
355 if (RT_FAILURE(rc))
356 return;
357
358 rc = RTNetPrefixToMaskIPv4(iPrefixOld, &MaskOld);
359 if (RT_FAILURE(rc))
360 return;
361
362 for (settings::NATRulesMap::iterator it = s.mapPortForwardRules4.begin();
363 it != s.mapPortForwardRules4.end();
364 ++it)
365 {
366 settings::NATRule &rule = it->second;
367
368 /* parse the old destination address */
369 RTNETADDRIPV4 AddrOld;
370 rc = RTNetStrToIPv4Addr(rule.strGuestIP.c_str(), &AddrOld);
371 if (RT_FAILURE(rc))
372 continue;
373
374 /* is it in the old network? (likely) */
375 if ((AddrOld.u & MaskOld.u) != NetOld.u)
376 continue;
377
378 uint32_t u32Host = (AddrOld.u & ~MaskOld.u);
379
380 /* does it fit into the new network? */
381 if ((u32Host & MaskNew.u) != 0)
382 continue;
383
384 rule.strGuestIP.printf("%RTnaipv4", NetNew.u | u32Host);
385 }
386}
387
388
389HRESULT NATNetwork::getIPv6Enabled(BOOL *aIPv6Enabled)
390{
391 *aIPv6Enabled = m->s.fIPv6Enabled;
392
393 return S_OK;
394}
395
396
397HRESULT NATNetwork::setIPv6Enabled(const BOOL aIPv6Enabled)
398{
399 {
400 AutoReadLock alockNatNetList(m->pVirtualBox->i_getNatNetLock() COMMA_LOCKVAL_SRC_POS);
401 if (m->pVirtualBox->i_isNatNetStarted(m->s.strNetworkName))
402 return setErrorBusy();
403
404 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
405
406 if (RT_BOOL(aIPv6Enabled) == m->s.fIPv6Enabled)
407 return S_OK;
408
409 m->s.fIPv6Enabled = RT_BOOL(aIPv6Enabled);
410 }
411
412 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
413 HRESULT rc = m->pVirtualBox->i_saveSettings();
414 ComAssertComRCRetRC(rc);
415
416 return S_OK;
417}
418
419
420HRESULT NATNetwork::getIPv6Prefix(com::Utf8Str &aIPv6Prefix)
421{
422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
423
424 aIPv6Prefix = m->s.strIPv6Prefix;
425 return S_OK;
426}
427
428HRESULT NATNetwork::setIPv6Prefix(const com::Utf8Str &aIPv6Prefix)
429{
430 HRESULT hrc;
431 int rc;
432
433 RTNETADDRIPV6 Net6;
434 int iPrefixLength;
435 rc = RTNetStrToIPv6Cidr(aIPv6Prefix.c_str(), &Net6, &iPrefixLength);
436 if (RT_FAILURE(rc))
437 return setError(E_INVALIDARG,
438 "%s is not a valid IPv6 prefix",
439 aIPv6Prefix.c_str());
440
441 /* Accept both addr:: and addr::/64 */
442 if (iPrefixLength == 128) /* no length was specified after the address? */
443 iPrefixLength = 64; /* take it to mean /64 which we require anyway */
444 else if (iPrefixLength != 64)
445 return setError(E_INVALIDARG,
446 "Invalid IPv6 prefix length %d, must be 64",
447 iPrefixLength);
448
449 /* Verify the address is unicast. */
450 if ( ((Net6.au8[0] & 0xe0) != 0x20) /* global 2000::/3 */
451 && ((Net6.au8[0] & 0xfe) != 0xfc)) /* local fc00::/7 */
452 return setError(E_INVALIDARG,
453 "IPv6 prefix %RTnaipv6 is not unicast",
454 &Net6);
455
456 /* Verify the interfaces ID part is zero */
457 if (Net6.au64[1] != 0)
458 return setError(E_INVALIDARG,
459 "Non-zero bits in the interface ID part"
460 " of the IPv6 prefix %RTnaipv6/64",
461 &Net6);
462
463 /* Since we store it in text form, use canonical representation */
464 com::Utf8StrFmt strNormalizedIPv6Prefix("%RTnaipv6/64", &Net6);
465
466 {
467 AutoReadLock alockNatNetList(m->pVirtualBox->i_getNatNetLock() COMMA_LOCKVAL_SRC_POS);
468 if (m->pVirtualBox->i_isNatNetStarted(m->s.strNetworkName))
469 return setErrorBusy();
470
471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
472
473 if (strNormalizedIPv6Prefix == m->s.strIPv6Prefix)
474 return S_OK;
475
476 /**
477 * @todo
478 * silently ignore network IPv6 prefix update.
479 * todo: see similar todo in NATNetwork::COMSETTER(Network)(IN_BSTR)
480 */
481 if (!m->s.mapPortForwardRules6.empty())
482 return S_OK;
483
484 m->s.strIPv6Prefix = strNormalizedIPv6Prefix;
485 }
486
487 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
488 hrc = m->pVirtualBox->i_saveSettings();
489 ComAssertComRCRetRC(hrc);
490
491 return S_OK;
492}
493
494
495HRESULT NATNetwork::getAdvertiseDefaultIPv6RouteEnabled(BOOL *aAdvertiseDefaultIPv6Route)
496{
497 *aAdvertiseDefaultIPv6Route = m->s.fAdvertiseDefaultIPv6Route;
498
499 return S_OK;
500}
501
502
503HRESULT NATNetwork::setAdvertiseDefaultIPv6RouteEnabled(const BOOL aAdvertiseDefaultIPv6Route)
504{
505 {
506 AutoReadLock alockNatNetList(m->pVirtualBox->i_getNatNetLock() COMMA_LOCKVAL_SRC_POS);
507 if (m->pVirtualBox->i_isNatNetStarted(m->s.strNetworkName))
508 return setErrorBusy();
509
510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
511
512 if (RT_BOOL(aAdvertiseDefaultIPv6Route) == m->s.fAdvertiseDefaultIPv6Route)
513 return S_OK;
514
515 m->s.fAdvertiseDefaultIPv6Route = RT_BOOL(aAdvertiseDefaultIPv6Route);
516
517 }
518
519 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
520 HRESULT rc = m->pVirtualBox->i_saveSettings();
521 ComAssertComRCRetRC(rc);
522
523 return S_OK;
524}
525
526
527HRESULT NATNetwork::getNeedDhcpServer(BOOL *aNeedDhcpServer)
528{
529 *aNeedDhcpServer = m->s.fNeedDhcpServer;
530
531 return S_OK;
532}
533
534HRESULT NATNetwork::setNeedDhcpServer(const BOOL aNeedDhcpServer)
535{
536 {
537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
538
539 if (RT_BOOL(aNeedDhcpServer) == m->s.fNeedDhcpServer)
540 return S_OK;
541
542 m->s.fNeedDhcpServer = RT_BOOL(aNeedDhcpServer);
543
544 i_recalculateIpv4AddressAssignments();
545
546 }
547
548 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
549 HRESULT rc = m->pVirtualBox->i_saveSettings();
550 ComAssertComRCRetRC(rc);
551
552 return S_OK;
553}
554
555HRESULT NATNetwork::getLocalMappings(std::vector<com::Utf8Str> &aLocalMappings)
556{
557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
558
559 aLocalMappings.resize(m->s.llHostLoopbackOffsetList.size());
560 size_t i = 0;
561 for (settings::NATLoopbackOffsetList::const_iterator it = m->s.llHostLoopbackOffsetList.begin();
562 it != m->s.llHostLoopbackOffsetList.end(); ++it, ++i)
563 {
564 aLocalMappings[i] = Utf8StrFmt("%s=%d",
565 (*it).strLoopbackHostAddress.c_str(),
566 (*it).u32Offset);
567 }
568
569 return S_OK;
570}
571
572HRESULT NATNetwork::addLocalMapping(const com::Utf8Str &aHostId, LONG aOffset)
573{
574 RTNETADDRIPV4 addr, net, mask;
575
576 int rc = RTNetStrToIPv4Addr(Utf8Str(aHostId).c_str(), &addr);
577 if (RT_FAILURE(rc))
578 return E_INVALIDARG;
579
580 /* check against 127/8 */
581 if ((RT_N2H_U32(addr.u) >> IN_CLASSA_NSHIFT) != IN_LOOPBACKNET)
582 return E_INVALIDARG;
583
584 /* check against networkid vs network mask */
585 rc = RTCidrStrToIPv4(Utf8Str(m->s.strIPv4NetworkCidr).c_str(), &net, &mask);
586 if (RT_FAILURE(rc))
587 return E_INVALIDARG;
588
589 if (((net.u + (uint32_t)aOffset) & mask.u) != net.u)
590 return E_INVALIDARG;
591
592 settings::NATLoopbackOffsetList::iterator it;
593
594 it = std::find(m->s.llHostLoopbackOffsetList.begin(),
595 m->s.llHostLoopbackOffsetList.end(),
596 aHostId);
597 if (it != m->s.llHostLoopbackOffsetList.end())
598 {
599 if (aOffset == 0) /* erase */
600 m->s.llHostLoopbackOffsetList.erase(it, it);
601 else /* modify */
602 {
603 settings::NATLoopbackOffsetList::iterator it1;
604 it1 = std::find(m->s.llHostLoopbackOffsetList.begin(),
605 m->s.llHostLoopbackOffsetList.end(),
606 (uint32_t)aOffset);
607 if (it1 != m->s.llHostLoopbackOffsetList.end())
608 return E_INVALIDARG; /* this offset is already registered. */
609
610 (*it).u32Offset = (uint32_t)aOffset;
611 }
612
613 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
614 return m->pVirtualBox->i_saveSettings();
615 }
616
617 /* injection */
618 it = std::find(m->s.llHostLoopbackOffsetList.begin(),
619 m->s.llHostLoopbackOffsetList.end(),
620 (uint32_t)aOffset);
621
622 if (it != m->s.llHostLoopbackOffsetList.end())
623 return E_INVALIDARG; /* offset is already registered. */
624
625 settings::NATHostLoopbackOffset off;
626 off.strLoopbackHostAddress = aHostId;
627 off.u32Offset = (uint32_t)aOffset;
628 m->s.llHostLoopbackOffsetList.push_back(off);
629
630 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
631 return m->pVirtualBox->i_saveSettings();
632}
633
634
635HRESULT NATNetwork::getLoopbackIp6(LONG *aLoopbackIp6)
636{
637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
638
639 *aLoopbackIp6 = (LONG)m->s.u32HostLoopback6Offset;
640 return S_OK;
641}
642
643
644HRESULT NATNetwork::setLoopbackIp6(LONG aLoopbackIp6)
645{
646 {
647 AutoReadLock alockNatNetList(m->pVirtualBox->i_getNatNetLock() COMMA_LOCKVAL_SRC_POS);
648 if (m->pVirtualBox->i_isNatNetStarted(m->s.strNetworkName))
649 return setErrorBusy();
650
651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
652
653 if (aLoopbackIp6 < 0)
654 return E_INVALIDARG;
655
656 if (static_cast<uint32_t>(aLoopbackIp6) == m->s.u32HostLoopback6Offset)
657 return S_OK;
658
659 m->s.u32HostLoopback6Offset = (uint32_t)aLoopbackIp6;
660 }
661
662 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
663 return m->pVirtualBox->i_saveSettings();
664}
665
666
667HRESULT NATNetwork::getPortForwardRules4(std::vector<com::Utf8Str> &aPortForwardRules4)
668{
669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
670 i_getPortForwardRulesFromMap(aPortForwardRules4,
671 m->s.mapPortForwardRules4);
672 return S_OK;
673}
674
675HRESULT NATNetwork::getPortForwardRules6(std::vector<com::Utf8Str> &aPortForwardRules6)
676{
677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
678 i_getPortForwardRulesFromMap(aPortForwardRules6,
679 m->s.mapPortForwardRules6);
680 return S_OK;
681}
682
683HRESULT NATNetwork::addPortForwardRule(BOOL aIsIpv6,
684 const com::Utf8Str &aPortForwardRuleName,
685 NATProtocol_T aProto,
686 const com::Utf8Str &aHostIp,
687 USHORT aHostPort,
688 const com::Utf8Str &aGuestIp,
689 USHORT aGuestPort)
690{
691 {
692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
693 Utf8Str name = aPortForwardRuleName;
694 Utf8Str proto;
695 settings::NATRule r;
696 settings::NATRulesMap &mapRules = aIsIpv6 ? m->s.mapPortForwardRules6 : m->s.mapPortForwardRules4;
697 switch (aProto)
698 {
699 case NATProtocol_TCP:
700 proto = "tcp";
701 break;
702 case NATProtocol_UDP:
703 proto = "udp";
704 break;
705 default:
706 return E_INVALIDARG;
707 }
708 if (name.isEmpty())
709 name = Utf8StrFmt("%s_[%s]%%%d_[%s]%%%d", proto.c_str(),
710 aHostIp.c_str(), aHostPort,
711 aGuestIp.c_str(), aGuestPort);
712
713 for (settings::NATRulesMap::iterator it = mapRules.begin(); it != mapRules.end(); ++it)
714 {
715 r = it->second;
716 if (it->first == name)
717 return setError(E_INVALIDARG,
718 tr("A NAT rule of this name already exists"));
719 if ( r.strHostIP == aHostIp
720 && r.u16HostPort == aHostPort
721 && r.proto == aProto)
722 return setError(E_INVALIDARG,
723 tr("A NAT rule for this host port and this host IP already exists"));
724 }
725
726 r.strName = name.c_str();
727 r.proto = aProto;
728 r.strHostIP = aHostIp;
729 r.u16HostPort = aHostPort;
730 r.strGuestIP = aGuestIp;
731 r.u16GuestPort = aGuestPort;
732 mapRules.insert(std::make_pair(name, r));
733 }
734 {
735 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
736 HRESULT rc = m->pVirtualBox->i_saveSettings();
737 ComAssertComRCRetRC(rc);
738 }
739
740 m->pVirtualBox->i_onNATNetworkPortForward(m->s.strNetworkName, TRUE, aIsIpv6,
741 aPortForwardRuleName, aProto,
742 aHostIp, aHostPort,
743 aGuestIp, aGuestPort);
744
745 /* Notify listerners listening on this network only */
746 ::FireNATNetworkPortForwardEvent(m->pEventSource, m->s.strNetworkName, TRUE,
747 aIsIpv6, aPortForwardRuleName, aProto,
748 aHostIp, aHostPort,
749 aGuestIp, aGuestPort);
750
751 return S_OK;
752}
753
754HRESULT NATNetwork::removePortForwardRule(BOOL aIsIpv6, const com::Utf8Str &aPortForwardRuleName)
755{
756 Utf8Str strHostIP;
757 Utf8Str strGuestIP;
758 uint16_t u16HostPort;
759 uint16_t u16GuestPort;
760 NATProtocol_T proto;
761
762 {
763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
764 settings::NATRulesMap &mapRules = aIsIpv6 ? m->s.mapPortForwardRules6 : m->s.mapPortForwardRules4;
765 settings::NATRulesMap::iterator it = mapRules.find(aPortForwardRuleName);
766
767 if (it == mapRules.end())
768 return E_INVALIDARG;
769
770 strHostIP = it->second.strHostIP;
771 strGuestIP = it->second.strGuestIP;
772 u16HostPort = it->second.u16HostPort;
773 u16GuestPort = it->second.u16GuestPort;
774 proto = it->second.proto;
775
776 mapRules.erase(it);
777 }
778
779 {
780 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
781 HRESULT rc = m->pVirtualBox->i_saveSettings();
782 ComAssertComRCRetRC(rc);
783 }
784
785 m->pVirtualBox->i_onNATNetworkPortForward(m->s.strNetworkName, FALSE, aIsIpv6, aPortForwardRuleName, proto,
786 strHostIP, u16HostPort, strGuestIP, u16GuestPort);
787
788 /* Notify listerners listening on this network only */
789 ::FireNATNetworkPortForwardEvent(m->pEventSource, m->s.strNetworkName, FALSE, aIsIpv6, aPortForwardRuleName, proto,
790 strHostIP, u16HostPort, strGuestIP, u16GuestPort);
791 return S_OK;
792}
793
794
795void NATNetwork::i_updateDomainNameOption(ComPtr<IHost> &host)
796{
797 com::Bstr domain;
798 if (FAILED(host->COMGETTER(DomainName)(domain.asOutParam())))
799 LogRel(("NATNetwork: Failed to get host's domain name\n"));
800 ComPtr<IDHCPGlobalConfig> pDHCPConfig;
801 HRESULT hrc = m->dhcpServer->COMGETTER(GlobalConfig)(pDHCPConfig.asOutParam());
802 if (FAILED(hrc))
803 {
804 LogRel(("NATNetwork: Failed to get global DHCP config when updating domain name option with %Rhrc\n", hrc));
805 return;
806 }
807 if (domain.isNotEmpty())
808 {
809 hrc = pDHCPConfig->SetOption(DHCPOption_DomainName, DHCPOptionEncoding_Normal, domain.raw());
810 if (FAILED(hrc))
811 LogRel(("NATNetwork: Failed to add domain name option with %Rhrc\n", hrc));
812 }
813 else
814 pDHCPConfig->RemoveOption(DHCPOption_DomainName);
815}
816
817void NATNetwork::i_updateDomainNameServerOption(ComPtr<IHost> &host)
818{
819 RTNETADDRIPV4 networkid, netmask;
820
821 int rc = RTCidrStrToIPv4(m->s.strIPv4NetworkCidr.c_str(), &networkid, &netmask);
822 if (RT_FAILURE(rc))
823 {
824 LogRel(("NATNetwork: Failed to parse cidr %s with %Rrc\n", m->s.strIPv4NetworkCidr.c_str(), rc));
825 return;
826 }
827
828 /* XXX: these are returned, surprisingly, in host order */
829 networkid.u = RT_H2N_U32(networkid.u);
830 netmask.u = RT_H2N_U32(netmask.u);
831
832 com::SafeArray<BSTR> nameServers;
833 HRESULT hrc = host->COMGETTER(NameServers)(ComSafeArrayAsOutParam(nameServers));
834 if (FAILED(hrc))
835 {
836 LogRel(("NATNetwork: Failed to get name servers from host with %Rhrc\n", hrc));
837 return;
838 }
839 ComPtr<IDHCPGlobalConfig> pDHCPConfig;
840 hrc = m->dhcpServer->COMGETTER(GlobalConfig)(pDHCPConfig.asOutParam());
841 if (FAILED(hrc))
842 {
843 LogRel(("NATNetwork: Failed to get global DHCP config when updating domain name server option with %Rhrc\n", hrc));
844 return;
845 }
846
847 size_t cAddresses = nameServers.size();
848 if (cAddresses)
849 {
850 RTCList<RTCString> lstServers;
851 /* The following code was copied (and adapted a bit) from VBoxNetDhcp::hostDnsServers */
852 /*
853 * Recent fashion is to run dnsmasq on 127.0.1.1 which we
854 * currently can't map. If that's the only nameserver we've got,
855 * we need to use DNS proxy for VMs to reach it.
856 */
857 bool fUnmappedLoopback = false;
858
859 for (size_t i = 0; i < cAddresses; ++i)
860 {
861 RTNETADDRIPV4 addr;
862
863 com::Utf8Str strNameServerAddress(nameServers[i]);
864 rc = RTNetStrToIPv4Addr(strNameServerAddress.c_str(), &addr);
865 if (RT_FAILURE(rc))
866 {
867 LogRel(("NATNetwork: Failed to parse IP address %s with %Rrc\n", strNameServerAddress.c_str(), rc));
868 continue;
869 }
870
871 if (addr.u == INADDR_ANY)
872 {
873 /*
874 * This doesn't seem to be very well documented except for
875 * RTFS of res_init.c, but INADDR_ANY is a valid value for
876 * for "nameserver".
877 */
878 addr.u = RT_H2N_U32_C(INADDR_LOOPBACK);
879 }
880
881 if (addr.au8[0] == 127)
882 {
883 settings::NATLoopbackOffsetList::const_iterator it;
884
885 it = std::find(m->s.llHostLoopbackOffsetList.begin(),
886 m->s.llHostLoopbackOffsetList.end(),
887 strNameServerAddress);
888 if (it == m->s.llHostLoopbackOffsetList.end())
889 {
890 fUnmappedLoopback = true;
891 continue;
892 }
893 addr.u = RT_H2N_U32(RT_N2H_U32(networkid.u) + it->u32Offset);
894 }
895 lstServers.append(RTCStringFmt("%RTnaipv4", addr));
896 }
897
898 if (lstServers.isEmpty() && fUnmappedLoopback)
899 lstServers.append(RTCStringFmt("%RTnaipv4", networkid.u | RT_H2N_U32_C(1U))); /* proxy */
900
901 hrc = pDHCPConfig->SetOption(DHCPOption_DomainNameServers, DHCPOptionEncoding_Normal, Bstr(RTCString::join(lstServers, " ")).raw());
902 if (FAILED(hrc))
903 LogRel(("NATNetwork: Failed to add domain name server option '%s' with %Rhrc\n", RTCString::join(lstServers, " ").c_str(), hrc));
904 }
905 else
906 pDHCPConfig->RemoveOption(DHCPOption_DomainNameServers);
907}
908
909void NATNetwork::i_updateDnsOptions()
910{
911 ComPtr<IHost> host;
912 if (SUCCEEDED(m->pVirtualBox->COMGETTER(Host)(host.asOutParam())))
913 {
914 i_updateDomainNameOption(host);
915 i_updateDomainNameServerOption(host);
916 }
917}
918
919
920HRESULT NATNetwork::start()
921{
922#ifdef VBOX_WITH_NAT_SERVICE
923 if (!m->s.fEnabled) return S_OK;
924 AssertReturn(!m->s.strNetworkName.isEmpty(), E_FAIL);
925
926 m->NATRunner.resetArguments();
927 m->NATRunner.addArgPair(NetworkServiceRunner::kpszKeyNetwork, Utf8Str(m->s.strNetworkName).c_str());
928
929 /* No portforwarding rules from command-line, all will be fetched via API */
930
931 if (m->s.fNeedDhcpServer)
932 {
933 /*
934 * Just to as idea... via API (on creation user pass the cidr of network and)
935 * and we calculate it's addreses (mutable?).
936 */
937
938 /*
939 * Configuration and running DHCP server:
940 * 1. find server first createDHCPServer
941 * 2. if return status is E_INVALARG => server already exists just find and start.
942 * 3. if return status neither E_INVALRG nor S_OK => return E_FAIL
943 * 4. if return status S_OK proceed to DHCP server configuration
944 * 5. call setConfiguration() and pass all required parameters
945 * 6. start dhcp server.
946 */
947 HRESULT hrc = m->pVirtualBox->FindDHCPServerByNetworkName(Bstr(m->s.strNetworkName).raw(),
948 m->dhcpServer.asOutParam());
949 switch (hrc)
950 {
951 case E_INVALIDARG:
952 /* server haven't beeen found let create it then */
953 hrc = m->pVirtualBox->CreateDHCPServer(Bstr(m->s.strNetworkName).raw(),
954 m->dhcpServer.asOutParam());
955 if (FAILED(hrc))
956 return E_FAIL;
957 /* breakthrough */
958
959 {
960 LogFunc(("gateway: %s, dhcpserver:%s, dhcplowerip:%s, dhcpupperip:%s\n",
961 m->IPv4Gateway.c_str(),
962 m->IPv4DhcpServer.c_str(),
963 m->IPv4DhcpServerLowerIp.c_str(),
964 m->IPv4DhcpServerUpperIp.c_str()));
965
966 hrc = m->dhcpServer->COMSETTER(Enabled)(true);
967
968 hrc = m->dhcpServer->SetConfiguration(Bstr(m->IPv4DhcpServer).raw(),
969 Bstr(m->IPv4NetworkMask).raw(),
970 Bstr(m->IPv4DhcpServerLowerIp).raw(),
971 Bstr(m->IPv4DhcpServerUpperIp).raw());
972 }
973 case S_OK:
974 break;
975
976 default:
977 return E_FAIL;
978 }
979
980#ifdef VBOX_WITH_DHCPD
981 i_updateDnsOptions();
982#endif /* VBOX_WITH_DHCPD */
983 /* XXX: AddGlobalOption(DhcpOpt_Router,) - enables attachement of DhcpServer to Main (no longer true with VBoxNetDhcpd). */
984 ComPtr<IDHCPGlobalConfig> pDHCPConfig;
985 hrc = m->dhcpServer->COMGETTER(GlobalConfig)(pDHCPConfig.asOutParam());
986 if (FAILED(hrc))
987 {
988 LogRel(("NATNetwork: Failed to get global DHCP config when updating IPv4 gateway option with %Rhrc\n", hrc));
989 m->dhcpServer.setNull();
990 return E_FAIL;
991 }
992 pDHCPConfig->SetOption(DHCPOption_Routers, DHCPOptionEncoding_Normal, Bstr(m->IPv4Gateway).raw());
993
994 hrc = m->dhcpServer->Start(Bstr::Empty.raw(), Bstr(TRUNKTYPE_WHATEVER).raw());
995 if (FAILED(hrc))
996 {
997 m->dhcpServer.setNull();
998 return E_FAIL;
999 }
1000 }
1001
1002 if (RT_SUCCESS(m->NATRunner.start(false /* KillProcOnStop */)))
1003 {
1004 m->pVirtualBox->i_onNATNetworkStartStop(m->s.strNetworkName, TRUE);
1005 return S_OK;
1006 }
1007 /** @todo missing setError()! */
1008 return E_FAIL;
1009#else
1010 ReturnComNotImplemented();
1011#endif
1012}
1013
1014HRESULT NATNetwork::stop()
1015{
1016#ifdef VBOX_WITH_NAT_SERVICE
1017 m->pVirtualBox->i_onNATNetworkStartStop(m->s.strNetworkName, FALSE);
1018
1019 if (!m->dhcpServer.isNull())
1020 m->dhcpServer->Stop();
1021
1022 if (RT_SUCCESS(m->NATRunner.stop()))
1023 return S_OK;
1024
1025 /** @todo missing setError()! */
1026 return E_FAIL;
1027#else
1028 ReturnComNotImplemented();
1029#endif
1030}
1031
1032
1033void NATNetwork::i_getPortForwardRulesFromMap(std::vector<com::Utf8Str> &aPortForwardRules, settings::NATRulesMap &aRules)
1034{
1035 aPortForwardRules.resize(aRules.size());
1036 size_t i = 0;
1037 for (settings::NATRulesMap::const_iterator it = aRules.begin();
1038 it != aRules.end(); ++it, ++i)
1039 {
1040 settings::NATRule r = it->second;
1041 aPortForwardRules[i] = Utf8StrFmt("%s:%s:[%s]:%d:[%s]:%d",
1042 r.strName.c_str(),
1043 (r.proto == NATProtocol_TCP ? "tcp" : "udp"),
1044 r.strHostIP.c_str(),
1045 r.u16HostPort,
1046 r.strGuestIP.c_str(),
1047 r.u16GuestPort);
1048 }
1049}
1050
1051
1052int NATNetwork::i_findFirstAvailableOffset(ADDRESSLOOKUPTYPE addrType, uint32_t *poff)
1053{
1054 RTNETADDRIPV4 network, netmask;
1055
1056 int rc = RTCidrStrToIPv4(m->s.strIPv4NetworkCidr.c_str(),
1057 &network,
1058 &netmask);
1059 AssertRCReturn(rc, rc);
1060
1061 uint32_t off;
1062 for (off = 1; off < ~netmask.u; ++off)
1063 {
1064 bool skip = false;
1065 for (settings::NATLoopbackOffsetList::iterator it = m->s.llHostLoopbackOffsetList.begin();
1066 it != m->s.llHostLoopbackOffsetList.end();
1067 ++it)
1068 {
1069 if ((*it).u32Offset == off)
1070 {
1071 skip = true;
1072 break;
1073 }
1074
1075 }
1076
1077 if (skip)
1078 continue;
1079
1080 if (off == m->offGateway)
1081 {
1082 if (addrType == ADDR_GATEWAY)
1083 break;
1084 else
1085 continue;
1086 }
1087
1088 if (off == m->offDhcp)
1089 {
1090 if (addrType == ADDR_DHCP)
1091 break;
1092 else
1093 continue;
1094 }
1095
1096 if (!skip)
1097 break;
1098 }
1099
1100 if (poff)
1101 *poff = off;
1102
1103 return VINF_SUCCESS;
1104}
1105
1106int NATNetwork::i_recalculateIpv4AddressAssignments()
1107{
1108 RTNETADDRIPV4 network, netmask;
1109 int rc = RTCidrStrToIPv4(m->s.strIPv4NetworkCidr.c_str(),
1110 &network,
1111 &netmask);
1112 AssertRCReturn(rc, rc);
1113
1114 i_findFirstAvailableOffset(ADDR_GATEWAY, &m->offGateway);
1115 if (m->s.fNeedDhcpServer)
1116 i_findFirstAvailableOffset(ADDR_DHCP, &m->offDhcp);
1117
1118 /* I don't remember the reason CIDR calculated on the host. */
1119 RTNETADDRIPV4 gateway = network;
1120 gateway.u += m->offGateway;
1121 gateway.u = RT_H2N_U32(gateway.u);
1122 char szTmpIp[16];
1123 RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", gateway);
1124 m->IPv4Gateway = szTmpIp;
1125
1126 if (m->s.fNeedDhcpServer)
1127 {
1128 RTNETADDRIPV4 dhcpserver = network;
1129 dhcpserver.u += m->offDhcp;
1130
1131 /* XXX: adding more services should change the math here */
1132 RTNETADDRIPV4 dhcplowerip = network;
1133 uint32_t offDhcpLowerIp;
1134 i_findFirstAvailableOffset(ADDR_DHCPLOWERIP, &offDhcpLowerIp);
1135 dhcplowerip.u = RT_H2N_U32(dhcplowerip.u + offDhcpLowerIp);
1136
1137 RTNETADDRIPV4 dhcpupperip;
1138 dhcpupperip.u = RT_H2N_U32((network.u | ~netmask.u) - 1);
1139
1140 dhcpserver.u = RT_H2N_U32(dhcpserver.u);
1141 network.u = RT_H2N_U32(network.u);
1142
1143 RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcpserver);
1144 m->IPv4DhcpServer = szTmpIp;
1145 RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcplowerip);
1146 m->IPv4DhcpServerLowerIp = szTmpIp;
1147 RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcpupperip);
1148 m->IPv4DhcpServerUpperIp = szTmpIp;
1149
1150 LogFunc(("network:%RTnaipv4, dhcpserver:%RTnaipv4, dhcplowerip:%RTnaipv4, dhcpupperip:%RTnaipv4\n",
1151 network, dhcpserver, dhcplowerip, dhcpupperip));
1152 }
1153
1154 /* we need IPv4NetworkMask for NAT's gw service start */
1155 netmask.u = RT_H2N_U32(netmask.u);
1156 RTStrPrintf(szTmpIp, 16, "%RTnaipv4", netmask);
1157 m->IPv4NetworkMask = szTmpIp;
1158
1159 LogFlowFunc(("getaway:%RTnaipv4, netmask:%RTnaipv4\n", gateway, netmask));
1160 return VINF_SUCCESS;
1161}
1162
1163
1164int NATNetwork::i_recalculateIPv6Prefix()
1165{
1166 int rc;
1167
1168 RTNETADDRIPV4 net, mask;
1169 rc = RTCidrStrToIPv4(Utf8Str(m->s.strIPv4NetworkCidr).c_str(), &net, &mask);
1170 if (RT_FAILURE(rc))
1171 return rc;
1172
1173 net.u = RT_H2N_U32(net.u); /* XXX: fix RTCidrStrToIPv4! */
1174
1175 /*
1176 * [fd17:625c:f037:XXXX::/64] - RFC 4193 (ULA) Locally Assigned
1177 * Global ID where XXXX, 16 bit Subnet ID, are two bytes from the
1178 * middle of the IPv4 address, e.g. :dead: for 10.222.173.1
1179 */
1180 RTNETADDRIPV6 prefix;
1181 RT_ZERO(prefix);
1182
1183 prefix.au8[0] = 0xFD;
1184 prefix.au8[1] = 0x17;
1185
1186 prefix.au8[2] = 0x62;
1187 prefix.au8[3] = 0x5C;
1188
1189 prefix.au8[4] = 0xF0;
1190 prefix.au8[5] = 0x37;
1191
1192 prefix.au8[6] = net.au8[1];
1193 prefix.au8[7] = net.au8[2];
1194
1195 char szBuf[32];
1196 RTStrPrintf(szBuf, sizeof(szBuf), "%RTnaipv6/64", &prefix);
1197
1198 m->s.strIPv6Prefix = szBuf;
1199 return VINF_SUCCESS;
1200}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette