VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/DHCPServerImpl.cpp@ 75617

Last change on this file since 75617 was 75617, checked in by vboxsync, 6 years ago

Rolled back r126789.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.4 KB
Line 
1/* $Id: DHCPServerImpl.cpp 75617 2018-11-20 11:59:31Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM class implementation
6 */
7
8/*
9 * Copyright (C) 2006-2017 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20#include <string>
21#include "NetworkServiceRunner.h"
22#include "DHCPServerImpl.h"
23#include "AutoCaller.h"
24#include "Logging.h"
25
26#include <iprt/asm.h>
27#include <iprt/net.h>
28#include <iprt/cpp/utils.h>
29
30#include <VBox/com/array.h>
31#include <VBox/settings.h>
32
33#include "VirtualBoxImpl.h"
34
35// constructor / destructor
36/////////////////////////////////////////////////////////////////////////////
37const std::string DHCPServerRunner::kDsrKeyGateway = "--gateway";
38const std::string DHCPServerRunner::kDsrKeyLowerIp = "--lower-ip";
39const std::string DHCPServerRunner::kDsrKeyUpperIp = "--upper-ip";
40
41
42struct DHCPServer::Data
43{
44 Data()
45 : enabled(FALSE)
46 , router(false)
47 {}
48
49 Utf8Str IPAddress;
50 Utf8Str lowerIP;
51 Utf8Str upperIP;
52
53 BOOL enabled;
54 bool router;
55 DHCPServerRunner dhcp;
56
57 settings::DhcpOptionMap GlobalDhcpOptions;
58 settings::VmSlot2OptionsMap VmSlot2Options;
59};
60
61
62DHCPServer::DHCPServer()
63 : m(NULL)
64 , mVirtualBox(NULL)
65{
66 m = new DHCPServer::Data();
67}
68
69
70DHCPServer::~DHCPServer()
71{
72 if (m)
73 {
74 delete m;
75 m = NULL;
76 }
77}
78
79
80HRESULT DHCPServer::FinalConstruct()
81{
82 return BaseFinalConstruct();
83}
84
85
86void DHCPServer::FinalRelease()
87{
88 uninit ();
89
90 BaseFinalRelease();
91}
92
93
94void DHCPServer::uninit()
95{
96 /* Enclose the state transition Ready->InUninit->NotReady */
97 AutoUninitSpan autoUninitSpan(this);
98 if (autoUninitSpan.uninitDone())
99 return;
100
101 unconst(mVirtualBox) = NULL;
102}
103
104
105HRESULT DHCPServer::init(VirtualBox *aVirtualBox, const Utf8Str &aName)
106{
107 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
108
109 AutoInitSpan autoInitSpan(this);
110 AssertReturn(autoInitSpan.isOk(), E_FAIL);
111
112 /* share VirtualBox weakly (parent remains NULL so far) */
113 unconst(mVirtualBox) = aVirtualBox;
114
115 unconst(mName) = aName;
116 m->IPAddress = "0.0.0.0";
117 m->GlobalDhcpOptions[DhcpOpt_SubnetMask] = settings::DhcpOptValue("0.0.0.0");
118 m->enabled = FALSE;
119
120 m->lowerIP = "0.0.0.0";
121 m->upperIP = "0.0.0.0";
122
123 /* Confirm a successful initialization */
124 autoInitSpan.setSucceeded();
125
126 return S_OK;
127}
128
129
130HRESULT DHCPServer::init(VirtualBox *aVirtualBox,
131 const settings::DHCPServer &data)
132{
133 /* Enclose the state transition NotReady->InInit->Ready */
134 AutoInitSpan autoInitSpan(this);
135 AssertReturn(autoInitSpan.isOk(), E_FAIL);
136
137 /* share VirtualBox weakly (parent remains NULL so far) */
138 unconst(mVirtualBox) = aVirtualBox;
139
140 unconst(mName) = data.strNetworkName;
141 m->IPAddress = data.strIPAddress;
142 m->enabled = data.fEnabled;
143 m->lowerIP = data.strIPLower;
144 m->upperIP = data.strIPUpper;
145
146 m->GlobalDhcpOptions.clear();
147 m->GlobalDhcpOptions.insert(data.GlobalDhcpOptions.begin(),
148 data.GlobalDhcpOptions.end());
149
150 m->VmSlot2Options.clear();
151 m->VmSlot2Options.insert(data.VmSlot2OptionsM.begin(),
152 data.VmSlot2OptionsM.end());
153
154 autoInitSpan.setSucceeded();
155
156 return S_OK;
157}
158
159
160HRESULT DHCPServer::i_saveSettings(settings::DHCPServer &data)
161{
162 AutoCaller autoCaller(this);
163 if (FAILED(autoCaller.rc())) return autoCaller.rc();
164
165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
166
167 data.strNetworkName = mName;
168 data.strIPAddress = m->IPAddress;
169
170 data.fEnabled = !!m->enabled;
171 data.strIPLower = m->lowerIP;
172 data.strIPUpper = m->upperIP;
173
174 data.GlobalDhcpOptions.clear();
175 data.GlobalDhcpOptions.insert(m->GlobalDhcpOptions.begin(),
176 m->GlobalDhcpOptions.end());
177
178 data.VmSlot2OptionsM.clear();
179 data.VmSlot2OptionsM.insert(m->VmSlot2Options.begin(),
180 m->VmSlot2Options.end());
181
182 return S_OK;
183}
184
185
186HRESULT DHCPServer::getNetworkName(com::Utf8Str &aName)
187{
188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
189
190 aName = mName;
191 return S_OK;
192}
193
194
195HRESULT DHCPServer::getEnabled(BOOL *aEnabled)
196{
197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
198
199 *aEnabled = m->enabled;
200 return S_OK;
201}
202
203
204HRESULT DHCPServer::setEnabled(BOOL aEnabled)
205{
206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
207 m->enabled = aEnabled;
208
209 // save the global settings; for that we should hold only the VirtualBox lock
210 alock.release();
211 AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
212 HRESULT rc = mVirtualBox->i_saveSettings();
213
214 return rc;
215}
216
217
218HRESULT DHCPServer::getIPAddress(com::Utf8Str &aIPAddress)
219{
220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
221
222 aIPAddress = Utf8Str(m->IPAddress);
223 return S_OK;
224}
225
226
227HRESULT DHCPServer::getNetworkMask(com::Utf8Str &aNetworkMask)
228{
229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
230
231 aNetworkMask = m->GlobalDhcpOptions[DhcpOpt_SubnetMask].text;
232 return S_OK;
233}
234
235
236HRESULT DHCPServer::getLowerIP(com::Utf8Str &aIPAddress)
237{
238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
239
240 aIPAddress = Utf8Str(m->lowerIP);
241 return S_OK;
242}
243
244
245HRESULT DHCPServer::getUpperIP(com::Utf8Str &aIPAddress)
246{
247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
248
249 aIPAddress = Utf8Str(m->upperIP);
250 return S_OK;
251}
252
253
254HRESULT DHCPServer::setConfiguration(const com::Utf8Str &aIPAddress,
255 const com::Utf8Str &aNetworkMask,
256 const com::Utf8Str &aLowerIP,
257 const com::Utf8Str &aUpperIP)
258{
259 RTNETADDRIPV4 IPAddress, NetworkMask, LowerIP, UpperIP;
260
261 int vrc = RTNetStrToIPv4Addr(aIPAddress.c_str(), &IPAddress);
262 if (RT_FAILURE(vrc))
263 return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid server address");
264
265 vrc = RTNetStrToIPv4Addr(aNetworkMask.c_str(), &NetworkMask);
266 if (RT_FAILURE(vrc))
267 return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid netmask");
268
269 vrc = RTNetStrToIPv4Addr(aLowerIP.c_str(), &LowerIP);
270 if (RT_FAILURE(vrc))
271 return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid range lower address");
272
273 vrc = RTNetStrToIPv4Addr(aUpperIP.c_str(), &UpperIP);
274 if (RT_FAILURE(vrc))
275 return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid range upper address");
276
277 /*
278 * Insist on continuous mask. May be also accept prefix length
279 * here or address/prefix for aIPAddress?
280 */
281 vrc = RTNetMaskToPrefixIPv4(&NetworkMask, NULL);
282 if (RT_FAILURE(vrc))
283 return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid netmask");
284
285 /* It's more convenient to convert to host order once */
286 IPAddress.u = RT_N2H_U32(IPAddress.u);
287 NetworkMask.u = RT_N2H_U32(NetworkMask.u);
288 LowerIP.u = RT_N2H_U32(LowerIP.u);
289 UpperIP.u = RT_N2H_U32(UpperIP.u);
290
291 /*
292 * Addresses must be unicast and from the same network
293 */
294 if ( (IPAddress.u & UINT32_C(0xe0000000)) == UINT32_C(0xe0000000)
295 || (IPAddress.u & ~NetworkMask.u) == 0
296 || ((IPAddress.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
297 return mVirtualBox->setError(E_INVALIDARG, "Invalid server address");
298
299 if ( (LowerIP.u & UINT32_C(0xe0000000)) == UINT32_C(0xe0000000)
300 || (LowerIP.u & NetworkMask.u) != (IPAddress.u &NetworkMask.u)
301 || (LowerIP.u & ~NetworkMask.u) == 0
302 || ((LowerIP.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
303 return mVirtualBox->setError(E_INVALIDARG, "Invalid range lower address");
304
305 if ( (UpperIP.u & UINT32_C(0xe0000000)) == UINT32_C(0xe0000000)
306 || (UpperIP.u & NetworkMask.u) != (IPAddress.u &NetworkMask.u)
307 || (UpperIP.u & ~NetworkMask.u) == 0
308 || ((UpperIP.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
309 return mVirtualBox->setError(E_INVALIDARG, "Invalid range upper address");
310
311 /* The range should be valid ... */
312 if (LowerIP.u > UpperIP.u)
313 return mVirtualBox->setError(E_INVALIDARG, "Invalid range bounds");
314
315 /* ... and shouldn't contain the server's address */
316 if (LowerIP.u <= IPAddress.u && IPAddress.u <= UpperIP.u)
317 return mVirtualBox->setError(E_INVALIDARG, "Server address within range bounds");
318
319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
320 m->IPAddress = aIPAddress;
321 m->GlobalDhcpOptions[DhcpOpt_SubnetMask] = aNetworkMask;
322
323 m->lowerIP = aLowerIP;
324 m->upperIP = aUpperIP;
325
326 // save the global settings; for that we should hold only the VirtualBox lock
327 alock.release();
328 AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
329 return mVirtualBox->i_saveSettings();
330}
331
332
333HRESULT DHCPServer::encodeOption(com::Utf8Str &aEncoded,
334 uint32_t aOptCode,
335 const settings::DhcpOptValue &aOptValue)
336{
337 switch (aOptValue.encoding)
338 {
339 case DhcpOptEncoding_Legacy:
340 {
341 /*
342 * This is original encoding which assumed that for each
343 * option we know its format and so we know how option
344 * "value" text is to be interpreted.
345 *
346 * "2:10800" # integer 32
347 * "6:1.2.3.4 8.8.8.8" # array of ip-address
348 */
349 aEncoded = Utf8StrFmt("%d:%s", aOptCode, aOptValue.text.c_str());
350 break;
351 }
352
353 case DhcpOptEncoding_Hex:
354 {
355 /*
356 * This is a bypass for any option - preformatted value as
357 * hex string with no semantic involved in formatting the
358 * value for the DHCP reply.
359 *
360 * 234=68:65:6c:6c:6f:2c:20:77:6f:72:6c:64
361 */
362 aEncoded = Utf8StrFmt("%d=%s", aOptCode, aOptValue.text.c_str());
363 break;
364 }
365
366 default:
367 {
368 /*
369 * Try to be forward compatible.
370 *
371 * "254@42=i hope you know what this means"
372 */
373 aEncoded = Utf8StrFmt("%d@%d=%s", aOptCode, (int)aOptValue.encoding,
374 aOptValue.text.c_str());
375 break;
376 }
377 }
378
379 return S_OK;
380}
381
382
383int DHCPServer::addOption(settings::DhcpOptionMap &aMap,
384 DhcpOpt_T aOption, const com::Utf8Str &aValue)
385{
386 settings::DhcpOptValue OptValue;
387
388 if (aOption != 0)
389 {
390 OptValue = settings::DhcpOptValue(aValue, DhcpOptEncoding_Legacy);
391 }
392 /*
393 * This is a kludge to sneak in option encoding information
394 * through existing API. We use option 0 and supply the real
395 * option/value in the same format that encodeOption() above
396 * produces for getter methods.
397 */
398 else
399 {
400 uint8_t u8Code;
401 char *pszNext;
402 int vrc = RTStrToUInt8Ex(aValue.c_str(), &pszNext, 10, &u8Code);
403 if (!RT_SUCCESS(vrc))
404 return VERR_PARSE_ERROR;
405
406 uint32_t u32Enc;
407 switch (*pszNext)
408 {
409 case ':': /* support legacy format too */
410 {
411 u32Enc = DhcpOptEncoding_Legacy;
412 break;
413 }
414
415 case '=':
416 {
417 u32Enc = DhcpOptEncoding_Hex;
418 break;
419 }
420
421 case '@':
422 {
423 vrc = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &u32Enc);
424 if (!RT_SUCCESS(vrc))
425 return VERR_PARSE_ERROR;
426 if (*pszNext != '=')
427 return VERR_PARSE_ERROR;
428 break;
429 }
430
431 default:
432 return VERR_PARSE_ERROR;
433 }
434
435 aOption = (DhcpOpt_T)u8Code;
436 OptValue = settings::DhcpOptValue(pszNext + 1, (DhcpOptEncoding_T)u32Enc);
437 }
438
439 aMap[aOption] = OptValue;
440 return VINF_SUCCESS;
441}
442
443
444HRESULT DHCPServer::addGlobalOption(DhcpOpt_T aOption, const com::Utf8Str &aValue)
445{
446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
447
448 int rc = addOption(m->GlobalDhcpOptions, aOption, aValue);
449 if (!RT_SUCCESS(rc))
450 return E_INVALIDARG;
451
452 /* Indirect way to understand that we're on NAT network */
453 if (aOption == DhcpOpt_Router)
454 {
455 m->dhcp.setOption(NetworkServiceRunner::kNsrKeyNeedMain, "on");
456 m->router = true;
457 }
458
459 alock.release();
460
461 AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
462 return mVirtualBox->i_saveSettings();
463}
464
465
466HRESULT DHCPServer::getGlobalOptions(std::vector<com::Utf8Str> &aValues)
467{
468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
469 aValues.resize(m->GlobalDhcpOptions.size());
470 settings::DhcpOptionMap::const_iterator it;
471 size_t i = 0;
472 for (it = m->GlobalDhcpOptions.begin(); it != m->GlobalDhcpOptions.end(); ++it, ++i)
473 {
474 uint32_t OptCode = (*it).first;
475 const settings::DhcpOptValue &OptValue = (*it).second;
476
477 encodeOption(aValues[i], OptCode, OptValue);
478 }
479
480 return S_OK;
481}
482
483HRESULT DHCPServer::getVmConfigs(std::vector<com::Utf8Str> &aValues)
484{
485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
486 aValues.resize(m->VmSlot2Options.size());
487 settings::VmSlot2OptionsMap::const_iterator it;
488 size_t i = 0;
489 for (it = m->VmSlot2Options.begin(); it != m->VmSlot2Options.end(); ++it, ++i)
490 {
491 aValues[i] = Utf8StrFmt("[%s]:%d", it->first.VmName.c_str(), it->first.Slot);
492 }
493
494 return S_OK;
495}
496
497
498HRESULT DHCPServer::addVmSlotOption(const com::Utf8Str &aVmName,
499 LONG aSlot,
500 DhcpOpt_T aOption,
501 const com::Utf8Str &aValue)
502{
503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
504
505 settings::DhcpOptionMap &map = m->VmSlot2Options[settings::VmNameSlotKey(aVmName, aSlot)];
506 int rc = addOption(map, aOption, aValue);
507 if (!RT_SUCCESS(rc))
508 return E_INVALIDARG;
509
510 alock.release();
511
512 AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
513 return mVirtualBox->i_saveSettings();
514}
515
516
517HRESULT DHCPServer::removeVmSlotOptions(const com::Utf8Str &aVmName, LONG aSlot)
518{
519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
520 settings::DhcpOptionMap &map = i_findOptMapByVmNameSlot(aVmName, aSlot);
521 map.clear();
522
523 alock.release();
524
525 AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
526 return mVirtualBox->i_saveSettings();
527}
528
529/**
530 * this is mapping (vm, slot)
531 */
532HRESULT DHCPServer::getVmSlotOptions(const com::Utf8Str &aVmName,
533 LONG aSlot,
534 std::vector<com::Utf8Str> &aValues)
535{
536
537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
538 settings::DhcpOptionMap &map = i_findOptMapByVmNameSlot(aVmName, aSlot);
539 aValues.resize(map.size());
540 size_t i = 0;
541 settings::DhcpOptionMap::const_iterator it;
542 for (it = map.begin(); it != map.end(); ++it, ++i)
543 {
544 uint32_t OptCode = (*it).first;
545 const settings::DhcpOptValue &OptValue = (*it).second;
546
547 encodeOption(aValues[i], OptCode, OptValue);
548 }
549
550 return S_OK;
551}
552
553
554HRESULT DHCPServer::getMacOptions(const com::Utf8Str &aMAC, std::vector<com::Utf8Str> &aOption)
555{
556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
557 HRESULT hrc = S_OK;
558 ComPtr<IMachine> machine;
559 ComPtr<INetworkAdapter> nic;
560 settings::VmSlot2OptionsIterator it;
561 for(it = m->VmSlot2Options.begin(); it != m->VmSlot2Options.end(); ++it)
562 {
563 alock.release();
564 hrc = mVirtualBox->FindMachine(Bstr(it->first.VmName).raw(), machine.asOutParam());
565 alock.acquire();
566
567 if (FAILED(hrc))
568 continue;
569
570 alock.release();
571 hrc = machine->GetNetworkAdapter(it->first.Slot, nic.asOutParam());
572 alock.acquire();
573
574 if (FAILED(hrc))
575 continue;
576
577 com::Bstr mac;
578
579 alock.release();
580 hrc = nic->COMGETTER(MACAddress)(mac.asOutParam());
581 alock.acquire();
582
583 if (FAILED(hrc)) /* no MAC address ??? */
584 break;
585 if (!RTStrICmp(com::Utf8Str(mac).c_str(), aMAC.c_str()))
586 return getVmSlotOptions(it->first.VmName,
587 it->first.Slot,
588 aOption);
589 } /* end of for */
590
591 return hrc;
592}
593
594HRESULT DHCPServer::getEventSource(ComPtr<IEventSource> &aEventSource)
595{
596 NOREF(aEventSource);
597 ReturnComNotImplemented();
598}
599
600
601HRESULT DHCPServer::start(const com::Utf8Str &aNetworkName,
602 const com::Utf8Str &aTrunkName,
603 const com::Utf8Str &aTrunkType)
604{
605 /* Silently ignore attempts to run disabled servers. */
606 if (!m->enabled)
607 return S_OK;
608
609 /* Commmon Network Settings */
610 m->dhcp.setOption(NetworkServiceRunner::kNsrKeyNetwork, aNetworkName.c_str());
611
612 if (!aTrunkName.isEmpty())
613 m->dhcp.setOption(NetworkServiceRunner::kNsrTrunkName, aTrunkName.c_str());
614
615 m->dhcp.setOption(NetworkServiceRunner::kNsrKeyTrunkType, aTrunkType.c_str());
616
617 /* XXX: should this MAC default initialization moved to NetworkServiceRunner? */
618 char strMAC[32];
619 Guid guid;
620 guid.create();
621 RTStrPrintf (strMAC, sizeof(strMAC), "08:00:27:%02X:%02X:%02X",
622 guid.raw()->au8[0],
623 guid.raw()->au8[1],
624 guid.raw()->au8[2]);
625 m->dhcp.setOption(NetworkServiceRunner::kNsrMacAddress, strMAC);
626 m->dhcp.setOption(NetworkServiceRunner::kNsrIpAddress, Utf8Str(m->IPAddress).c_str());
627 m->dhcp.setOption(NetworkServiceRunner::kNsrIpNetmask, Utf8Str(m->GlobalDhcpOptions[DhcpOpt_SubnetMask].text).c_str());
628 m->dhcp.setOption(DHCPServerRunner::kDsrKeyLowerIp, Utf8Str(m->lowerIP).c_str());
629 m->dhcp.setOption(DHCPServerRunner::kDsrKeyUpperIp, Utf8Str(m->upperIP).c_str());
630
631 /* XXX: This parameters Dhcp Server will fetch via API */
632 return RT_FAILURE(m->dhcp.start(!m->router /* KillProcOnExit */)) ? E_FAIL : S_OK;
633 //m->dhcp.detachFromServer(); /* need to do this to avoid server shutdown on runner destruction */
634}
635
636
637HRESULT DHCPServer::stop (void)
638{
639 return RT_FAILURE(m->dhcp.stop()) ? E_FAIL : S_OK;
640}
641
642
643settings::DhcpOptionMap &DHCPServer::i_findOptMapByVmNameSlot(const com::Utf8Str &aVmName,
644 LONG aSlot)
645{
646 return m->VmSlot2Options[settings::VmNameSlotKey(aVmName, aSlot)];
647}
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