VirtualBox

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

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

fMain/NATNetwork: NATNetwork::setNetwork - validate and normalize the
argument. bugref:9909.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.7 KB
Line 
1/* $Id: NATNetworkImpl.cpp 87360 2021-01-21 20:19:42Z 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 RTNETADDRIPV4 Addr, Mask;
264 int iPrefix;
265 int rc;
266
267 rc = RTNetStrToIPv4Cidr(aIPv4NetworkCidr.c_str(), &Addr, &iPrefix);
268 if (RT_FAILURE(rc))
269 return setError(E_FAIL, "%s is not a valid IPv4 CIDR notation",
270 aIPv4NetworkCidr.c_str());
271
272 /*
273 * /32 is a single address, not a network, /31 is the degenerate
274 * point-to-point case, so reject these. Larger values and
275 * non-positive values are already treated as errors by the
276 * conversion.
277 */
278 if (iPrefix > 30)
279 return setError(E_FAIL, "%s network is too small", aIPv4NetworkCidr.c_str());
280
281 rc = RTNetPrefixToMaskIPv4(iPrefix, &Mask);
282 AssertRCReturn(rc, setError(E_FAIL,
283 "%s: internal error: failed to convert prefix %d to netmask: %Rrc",
284 aIPv4NetworkCidr.c_str(), iPrefix, rc));
285
286 if ((Addr.u & ~Mask.u) != 0)
287 return setError(E_FAIL,
288 "%s: the specified address is longer than the specified prefix",
289 aIPv4NetworkCidr.c_str());
290
291 /* normalized CIDR notation */
292 com::Utf8StrFmt strCidr("%RTnaipv4/%d", Addr.u, iPrefix);
293
294 {
295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
296
297 if (m->s.strIPv4NetworkCidr == strCidr)
298 return S_OK;
299
300 /**
301 * @todo
302 * silently ignore network cidr update for now.
303 * todo: keep internally guest address of port forward rule
304 * as offset from network id.
305 */
306 if (!m->s.mapPortForwardRules4.empty())
307 return S_OK;
308
309
310 m->s.strIPv4NetworkCidr = strCidr;
311 i_recalculateIpv4AddressAssignments();
312 }
313
314 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
315 HRESULT hrc = m->pVirtualBox->i_saveSettings();
316 ComAssertComRCRetRC(hrc);
317 return S_OK;
318}
319
320
321HRESULT NATNetwork::getIPv6Enabled(BOOL *aIPv6Enabled)
322{
323 *aIPv6Enabled = m->s.fIPv6Enabled;
324
325 return S_OK;
326}
327
328
329HRESULT NATNetwork::setIPv6Enabled(const BOOL aIPv6Enabled)
330{
331 {
332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
333
334 if (RT_BOOL(aIPv6Enabled) == m->s.fIPv6Enabled)
335 return S_OK;
336
337 m->s.fIPv6Enabled = RT_BOOL(aIPv6Enabled);
338 }
339
340 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
341 HRESULT rc = m->pVirtualBox->i_saveSettings();
342 ComAssertComRCRetRC(rc);
343
344 return S_OK;
345}
346
347
348HRESULT NATNetwork::getIPv6Prefix(com::Utf8Str &aIPv6Prefix)
349{
350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
351
352 aIPv6Prefix = m->s.strIPv6Prefix;
353 return S_OK;
354}
355
356HRESULT NATNetwork::setIPv6Prefix(const com::Utf8Str &aIPv6Prefix)
357{
358 {
359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
360
361 if (aIPv6Prefix == m->s.strIPv6Prefix)
362 return S_OK;
363
364 /**
365 * @todo
366 * silently ignore network IPv6 prefix update.
367 * todo: see similar todo in NATNetwork::COMSETTER(Network)(IN_BSTR)
368 */
369 if (!m->s.mapPortForwardRules6.empty())
370 return S_OK;
371
372 m->s.strIPv6Prefix = aIPv6Prefix;
373 }
374
375 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
376 HRESULT rc = m->pVirtualBox->i_saveSettings();
377 ComAssertComRCRetRC(rc);
378
379 return S_OK;
380}
381
382
383HRESULT NATNetwork::getAdvertiseDefaultIPv6RouteEnabled(BOOL *aAdvertiseDefaultIPv6Route)
384{
385 *aAdvertiseDefaultIPv6Route = m->s.fAdvertiseDefaultIPv6Route;
386
387 return S_OK;
388}
389
390
391HRESULT NATNetwork::setAdvertiseDefaultIPv6RouteEnabled(const BOOL aAdvertiseDefaultIPv6Route)
392{
393 {
394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
395
396 if (RT_BOOL(aAdvertiseDefaultIPv6Route) == m->s.fAdvertiseDefaultIPv6Route)
397 return S_OK;
398
399 m->s.fAdvertiseDefaultIPv6Route = RT_BOOL(aAdvertiseDefaultIPv6Route);
400
401 }
402
403 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
404 HRESULT rc = m->pVirtualBox->i_saveSettings();
405 ComAssertComRCRetRC(rc);
406
407 return S_OK;
408}
409
410
411HRESULT NATNetwork::getNeedDhcpServer(BOOL *aNeedDhcpServer)
412{
413 *aNeedDhcpServer = m->s.fNeedDhcpServer;
414
415 return S_OK;
416}
417
418HRESULT NATNetwork::setNeedDhcpServer(const BOOL aNeedDhcpServer)
419{
420 {
421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
422
423 if (RT_BOOL(aNeedDhcpServer) == m->s.fNeedDhcpServer)
424 return S_OK;
425
426 m->s.fNeedDhcpServer = RT_BOOL(aNeedDhcpServer);
427
428 i_recalculateIpv4AddressAssignments();
429
430 }
431
432 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
433 HRESULT rc = m->pVirtualBox->i_saveSettings();
434 ComAssertComRCRetRC(rc);
435
436 return S_OK;
437}
438
439HRESULT NATNetwork::getLocalMappings(std::vector<com::Utf8Str> &aLocalMappings)
440{
441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
442
443 aLocalMappings.resize(m->s.llHostLoopbackOffsetList.size());
444 size_t i = 0;
445 for (settings::NATLoopbackOffsetList::const_iterator it = m->s.llHostLoopbackOffsetList.begin();
446 it != m->s.llHostLoopbackOffsetList.end(); ++it, ++i)
447 {
448 aLocalMappings[i] = Utf8StrFmt("%s=%d",
449 (*it).strLoopbackHostAddress.c_str(),
450 (*it).u32Offset);
451 }
452
453 return S_OK;
454}
455
456HRESULT NATNetwork::addLocalMapping(const com::Utf8Str &aHostId, LONG aOffset)
457{
458 RTNETADDRIPV4 addr, net, mask;
459
460 int rc = RTNetStrToIPv4Addr(Utf8Str(aHostId).c_str(), &addr);
461 if (RT_FAILURE(rc))
462 return E_INVALIDARG;
463
464 /* check against 127/8 */
465 if ((RT_N2H_U32(addr.u) >> IN_CLASSA_NSHIFT) != IN_LOOPBACKNET)
466 return E_INVALIDARG;
467
468 /* check against networkid vs network mask */
469 rc = RTCidrStrToIPv4(Utf8Str(m->s.strIPv4NetworkCidr).c_str(), &net, &mask);
470 if (RT_FAILURE(rc))
471 return E_INVALIDARG;
472
473 if (((net.u + (uint32_t)aOffset) & mask.u) != net.u)
474 return E_INVALIDARG;
475
476 settings::NATLoopbackOffsetList::iterator it;
477
478 it = std::find(m->s.llHostLoopbackOffsetList.begin(),
479 m->s.llHostLoopbackOffsetList.end(),
480 aHostId);
481 if (it != m->s.llHostLoopbackOffsetList.end())
482 {
483 if (aOffset == 0) /* erase */
484 m->s.llHostLoopbackOffsetList.erase(it, it);
485 else /* modify */
486 {
487 settings::NATLoopbackOffsetList::iterator it1;
488 it1 = std::find(m->s.llHostLoopbackOffsetList.begin(),
489 m->s.llHostLoopbackOffsetList.end(),
490 (uint32_t)aOffset);
491 if (it1 != m->s.llHostLoopbackOffsetList.end())
492 return E_INVALIDARG; /* this offset is already registered. */
493
494 (*it).u32Offset = (uint32_t)aOffset;
495 }
496
497 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
498 return m->pVirtualBox->i_saveSettings();
499 }
500
501 /* injection */
502 it = std::find(m->s.llHostLoopbackOffsetList.begin(),
503 m->s.llHostLoopbackOffsetList.end(),
504 (uint32_t)aOffset);
505
506 if (it != m->s.llHostLoopbackOffsetList.end())
507 return E_INVALIDARG; /* offset is already registered. */
508
509 settings::NATHostLoopbackOffset off;
510 off.strLoopbackHostAddress = aHostId;
511 off.u32Offset = (uint32_t)aOffset;
512 m->s.llHostLoopbackOffsetList.push_back(off);
513
514 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
515 return m->pVirtualBox->i_saveSettings();
516}
517
518
519HRESULT NATNetwork::getLoopbackIp6(LONG *aLoopbackIp6)
520{
521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
522
523 *aLoopbackIp6 = (LONG)m->s.u32HostLoopback6Offset;
524 return S_OK;
525}
526
527
528HRESULT NATNetwork::setLoopbackIp6(LONG aLoopbackIp6)
529{
530 {
531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
532
533 if (aLoopbackIp6 < 0)
534 return E_INVALIDARG;
535
536 if (static_cast<uint32_t>(aLoopbackIp6) == m->s.u32HostLoopback6Offset)
537 return S_OK;
538
539 m->s.u32HostLoopback6Offset = (uint32_t)aLoopbackIp6;
540 }
541
542 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
543 return m->pVirtualBox->i_saveSettings();
544}
545
546
547HRESULT NATNetwork::getPortForwardRules4(std::vector<com::Utf8Str> &aPortForwardRules4)
548{
549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
550 i_getPortForwardRulesFromMap(aPortForwardRules4,
551 m->s.mapPortForwardRules4);
552 return S_OK;
553}
554
555HRESULT NATNetwork::getPortForwardRules6(std::vector<com::Utf8Str> &aPortForwardRules6)
556{
557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
558 i_getPortForwardRulesFromMap(aPortForwardRules6,
559 m->s.mapPortForwardRules6);
560 return S_OK;
561}
562
563HRESULT NATNetwork::addPortForwardRule(BOOL aIsIpv6,
564 const com::Utf8Str &aPortForwardRuleName,
565 NATProtocol_T aProto,
566 const com::Utf8Str &aHostIp,
567 USHORT aHostPort,
568 const com::Utf8Str &aGuestIp,
569 USHORT aGuestPort)
570{
571 {
572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
573 Utf8Str name = aPortForwardRuleName;
574 Utf8Str proto;
575 settings::NATRule r;
576 settings::NATRulesMap &mapRules = aIsIpv6 ? m->s.mapPortForwardRules6 : m->s.mapPortForwardRules4;
577 switch (aProto)
578 {
579 case NATProtocol_TCP:
580 proto = "tcp";
581 break;
582 case NATProtocol_UDP:
583 proto = "udp";
584 break;
585 default:
586 return E_INVALIDARG;
587 }
588 if (name.isEmpty())
589 name = Utf8StrFmt("%s_[%s]%%%d_[%s]%%%d", proto.c_str(),
590 aHostIp.c_str(), aHostPort,
591 aGuestIp.c_str(), aGuestPort);
592
593 for (settings::NATRulesMap::iterator it = mapRules.begin(); it != mapRules.end(); ++it)
594 {
595 r = it->second;
596 if (it->first == name)
597 return setError(E_INVALIDARG,
598 tr("A NAT rule of this name already exists"));
599 if ( r.strHostIP == aHostIp
600 && r.u16HostPort == aHostPort
601 && r.proto == aProto)
602 return setError(E_INVALIDARG,
603 tr("A NAT rule for this host port and this host IP already exists"));
604 }
605
606 r.strName = name.c_str();
607 r.proto = aProto;
608 r.strHostIP = aHostIp;
609 r.u16HostPort = aHostPort;
610 r.strGuestIP = aGuestIp;
611 r.u16GuestPort = aGuestPort;
612 mapRules.insert(std::make_pair(name, r));
613 }
614 {
615 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
616 HRESULT rc = m->pVirtualBox->i_saveSettings();
617 ComAssertComRCRetRC(rc);
618 }
619
620 m->pVirtualBox->i_onNATNetworkPortForward(m->s.strNetworkName, TRUE, aIsIpv6,
621 aPortForwardRuleName, aProto,
622 aHostIp, aHostPort,
623 aGuestIp, aGuestPort);
624
625 /* Notify listerners listening on this network only */
626 ::FireNATNetworkPortForwardEvent(m->pEventSource, m->s.strNetworkName, TRUE,
627 aIsIpv6, aPortForwardRuleName, aProto,
628 aHostIp, aHostPort,
629 aGuestIp, aGuestPort);
630
631 return S_OK;
632}
633
634HRESULT NATNetwork::removePortForwardRule(BOOL aIsIpv6, const com::Utf8Str &aPortForwardRuleName)
635{
636 Utf8Str strHostIP;
637 Utf8Str strGuestIP;
638 uint16_t u16HostPort;
639 uint16_t u16GuestPort;
640 NATProtocol_T proto;
641
642 {
643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
644 settings::NATRulesMap &mapRules = aIsIpv6 ? m->s.mapPortForwardRules6 : m->s.mapPortForwardRules4;
645 settings::NATRulesMap::iterator it = mapRules.find(aPortForwardRuleName);
646
647 if (it == mapRules.end())
648 return E_INVALIDARG;
649
650 strHostIP = it->second.strHostIP;
651 strGuestIP = it->second.strGuestIP;
652 u16HostPort = it->second.u16HostPort;
653 u16GuestPort = it->second.u16GuestPort;
654 proto = it->second.proto;
655
656 mapRules.erase(it);
657 }
658
659 {
660 AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
661 HRESULT rc = m->pVirtualBox->i_saveSettings();
662 ComAssertComRCRetRC(rc);
663 }
664
665 m->pVirtualBox->i_onNATNetworkPortForward(m->s.strNetworkName, FALSE, aIsIpv6, aPortForwardRuleName, proto,
666 strHostIP, u16HostPort, strGuestIP, u16GuestPort);
667
668 /* Notify listerners listening on this network only */
669 ::FireNATNetworkPortForwardEvent(m->pEventSource, m->s.strNetworkName, FALSE, aIsIpv6, aPortForwardRuleName, proto,
670 strHostIP, u16HostPort, strGuestIP, u16GuestPort);
671 return S_OK;
672}
673
674
675void NATNetwork::i_updateDomainNameOption(ComPtr<IHost> &host)
676{
677 com::Bstr domain;
678 if (FAILED(host->COMGETTER(DomainName)(domain.asOutParam())))
679 LogRel(("NATNetwork: Failed to get host's domain name\n"));
680 ComPtr<IDHCPGlobalConfig> pDHCPConfig;
681 HRESULT hrc = m->dhcpServer->COMGETTER(GlobalConfig)(pDHCPConfig.asOutParam());
682 if (FAILED(hrc))
683 {
684 LogRel(("NATNetwork: Failed to get global DHCP config when updating domain name option with %Rhrc\n", hrc));
685 return;
686 }
687 if (domain.isNotEmpty())
688 {
689 hrc = pDHCPConfig->SetOption(DHCPOption_DomainName, DHCPOptionEncoding_Normal, domain.raw());
690 if (FAILED(hrc))
691 LogRel(("NATNetwork: Failed to add domain name option with %Rhrc\n", hrc));
692 }
693 else
694 pDHCPConfig->RemoveOption(DHCPOption_DomainName);
695}
696
697void NATNetwork::i_updateDomainNameServerOption(ComPtr<IHost> &host)
698{
699 RTNETADDRIPV4 networkid, netmask;
700
701 int rc = RTCidrStrToIPv4(m->s.strIPv4NetworkCidr.c_str(), &networkid, &netmask);
702 if (RT_FAILURE(rc))
703 {
704 LogRel(("NATNetwork: Failed to parse cidr %s with %Rrc\n", m->s.strIPv4NetworkCidr.c_str(), rc));
705 return;
706 }
707
708 /* XXX: these are returned, surprisingly, in host order */
709 networkid.u = RT_H2N_U32(networkid.u);
710 netmask.u = RT_H2N_U32(netmask.u);
711
712 com::SafeArray<BSTR> nameServers;
713 HRESULT hrc = host->COMGETTER(NameServers)(ComSafeArrayAsOutParam(nameServers));
714 if (FAILED(hrc))
715 {
716 LogRel(("NATNetwork: Failed to get name servers from host with %Rhrc\n", hrc));
717 return;
718 }
719 ComPtr<IDHCPGlobalConfig> pDHCPConfig;
720 hrc = m->dhcpServer->COMGETTER(GlobalConfig)(pDHCPConfig.asOutParam());
721 if (FAILED(hrc))
722 {
723 LogRel(("NATNetwork: Failed to get global DHCP config when updating domain name server option with %Rhrc\n", hrc));
724 return;
725 }
726
727 size_t cAddresses = nameServers.size();
728 if (cAddresses)
729 {
730 RTCList<RTCString> lstServers;
731 /* The following code was copied (and adapted a bit) from VBoxNetDhcp::hostDnsServers */
732 /*
733 * Recent fashion is to run dnsmasq on 127.0.1.1 which we
734 * currently can't map. If that's the only nameserver we've got,
735 * we need to use DNS proxy for VMs to reach it.
736 */
737 bool fUnmappedLoopback = false;
738
739 for (size_t i = 0; i < cAddresses; ++i)
740 {
741 RTNETADDRIPV4 addr;
742
743 com::Utf8Str strNameServerAddress(nameServers[i]);
744 rc = RTNetStrToIPv4Addr(strNameServerAddress.c_str(), &addr);
745 if (RT_FAILURE(rc))
746 {
747 LogRel(("NATNetwork: Failed to parse IP address %s with %Rrc\n", strNameServerAddress.c_str(), rc));
748 continue;
749 }
750
751 if (addr.u == INADDR_ANY)
752 {
753 /*
754 * This doesn't seem to be very well documented except for
755 * RTFS of res_init.c, but INADDR_ANY is a valid value for
756 * for "nameserver".
757 */
758 addr.u = RT_H2N_U32_C(INADDR_LOOPBACK);
759 }
760
761 if (addr.au8[0] == 127)
762 {
763 settings::NATLoopbackOffsetList::const_iterator it;
764
765 it = std::find(m->s.llHostLoopbackOffsetList.begin(),
766 m->s.llHostLoopbackOffsetList.end(),
767 strNameServerAddress);
768 if (it == m->s.llHostLoopbackOffsetList.end())
769 {
770 fUnmappedLoopback = true;
771 continue;
772 }
773 addr.u = RT_H2N_U32(RT_N2H_U32(networkid.u) + it->u32Offset);
774 }
775 lstServers.append(RTCStringFmt("%RTnaipv4", addr));
776 }
777
778 if (lstServers.isEmpty() && fUnmappedLoopback)
779 lstServers.append(RTCStringFmt("%RTnaipv4", networkid.u | RT_H2N_U32_C(1U))); /* proxy */
780
781 hrc = pDHCPConfig->SetOption(DHCPOption_DomainNameServers, DHCPOptionEncoding_Normal, Bstr(RTCString::join(lstServers, " ")).raw());
782 if (FAILED(hrc))
783 LogRel(("NATNetwork: Failed to add domain name server option '%s' with %Rhrc\n", RTCString::join(lstServers, " ").c_str(), hrc));
784 }
785 else
786 pDHCPConfig->RemoveOption(DHCPOption_DomainNameServers);
787}
788
789void NATNetwork::i_updateDnsOptions()
790{
791 ComPtr<IHost> host;
792 if (SUCCEEDED(m->pVirtualBox->COMGETTER(Host)(host.asOutParam())))
793 {
794 i_updateDomainNameOption(host);
795 i_updateDomainNameServerOption(host);
796 }
797}
798
799
800HRESULT NATNetwork::start()
801{
802#ifdef VBOX_WITH_NAT_SERVICE
803 if (!m->s.fEnabled) return S_OK;
804 AssertReturn(!m->s.strNetworkName.isEmpty(), E_FAIL);
805
806 m->NATRunner.addArgPair(NetworkServiceRunner::kpszKeyNetwork, Utf8Str(m->s.strNetworkName).c_str());
807 m->NATRunner.addArgPair(NetworkServiceRunner::kpszKeyTrunkType, Utf8Str(TRUNKTYPE_WHATEVER).c_str());
808 m->NATRunner.addArgPair(NetworkServiceRunner::kpszIpAddress, Utf8Str(m->IPv4Gateway).c_str());
809 m->NATRunner.addArgPair(NetworkServiceRunner::kpszIpNetmask, Utf8Str(m->IPv4NetworkMask).c_str());
810
811 /* No portforwarding rules from command-line, all will be fetched via API */
812
813 if (m->s.fNeedDhcpServer)
814 {
815 /*
816 * Just to as idea... via API (on creation user pass the cidr of network and)
817 * and we calculate it's addreses (mutable?).
818 */
819
820 /*
821 * Configuration and running DHCP server:
822 * 1. find server first createDHCPServer
823 * 2. if return status is E_INVALARG => server already exists just find and start.
824 * 3. if return status neither E_INVALRG nor S_OK => return E_FAIL
825 * 4. if return status S_OK proceed to DHCP server configuration
826 * 5. call setConfiguration() and pass all required parameters
827 * 6. start dhcp server.
828 */
829 HRESULT hrc = m->pVirtualBox->FindDHCPServerByNetworkName(Bstr(m->s.strNetworkName).raw(),
830 m->dhcpServer.asOutParam());
831 switch (hrc)
832 {
833 case E_INVALIDARG:
834 /* server haven't beeen found let create it then */
835 hrc = m->pVirtualBox->CreateDHCPServer(Bstr(m->s.strNetworkName).raw(),
836 m->dhcpServer.asOutParam());
837 if (FAILED(hrc))
838 return E_FAIL;
839 /* breakthrough */
840
841 {
842 LogFunc(("gateway: %s, dhcpserver:%s, dhcplowerip:%s, dhcpupperip:%s\n",
843 m->IPv4Gateway.c_str(),
844 m->IPv4DhcpServer.c_str(),
845 m->IPv4DhcpServerLowerIp.c_str(),
846 m->IPv4DhcpServerUpperIp.c_str()));
847
848 hrc = m->dhcpServer->COMSETTER(Enabled)(true);
849
850 hrc = m->dhcpServer->SetConfiguration(Bstr(m->IPv4DhcpServer).raw(),
851 Bstr(m->IPv4NetworkMask).raw(),
852 Bstr(m->IPv4DhcpServerLowerIp).raw(),
853 Bstr(m->IPv4DhcpServerUpperIp).raw());
854 }
855 case S_OK:
856 break;
857
858 default:
859 return E_FAIL;
860 }
861
862#ifdef VBOX_WITH_DHCPD
863 i_updateDnsOptions();
864#endif /* VBOX_WITH_DHCPD */
865 /* XXX: AddGlobalOption(DhcpOpt_Router,) - enables attachement of DhcpServer to Main (no longer true with VBoxNetDhcpd). */
866 ComPtr<IDHCPGlobalConfig> pDHCPConfig;
867 hrc = m->dhcpServer->COMGETTER(GlobalConfig)(pDHCPConfig.asOutParam());
868 if (FAILED(hrc))
869 {
870 LogRel(("NATNetwork: Failed to get global DHCP config when updating IPv4 gateway option with %Rhrc\n", hrc));
871 m->dhcpServer.setNull();
872 return E_FAIL;
873 }
874 pDHCPConfig->SetOption(DHCPOption_Routers, DHCPOptionEncoding_Normal, Bstr(m->IPv4Gateway).raw());
875
876 hrc = m->dhcpServer->Start(Bstr::Empty.raw(), Bstr(TRUNKTYPE_WHATEVER).raw());
877 if (FAILED(hrc))
878 {
879 m->dhcpServer.setNull();
880 return E_FAIL;
881 }
882 }
883
884 if (RT_SUCCESS(m->NATRunner.start(false /* KillProcOnStop */)))
885 {
886 m->pVirtualBox->i_onNATNetworkStartStop(m->s.strNetworkName, TRUE);
887 return S_OK;
888 }
889 /** @todo missing setError()! */
890 return E_FAIL;
891#else
892 ReturnComNotImplemented();
893#endif
894}
895
896HRESULT NATNetwork::stop()
897{
898#ifdef VBOX_WITH_NAT_SERVICE
899 m->pVirtualBox->i_onNATNetworkStartStop(m->s.strNetworkName, FALSE);
900
901 if (!m->dhcpServer.isNull())
902 m->dhcpServer->Stop();
903
904 if (RT_SUCCESS(m->NATRunner.stop()))
905 return S_OK;
906
907 /** @todo missing setError()! */
908 return E_FAIL;
909#else
910 ReturnComNotImplemented();
911#endif
912}
913
914
915void NATNetwork::i_getPortForwardRulesFromMap(std::vector<com::Utf8Str> &aPortForwardRules, settings::NATRulesMap &aRules)
916{
917 aPortForwardRules.resize(aRules.size());
918 size_t i = 0;
919 for (settings::NATRulesMap::const_iterator it = aRules.begin();
920 it != aRules.end(); ++it, ++i)
921 {
922 settings::NATRule r = it->second;
923 aPortForwardRules[i] = Utf8StrFmt("%s:%s:[%s]:%d:[%s]:%d",
924 r.strName.c_str(),
925 (r.proto == NATProtocol_TCP ? "tcp" : "udp"),
926 r.strHostIP.c_str(),
927 r.u16HostPort,
928 r.strGuestIP.c_str(),
929 r.u16GuestPort);
930 }
931}
932
933
934int NATNetwork::i_findFirstAvailableOffset(ADDRESSLOOKUPTYPE addrType, uint32_t *poff)
935{
936 RTNETADDRIPV4 network, netmask;
937
938 int rc = RTCidrStrToIPv4(m->s.strIPv4NetworkCidr.c_str(),
939 &network,
940 &netmask);
941 AssertRCReturn(rc, rc);
942
943 uint32_t off;
944 for (off = 1; off < ~netmask.u; ++off)
945 {
946 bool skip = false;
947 for (settings::NATLoopbackOffsetList::iterator it = m->s.llHostLoopbackOffsetList.begin();
948 it != m->s.llHostLoopbackOffsetList.end();
949 ++it)
950 {
951 if ((*it).u32Offset == off)
952 {
953 skip = true;
954 break;
955 }
956
957 }
958
959 if (skip)
960 continue;
961
962 if (off == m->offGateway)
963 {
964 if (addrType == ADDR_GATEWAY)
965 break;
966 else
967 continue;
968 }
969
970 if (off == m->offDhcp)
971 {
972 if (addrType == ADDR_DHCP)
973 break;
974 else
975 continue;
976 }
977
978 if (!skip)
979 break;
980 }
981
982 if (poff)
983 *poff = off;
984
985 return VINF_SUCCESS;
986}
987
988int NATNetwork::i_recalculateIpv4AddressAssignments()
989{
990 RTNETADDRIPV4 network, netmask;
991 int rc = RTCidrStrToIPv4(m->s.strIPv4NetworkCidr.c_str(),
992 &network,
993 &netmask);
994 AssertRCReturn(rc, rc);
995
996 i_findFirstAvailableOffset(ADDR_GATEWAY, &m->offGateway);
997 if (m->s.fNeedDhcpServer)
998 i_findFirstAvailableOffset(ADDR_DHCP, &m->offDhcp);
999
1000 /* I don't remember the reason CIDR calculated on the host. */
1001 RTNETADDRIPV4 gateway = network;
1002 gateway.u += m->offGateway;
1003 gateway.u = RT_H2N_U32(gateway.u);
1004 char szTmpIp[16];
1005 RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", gateway);
1006 m->IPv4Gateway = szTmpIp;
1007
1008 if (m->s.fNeedDhcpServer)
1009 {
1010 RTNETADDRIPV4 dhcpserver = network;
1011 dhcpserver.u += m->offDhcp;
1012
1013 /* XXX: adding more services should change the math here */
1014 RTNETADDRIPV4 dhcplowerip = network;
1015 uint32_t offDhcpLowerIp;
1016 i_findFirstAvailableOffset(ADDR_DHCPLOWERIP, &offDhcpLowerIp);
1017 dhcplowerip.u = RT_H2N_U32(dhcplowerip.u + offDhcpLowerIp);
1018
1019 RTNETADDRIPV4 dhcpupperip;
1020 dhcpupperip.u = RT_H2N_U32((network.u | ~netmask.u) - 1);
1021
1022 dhcpserver.u = RT_H2N_U32(dhcpserver.u);
1023 network.u = RT_H2N_U32(network.u);
1024
1025 RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcpserver);
1026 m->IPv4DhcpServer = szTmpIp;
1027 RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcplowerip);
1028 m->IPv4DhcpServerLowerIp = szTmpIp;
1029 RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcpupperip);
1030 m->IPv4DhcpServerUpperIp = szTmpIp;
1031
1032 LogFunc(("network:%RTnaipv4, dhcpserver:%RTnaipv4, dhcplowerip:%RTnaipv4, dhcpupperip:%RTnaipv4\n",
1033 network, dhcpserver, dhcplowerip, dhcpupperip));
1034 }
1035
1036 /* we need IPv4NetworkMask for NAT's gw service start */
1037 netmask.u = RT_H2N_U32(netmask.u);
1038 RTStrPrintf(szTmpIp, 16, "%RTnaipv4", netmask);
1039 m->IPv4NetworkMask = szTmpIp;
1040
1041 LogFlowFunc(("getaway:%RTnaipv4, netmask:%RTnaipv4\n", gateway, netmask));
1042 return VINF_SUCCESS;
1043}
1044
1045
1046int NATNetwork::i_recalculateIPv6Prefix()
1047{
1048 int rc;
1049
1050 RTNETADDRIPV4 net, mask;
1051 rc = RTCidrStrToIPv4(Utf8Str(m->s.strIPv4NetworkCidr).c_str(), &net, &mask);
1052 if (RT_FAILURE(rc))
1053 return rc;
1054
1055 net.u = RT_H2N_U32(net.u); /* XXX: fix RTCidrStrToIPv4! */
1056
1057 /*
1058 * [fd17:625c:f037:XXXX::/64] - RFC 4193 (ULA) Locally Assigned
1059 * Global ID where XXXX, 16 bit Subnet ID, are two bytes from the
1060 * middle of the IPv4 address, e.g. :dead: for 10.222.173.1
1061 */
1062 RTNETADDRIPV6 prefix;
1063 RT_ZERO(prefix);
1064
1065 prefix.au8[0] = 0xFD;
1066 prefix.au8[1] = 0x17;
1067
1068 prefix.au8[2] = 0x62;
1069 prefix.au8[3] = 0x5C;
1070
1071 prefix.au8[4] = 0xF0;
1072 prefix.au8[5] = 0x37;
1073
1074 prefix.au8[6] = net.au8[1];
1075 prefix.au8[7] = net.au8[2];
1076
1077 char szBuf[32];
1078 RTStrPrintf(szBuf, sizeof(szBuf), "%RTnaipv6/64", &prefix);
1079
1080 m->s.strIPv6Prefix = szBuf;
1081 return VINF_SUCCESS;
1082}
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