VirtualBox

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

Last change on this file since 93348 was 93348, checked in by vboxsync, 3 years ago

NAT/Net: When enabling IPv6, check if IPv6 prefix is set. If it's not

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