VirtualBox

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

Last change on this file since 90828 was 88747, checked in by vboxsync, 4 years ago

NAT/Net: setIPv6Prefix - allow empty prefix if IPv6 is disabled. This
happens e.g. when we create a new natnet. Need to ensure consistent
state transitions elsewhere (enabling IPv6 - a separate knob - when
prefix is not set, etc).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.4 KB
Line 
1/* $Id: NATNetworkImpl.cpp 88747 2021-04-28 14:25:53Z 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 /* Since we store it in text form, use canonical representation */
434 com::Utf8Str strNormalizedIPv6Prefix;
435
436 const char *pcsz = RTStrStripL(aIPv6Prefix.c_str());
437 if (*pcsz != '\0') /* verify it first if not empty/blank */
438 {
439 RTNETADDRIPV6 Net6;
440 int iPrefixLength;
441 rc = RTNetStrToIPv6Cidr(aIPv6Prefix.c_str(), &Net6, &iPrefixLength);
442 if (RT_FAILURE(rc))
443 return setError(E_INVALIDARG,
444 "%s is not a valid IPv6 prefix",
445 aIPv6Prefix.c_str());
446
447 /* Accept both addr:: and addr::/64 */
448 if (iPrefixLength == 128) /* no length was specified after the address? */
449 iPrefixLength = 64; /* take it to mean /64 which we require anyway */
450 else if (iPrefixLength != 64)
451 return setError(E_INVALIDARG,
452 "Invalid IPv6 prefix length %d, must be 64",
453 iPrefixLength);
454
455 /* Verify the address is unicast. */
456 if ( ((Net6.au8[0] & 0xe0) != 0x20) /* global 2000::/3 */
457 && ((Net6.au8[0] & 0xfe) != 0xfc)) /* local fc00::/7 */
458 return setError(E_INVALIDARG,
459 "IPv6 prefix %RTnaipv6 is not unicast",
460 &Net6);
461
462 /* Verify the interfaces ID part is zero */
463 if (Net6.au64[1] != 0)
464 return setError(E_INVALIDARG,
465 "Non-zero bits in the interface ID part"
466 " of the IPv6 prefix %RTnaipv6/64",
467 &Net6);
468
469 rc = strNormalizedIPv6Prefix.printfNoThrow("%RTnaipv6/64", &Net6);
470 if (RT_FAILURE(rc))
471 {
472 if (rc == VERR_NO_MEMORY)
473 return setError(E_OUTOFMEMORY);
474 else
475 return setError(E_FAIL, "Internal error");
476 }
477 }
478
479 {
480 AutoReadLock alockNatNetList(m->pVirtualBox->i_getNatNetLock() COMMA_LOCKVAL_SRC_POS);
481 if (m->pVirtualBox->i_isNatNetStarted(m->s.strNetworkName))
482 return setErrorBusy();
483
484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
485
486 if (strNormalizedIPv6Prefix == m->s.strIPv6Prefix)
487 return S_OK;
488
489 /* only allow prefix to be empty if IPv6 is disabled */
490 if (strNormalizedIPv6Prefix.isEmpty() && m->s.fIPv6Enabled)
491 return setError(E_FAIL, "Setting an empty IPv6 prefix when IPv6 is enabled");
492
493 /**
494 * @todo
495 * silently ignore network IPv6 prefix update.
496 * todo: see similar todo in NATNetwork::COMSETTER(Network)(IN_BSTR)
497 */
498 if (!m->s.mapPortForwardRules6.empty())
499 return S_OK;
500
501 m->s.strIPv6Prefix = strNormalizedIPv6Prefix;
502 }
503
504 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
505 hrc = m->pVirtualBox->i_saveSettings();
506 ComAssertComRCRetRC(hrc);
507
508 return S_OK;
509}
510
511
512HRESULT NATNetwork::getAdvertiseDefaultIPv6RouteEnabled(BOOL *aAdvertiseDefaultIPv6Route)
513{
514 *aAdvertiseDefaultIPv6Route = m->s.fAdvertiseDefaultIPv6Route;
515
516 return S_OK;
517}
518
519
520HRESULT NATNetwork::setAdvertiseDefaultIPv6RouteEnabled(const BOOL aAdvertiseDefaultIPv6Route)
521{
522 {
523 AutoReadLock alockNatNetList(m->pVirtualBox->i_getNatNetLock() COMMA_LOCKVAL_SRC_POS);
524 if (m->pVirtualBox->i_isNatNetStarted(m->s.strNetworkName))
525 return setErrorBusy();
526
527 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
528
529 if (RT_BOOL(aAdvertiseDefaultIPv6Route) == m->s.fAdvertiseDefaultIPv6Route)
530 return S_OK;
531
532 m->s.fAdvertiseDefaultIPv6Route = RT_BOOL(aAdvertiseDefaultIPv6Route);
533
534 }
535
536 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
537 HRESULT rc = m->pVirtualBox->i_saveSettings();
538 ComAssertComRCRetRC(rc);
539
540 return S_OK;
541}
542
543
544HRESULT NATNetwork::getNeedDhcpServer(BOOL *aNeedDhcpServer)
545{
546 *aNeedDhcpServer = m->s.fNeedDhcpServer;
547
548 return S_OK;
549}
550
551HRESULT NATNetwork::setNeedDhcpServer(const BOOL aNeedDhcpServer)
552{
553 {
554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
555
556 if (RT_BOOL(aNeedDhcpServer) == m->s.fNeedDhcpServer)
557 return S_OK;
558
559 m->s.fNeedDhcpServer = RT_BOOL(aNeedDhcpServer);
560
561 i_recalculateIpv4AddressAssignments();
562
563 }
564
565 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
566 HRESULT rc = m->pVirtualBox->i_saveSettings();
567 ComAssertComRCRetRC(rc);
568
569 return S_OK;
570}
571
572HRESULT NATNetwork::getLocalMappings(std::vector<com::Utf8Str> &aLocalMappings)
573{
574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
575
576 aLocalMappings.resize(m->s.llHostLoopbackOffsetList.size());
577 size_t i = 0;
578 for (settings::NATLoopbackOffsetList::const_iterator it = m->s.llHostLoopbackOffsetList.begin();
579 it != m->s.llHostLoopbackOffsetList.end(); ++it, ++i)
580 {
581 aLocalMappings[i] = Utf8StrFmt("%s=%d",
582 (*it).strLoopbackHostAddress.c_str(),
583 (*it).u32Offset);
584 }
585
586 return S_OK;
587}
588
589HRESULT NATNetwork::addLocalMapping(const com::Utf8Str &aHostId, LONG aOffset)
590{
591 RTNETADDRIPV4 addr, net, mask;
592
593 int rc = RTNetStrToIPv4Addr(Utf8Str(aHostId).c_str(), &addr);
594 if (RT_FAILURE(rc))
595 return E_INVALIDARG;
596
597 /* check against 127/8 */
598 if ((RT_N2H_U32(addr.u) >> IN_CLASSA_NSHIFT) != IN_LOOPBACKNET)
599 return E_INVALIDARG;
600
601 /* check against networkid vs network mask */
602 rc = RTCidrStrToIPv4(Utf8Str(m->s.strIPv4NetworkCidr).c_str(), &net, &mask);
603 if (RT_FAILURE(rc))
604 return E_INVALIDARG;
605
606 if (((net.u + (uint32_t)aOffset) & mask.u) != net.u)
607 return E_INVALIDARG;
608
609 settings::NATLoopbackOffsetList::iterator it;
610
611 it = std::find(m->s.llHostLoopbackOffsetList.begin(),
612 m->s.llHostLoopbackOffsetList.end(),
613 aHostId);
614 if (it != m->s.llHostLoopbackOffsetList.end())
615 {
616 if (aOffset == 0) /* erase */
617 m->s.llHostLoopbackOffsetList.erase(it, it);
618 else /* modify */
619 {
620 settings::NATLoopbackOffsetList::iterator it1;
621 it1 = std::find(m->s.llHostLoopbackOffsetList.begin(),
622 m->s.llHostLoopbackOffsetList.end(),
623 (uint32_t)aOffset);
624 if (it1 != m->s.llHostLoopbackOffsetList.end())
625 return E_INVALIDARG; /* this offset is already registered. */
626
627 (*it).u32Offset = (uint32_t)aOffset;
628 }
629
630 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
631 return m->pVirtualBox->i_saveSettings();
632 }
633
634 /* injection */
635 it = std::find(m->s.llHostLoopbackOffsetList.begin(),
636 m->s.llHostLoopbackOffsetList.end(),
637 (uint32_t)aOffset);
638
639 if (it != m->s.llHostLoopbackOffsetList.end())
640 return E_INVALIDARG; /* offset is already registered. */
641
642 settings::NATHostLoopbackOffset off;
643 off.strLoopbackHostAddress = aHostId;
644 off.u32Offset = (uint32_t)aOffset;
645 m->s.llHostLoopbackOffsetList.push_back(off);
646
647 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
648 return m->pVirtualBox->i_saveSettings();
649}
650
651
652HRESULT NATNetwork::getLoopbackIp6(LONG *aLoopbackIp6)
653{
654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
655
656 *aLoopbackIp6 = (LONG)m->s.u32HostLoopback6Offset;
657 return S_OK;
658}
659
660
661HRESULT NATNetwork::setLoopbackIp6(LONG aLoopbackIp6)
662{
663 {
664 AutoReadLock alockNatNetList(m->pVirtualBox->i_getNatNetLock() COMMA_LOCKVAL_SRC_POS);
665 if (m->pVirtualBox->i_isNatNetStarted(m->s.strNetworkName))
666 return setErrorBusy();
667
668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
669
670 if (aLoopbackIp6 < 0)
671 return E_INVALIDARG;
672
673 if (static_cast<uint32_t>(aLoopbackIp6) == m->s.u32HostLoopback6Offset)
674 return S_OK;
675
676 m->s.u32HostLoopback6Offset = (uint32_t)aLoopbackIp6;
677 }
678
679 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
680 return m->pVirtualBox->i_saveSettings();
681}
682
683
684HRESULT NATNetwork::getPortForwardRules4(std::vector<com::Utf8Str> &aPortForwardRules4)
685{
686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
687 i_getPortForwardRulesFromMap(aPortForwardRules4,
688 m->s.mapPortForwardRules4);
689 return S_OK;
690}
691
692HRESULT NATNetwork::getPortForwardRules6(std::vector<com::Utf8Str> &aPortForwardRules6)
693{
694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
695 i_getPortForwardRulesFromMap(aPortForwardRules6,
696 m->s.mapPortForwardRules6);
697 return S_OK;
698}
699
700HRESULT NATNetwork::addPortForwardRule(BOOL aIsIpv6,
701 const com::Utf8Str &aPortForwardRuleName,
702 NATProtocol_T aProto,
703 const com::Utf8Str &aHostIp,
704 USHORT aHostPort,
705 const com::Utf8Str &aGuestIp,
706 USHORT aGuestPort)
707{
708 {
709 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
710 Utf8Str name = aPortForwardRuleName;
711 Utf8Str proto;
712 settings::NATRule r;
713 settings::NATRulesMap &mapRules = aIsIpv6 ? m->s.mapPortForwardRules6 : m->s.mapPortForwardRules4;
714 switch (aProto)
715 {
716 case NATProtocol_TCP:
717 proto = "tcp";
718 break;
719 case NATProtocol_UDP:
720 proto = "udp";
721 break;
722 default:
723 return E_INVALIDARG;
724 }
725 if (name.isEmpty())
726 name = Utf8StrFmt("%s_[%s]%%%d_[%s]%%%d", proto.c_str(),
727 aHostIp.c_str(), aHostPort,
728 aGuestIp.c_str(), aGuestPort);
729
730 for (settings::NATRulesMap::iterator it = mapRules.begin(); it != mapRules.end(); ++it)
731 {
732 r = it->second;
733 if (it->first == name)
734 return setError(E_INVALIDARG,
735 tr("A NAT rule of this name already exists"));
736 if ( r.strHostIP == aHostIp
737 && r.u16HostPort == aHostPort
738 && r.proto == aProto)
739 return setError(E_INVALIDARG,
740 tr("A NAT rule for this host port and this host IP already exists"));
741 }
742
743 r.strName = name.c_str();
744 r.proto = aProto;
745 r.strHostIP = aHostIp;
746 r.u16HostPort = aHostPort;
747 r.strGuestIP = aGuestIp;
748 r.u16GuestPort = aGuestPort;
749 mapRules.insert(std::make_pair(name, r));
750 }
751 {
752 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
753 HRESULT rc = m->pVirtualBox->i_saveSettings();
754 ComAssertComRCRetRC(rc);
755 }
756
757 m->pVirtualBox->i_onNATNetworkPortForward(m->s.strNetworkName, TRUE, aIsIpv6,
758 aPortForwardRuleName, aProto,
759 aHostIp, aHostPort,
760 aGuestIp, aGuestPort);
761
762 /* Notify listerners listening on this network only */
763 ::FireNATNetworkPortForwardEvent(m->pEventSource, m->s.strNetworkName, TRUE,
764 aIsIpv6, aPortForwardRuleName, aProto,
765 aHostIp, aHostPort,
766 aGuestIp, aGuestPort);
767
768 return S_OK;
769}
770
771HRESULT NATNetwork::removePortForwardRule(BOOL aIsIpv6, const com::Utf8Str &aPortForwardRuleName)
772{
773 Utf8Str strHostIP;
774 Utf8Str strGuestIP;
775 uint16_t u16HostPort;
776 uint16_t u16GuestPort;
777 NATProtocol_T proto;
778
779 {
780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
781 settings::NATRulesMap &mapRules = aIsIpv6 ? m->s.mapPortForwardRules6 : m->s.mapPortForwardRules4;
782 settings::NATRulesMap::iterator it = mapRules.find(aPortForwardRuleName);
783
784 if (it == mapRules.end())
785 return E_INVALIDARG;
786
787 strHostIP = it->second.strHostIP;
788 strGuestIP = it->second.strGuestIP;
789 u16HostPort = it->second.u16HostPort;
790 u16GuestPort = it->second.u16GuestPort;
791 proto = it->second.proto;
792
793 mapRules.erase(it);
794 }
795
796 {
797 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
798 HRESULT rc = m->pVirtualBox->i_saveSettings();
799 ComAssertComRCRetRC(rc);
800 }
801
802 m->pVirtualBox->i_onNATNetworkPortForward(m->s.strNetworkName, FALSE, aIsIpv6, aPortForwardRuleName, proto,
803 strHostIP, u16HostPort, strGuestIP, u16GuestPort);
804
805 /* Notify listerners listening on this network only */
806 ::FireNATNetworkPortForwardEvent(m->pEventSource, m->s.strNetworkName, FALSE, aIsIpv6, aPortForwardRuleName, proto,
807 strHostIP, u16HostPort, strGuestIP, u16GuestPort);
808 return S_OK;
809}
810
811
812void NATNetwork::i_updateDomainNameOption(ComPtr<IHost> &host)
813{
814 com::Bstr domain;
815 if (FAILED(host->COMGETTER(DomainName)(domain.asOutParam())))
816 LogRel(("NATNetwork: Failed to get host's domain name\n"));
817 ComPtr<IDHCPGlobalConfig> pDHCPConfig;
818 HRESULT hrc = m->dhcpServer->COMGETTER(GlobalConfig)(pDHCPConfig.asOutParam());
819 if (FAILED(hrc))
820 {
821 LogRel(("NATNetwork: Failed to get global DHCP config when updating domain name option with %Rhrc\n", hrc));
822 return;
823 }
824 if (domain.isNotEmpty())
825 {
826 hrc = pDHCPConfig->SetOption(DHCPOption_DomainName, DHCPOptionEncoding_Normal, domain.raw());
827 if (FAILED(hrc))
828 LogRel(("NATNetwork: Failed to add domain name option with %Rhrc\n", hrc));
829 }
830 else
831 pDHCPConfig->RemoveOption(DHCPOption_DomainName);
832}
833
834void NATNetwork::i_updateDomainNameServerOption(ComPtr<IHost> &host)
835{
836 RTNETADDRIPV4 networkid, netmask;
837
838 int rc = RTCidrStrToIPv4(m->s.strIPv4NetworkCidr.c_str(), &networkid, &netmask);
839 if (RT_FAILURE(rc))
840 {
841 LogRel(("NATNetwork: Failed to parse cidr %s with %Rrc\n", m->s.strIPv4NetworkCidr.c_str(), rc));
842 return;
843 }
844
845 /* XXX: these are returned, surprisingly, in host order */
846 networkid.u = RT_H2N_U32(networkid.u);
847 netmask.u = RT_H2N_U32(netmask.u);
848
849 com::SafeArray<BSTR> nameServers;
850 HRESULT hrc = host->COMGETTER(NameServers)(ComSafeArrayAsOutParam(nameServers));
851 if (FAILED(hrc))
852 {
853 LogRel(("NATNetwork: Failed to get name servers from host with %Rhrc\n", hrc));
854 return;
855 }
856 ComPtr<IDHCPGlobalConfig> pDHCPConfig;
857 hrc = m->dhcpServer->COMGETTER(GlobalConfig)(pDHCPConfig.asOutParam());
858 if (FAILED(hrc))
859 {
860 LogRel(("NATNetwork: Failed to get global DHCP config when updating domain name server option with %Rhrc\n", hrc));
861 return;
862 }
863
864 size_t cAddresses = nameServers.size();
865 if (cAddresses)
866 {
867 RTCList<RTCString> lstServers;
868 /* The following code was copied (and adapted a bit) from VBoxNetDhcp::hostDnsServers */
869 /*
870 * Recent fashion is to run dnsmasq on 127.0.1.1 which we
871 * currently can't map. If that's the only nameserver we've got,
872 * we need to use DNS proxy for VMs to reach it.
873 */
874 bool fUnmappedLoopback = false;
875
876 for (size_t i = 0; i < cAddresses; ++i)
877 {
878 RTNETADDRIPV4 addr;
879
880 com::Utf8Str strNameServerAddress(nameServers[i]);
881 rc = RTNetStrToIPv4Addr(strNameServerAddress.c_str(), &addr);
882 if (RT_FAILURE(rc))
883 {
884 LogRel(("NATNetwork: Failed to parse IP address %s with %Rrc\n", strNameServerAddress.c_str(), rc));
885 continue;
886 }
887
888 if (addr.u == INADDR_ANY)
889 {
890 /*
891 * This doesn't seem to be very well documented except for
892 * RTFS of res_init.c, but INADDR_ANY is a valid value for
893 * for "nameserver".
894 */
895 addr.u = RT_H2N_U32_C(INADDR_LOOPBACK);
896 }
897
898 if (addr.au8[0] == 127)
899 {
900 settings::NATLoopbackOffsetList::const_iterator it;
901
902 it = std::find(m->s.llHostLoopbackOffsetList.begin(),
903 m->s.llHostLoopbackOffsetList.end(),
904 strNameServerAddress);
905 if (it == m->s.llHostLoopbackOffsetList.end())
906 {
907 fUnmappedLoopback = true;
908 continue;
909 }
910 addr.u = RT_H2N_U32(RT_N2H_U32(networkid.u) + it->u32Offset);
911 }
912 lstServers.append(RTCStringFmt("%RTnaipv4", addr));
913 }
914
915 if (lstServers.isEmpty() && fUnmappedLoopback)
916 lstServers.append(RTCStringFmt("%RTnaipv4", networkid.u | RT_H2N_U32_C(1U))); /* proxy */
917
918 hrc = pDHCPConfig->SetOption(DHCPOption_DomainNameServers, DHCPOptionEncoding_Normal, Bstr(RTCString::join(lstServers, " ")).raw());
919 if (FAILED(hrc))
920 LogRel(("NATNetwork: Failed to add domain name server option '%s' with %Rhrc\n", RTCString::join(lstServers, " ").c_str(), hrc));
921 }
922 else
923 pDHCPConfig->RemoveOption(DHCPOption_DomainNameServers);
924}
925
926void NATNetwork::i_updateDnsOptions()
927{
928 ComPtr<IHost> host;
929 if (SUCCEEDED(m->pVirtualBox->COMGETTER(Host)(host.asOutParam())))
930 {
931 i_updateDomainNameOption(host);
932 i_updateDomainNameServerOption(host);
933 }
934}
935
936
937HRESULT NATNetwork::start()
938{
939#ifdef VBOX_WITH_NAT_SERVICE
940 if (!m->s.fEnabled) return S_OK;
941 AssertReturn(!m->s.strNetworkName.isEmpty(), E_FAIL);
942
943 m->NATRunner.resetArguments();
944 m->NATRunner.addArgPair(NetworkServiceRunner::kpszKeyNetwork, Utf8Str(m->s.strNetworkName).c_str());
945
946 /* No portforwarding rules from command-line, all will be fetched via API */
947
948 if (m->s.fNeedDhcpServer)
949 {
950 /*
951 * Just to as idea... via API (on creation user pass the cidr of network and)
952 * and we calculate it's addreses (mutable?).
953 */
954
955 /*
956 * Configuration and running DHCP server:
957 * 1. find server first createDHCPServer
958 * 2. if return status is E_INVALARG => server already exists just find and start.
959 * 3. if return status neither E_INVALRG nor S_OK => return E_FAIL
960 * 4. if return status S_OK proceed to DHCP server configuration
961 * 5. call setConfiguration() and pass all required parameters
962 * 6. start dhcp server.
963 */
964 HRESULT hrc = m->pVirtualBox->FindDHCPServerByNetworkName(Bstr(m->s.strNetworkName).raw(),
965 m->dhcpServer.asOutParam());
966 switch (hrc)
967 {
968 case E_INVALIDARG:
969 /* server haven't beeen found let create it then */
970 hrc = m->pVirtualBox->CreateDHCPServer(Bstr(m->s.strNetworkName).raw(),
971 m->dhcpServer.asOutParam());
972 if (FAILED(hrc))
973 return E_FAIL;
974 /* breakthrough */
975
976 {
977 LogFunc(("gateway: %s, dhcpserver:%s, dhcplowerip:%s, dhcpupperip:%s\n",
978 m->IPv4Gateway.c_str(),
979 m->IPv4DhcpServer.c_str(),
980 m->IPv4DhcpServerLowerIp.c_str(),
981 m->IPv4DhcpServerUpperIp.c_str()));
982
983 hrc = m->dhcpServer->COMSETTER(Enabled)(true);
984
985 hrc = m->dhcpServer->SetConfiguration(Bstr(m->IPv4DhcpServer).raw(),
986 Bstr(m->IPv4NetworkMask).raw(),
987 Bstr(m->IPv4DhcpServerLowerIp).raw(),
988 Bstr(m->IPv4DhcpServerUpperIp).raw());
989 }
990 case S_OK:
991 break;
992
993 default:
994 return E_FAIL;
995 }
996
997#ifdef VBOX_WITH_DHCPD
998 i_updateDnsOptions();
999#endif /* VBOX_WITH_DHCPD */
1000 /* XXX: AddGlobalOption(DhcpOpt_Router,) - enables attachement of DhcpServer to Main (no longer true with VBoxNetDhcpd). */
1001 ComPtr<IDHCPGlobalConfig> pDHCPConfig;
1002 hrc = m->dhcpServer->COMGETTER(GlobalConfig)(pDHCPConfig.asOutParam());
1003 if (FAILED(hrc))
1004 {
1005 LogRel(("NATNetwork: Failed to get global DHCP config when updating IPv4 gateway option with %Rhrc\n", hrc));
1006 m->dhcpServer.setNull();
1007 return E_FAIL;
1008 }
1009 pDHCPConfig->SetOption(DHCPOption_Routers, DHCPOptionEncoding_Normal, Bstr(m->IPv4Gateway).raw());
1010
1011 hrc = m->dhcpServer->Start(Bstr::Empty.raw(), Bstr(TRUNKTYPE_WHATEVER).raw());
1012 if (FAILED(hrc))
1013 {
1014 m->dhcpServer.setNull();
1015 return E_FAIL;
1016 }
1017 }
1018
1019 if (RT_SUCCESS(m->NATRunner.start(false /* KillProcOnStop */)))
1020 {
1021 m->pVirtualBox->i_onNATNetworkStartStop(m->s.strNetworkName, TRUE);
1022 return S_OK;
1023 }
1024 /** @todo missing setError()! */
1025 return E_FAIL;
1026#else
1027 ReturnComNotImplemented();
1028#endif
1029}
1030
1031HRESULT NATNetwork::stop()
1032{
1033#ifdef VBOX_WITH_NAT_SERVICE
1034 m->pVirtualBox->i_onNATNetworkStartStop(m->s.strNetworkName, FALSE);
1035
1036 if (!m->dhcpServer.isNull())
1037 m->dhcpServer->Stop();
1038
1039 if (RT_SUCCESS(m->NATRunner.stop()))
1040 return S_OK;
1041
1042 /** @todo missing setError()! */
1043 return E_FAIL;
1044#else
1045 ReturnComNotImplemented();
1046#endif
1047}
1048
1049
1050void NATNetwork::i_getPortForwardRulesFromMap(std::vector<com::Utf8Str> &aPortForwardRules, settings::NATRulesMap &aRules)
1051{
1052 aPortForwardRules.resize(aRules.size());
1053 size_t i = 0;
1054 for (settings::NATRulesMap::const_iterator it = aRules.begin();
1055 it != aRules.end(); ++it, ++i)
1056 {
1057 settings::NATRule r = it->second;
1058 aPortForwardRules[i] = Utf8StrFmt("%s:%s:[%s]:%d:[%s]:%d",
1059 r.strName.c_str(),
1060 (r.proto == NATProtocol_TCP ? "tcp" : "udp"),
1061 r.strHostIP.c_str(),
1062 r.u16HostPort,
1063 r.strGuestIP.c_str(),
1064 r.u16GuestPort);
1065 }
1066}
1067
1068
1069int NATNetwork::i_findFirstAvailableOffset(ADDRESSLOOKUPTYPE addrType, uint32_t *poff)
1070{
1071 RTNETADDRIPV4 network, netmask;
1072
1073 int rc = RTCidrStrToIPv4(m->s.strIPv4NetworkCidr.c_str(),
1074 &network,
1075 &netmask);
1076 AssertRCReturn(rc, rc);
1077
1078 uint32_t off;
1079 for (off = 1; off < ~netmask.u; ++off)
1080 {
1081 bool skip = false;
1082 for (settings::NATLoopbackOffsetList::iterator it = m->s.llHostLoopbackOffsetList.begin();
1083 it != m->s.llHostLoopbackOffsetList.end();
1084 ++it)
1085 {
1086 if ((*it).u32Offset == off)
1087 {
1088 skip = true;
1089 break;
1090 }
1091
1092 }
1093
1094 if (skip)
1095 continue;
1096
1097 if (off == m->offGateway)
1098 {
1099 if (addrType == ADDR_GATEWAY)
1100 break;
1101 else
1102 continue;
1103 }
1104
1105 if (off == m->offDhcp)
1106 {
1107 if (addrType == ADDR_DHCP)
1108 break;
1109 else
1110 continue;
1111 }
1112
1113 if (!skip)
1114 break;
1115 }
1116
1117 if (poff)
1118 *poff = off;
1119
1120 return VINF_SUCCESS;
1121}
1122
1123int NATNetwork::i_recalculateIpv4AddressAssignments()
1124{
1125 RTNETADDRIPV4 network, netmask;
1126 int rc = RTCidrStrToIPv4(m->s.strIPv4NetworkCidr.c_str(),
1127 &network,
1128 &netmask);
1129 AssertRCReturn(rc, rc);
1130
1131 i_findFirstAvailableOffset(ADDR_GATEWAY, &m->offGateway);
1132 if (m->s.fNeedDhcpServer)
1133 i_findFirstAvailableOffset(ADDR_DHCP, &m->offDhcp);
1134
1135 /* I don't remember the reason CIDR calculated on the host. */
1136 RTNETADDRIPV4 gateway = network;
1137 gateway.u += m->offGateway;
1138 gateway.u = RT_H2N_U32(gateway.u);
1139 char szTmpIp[16];
1140 RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", gateway);
1141 m->IPv4Gateway = szTmpIp;
1142
1143 if (m->s.fNeedDhcpServer)
1144 {
1145 RTNETADDRIPV4 dhcpserver = network;
1146 dhcpserver.u += m->offDhcp;
1147
1148 /* XXX: adding more services should change the math here */
1149 RTNETADDRIPV4 dhcplowerip = network;
1150 uint32_t offDhcpLowerIp;
1151 i_findFirstAvailableOffset(ADDR_DHCPLOWERIP, &offDhcpLowerIp);
1152 dhcplowerip.u = RT_H2N_U32(dhcplowerip.u + offDhcpLowerIp);
1153
1154 RTNETADDRIPV4 dhcpupperip;
1155 dhcpupperip.u = RT_H2N_U32((network.u | ~netmask.u) - 1);
1156
1157 dhcpserver.u = RT_H2N_U32(dhcpserver.u);
1158 network.u = RT_H2N_U32(network.u);
1159
1160 RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcpserver);
1161 m->IPv4DhcpServer = szTmpIp;
1162 RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcplowerip);
1163 m->IPv4DhcpServerLowerIp = szTmpIp;
1164 RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcpupperip);
1165 m->IPv4DhcpServerUpperIp = szTmpIp;
1166
1167 LogFunc(("network:%RTnaipv4, dhcpserver:%RTnaipv4, dhcplowerip:%RTnaipv4, dhcpupperip:%RTnaipv4\n",
1168 network, dhcpserver, dhcplowerip, dhcpupperip));
1169 }
1170
1171 /* we need IPv4NetworkMask for NAT's gw service start */
1172 netmask.u = RT_H2N_U32(netmask.u);
1173 RTStrPrintf(szTmpIp, 16, "%RTnaipv4", netmask);
1174 m->IPv4NetworkMask = szTmpIp;
1175
1176 LogFlowFunc(("getaway:%RTnaipv4, netmask:%RTnaipv4\n", gateway, netmask));
1177 return VINF_SUCCESS;
1178}
1179
1180
1181int NATNetwork::i_recalculateIPv6Prefix()
1182{
1183 int rc;
1184
1185 RTNETADDRIPV4 net, mask;
1186 rc = RTCidrStrToIPv4(Utf8Str(m->s.strIPv4NetworkCidr).c_str(), &net, &mask);
1187 if (RT_FAILURE(rc))
1188 return rc;
1189
1190 net.u = RT_H2N_U32(net.u); /* XXX: fix RTCidrStrToIPv4! */
1191
1192 /*
1193 * [fd17:625c:f037:XXXX::/64] - RFC 4193 (ULA) Locally Assigned
1194 * Global ID where XXXX, 16 bit Subnet ID, are two bytes from the
1195 * middle of the IPv4 address, e.g. :dead: for 10.222.173.1
1196 */
1197 RTNETADDRIPV6 prefix;
1198 RT_ZERO(prefix);
1199
1200 prefix.au8[0] = 0xFD;
1201 prefix.au8[1] = 0x17;
1202
1203 prefix.au8[2] = 0x62;
1204 prefix.au8[3] = 0x5C;
1205
1206 prefix.au8[4] = 0xF0;
1207 prefix.au8[5] = 0x37;
1208
1209 prefix.au8[6] = net.au8[1];
1210 prefix.au8[7] = net.au8[2];
1211
1212 char szBuf[32];
1213 RTStrPrintf(szBuf, sizeof(szBuf), "%RTnaipv6/64", &prefix);
1214
1215 m->s.strIPv6Prefix = szBuf;
1216 return VINF_SUCCESS;
1217}
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