VirtualBox

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

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

Main/NATNetwork: mark up two lost todos from day 1, forgotten there
for years. bugref:9909.

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

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