VirtualBox

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

Last change on this file since 49127 was 49127, checked in by vboxsync, 12 years ago

Main/NATNetworkImpl.cpp: do settings saving and event firing iff new value differs from old one.

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