VirtualBox

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

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

Main/IDHCPServer: Added a FindLeaseByMAC method that can be used to query the lease database and help the ValKit finding the TXS running inside a VM it just started using natnet or host-only. This uses a reserved method as it will be backported to 6.0 in a bit. bugref:9288 bugref:9151

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.5 KB
Line 
1/* $Id: DHCPServerImpl.cpp 79610 2019-07-08 23:29:30Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_DHCPSERVER
19#include "NetworkServiceRunner.h"
20#include "DHCPServerImpl.h"
21#include "AutoCaller.h"
22#include "LoggingNew.h"
23
24#include <iprt/asm.h>
25#include <iprt/file.h>
26#include <iprt/net.h>
27#include <iprt/path.h>
28#include <iprt/cpp/path.h>
29#include <iprt/cpp/utils.h>
30#include <iprt/cpp/xml.h>
31
32#include <VBox/com/array.h>
33#include <VBox/settings.h>
34
35#include "VirtualBoxImpl.h"
36
37// constructor / destructor
38/////////////////////////////////////////////////////////////////////////////
39/** @todo Convert to C strings as this is wastefull: */
40const std::string DHCPServerRunner::kDsrKeyGateway = "--gateway";
41const std::string DHCPServerRunner::kDsrKeyLowerIp = "--lower-ip";
42const std::string DHCPServerRunner::kDsrKeyUpperIp = "--upper-ip";
43const std::string DHCPServerRunner::kDsrKeyConfig = "--config";
44const std::string DHCPServerRunner::kDsrKeyComment = "--comment";
45
46
47struct DHCPServer::Data
48{
49 Data()
50 : enabled(FALSE)
51 , router(false)
52 {
53 tempConfigFileName[0] = '\0';
54 }
55
56 Utf8Str IPAddress;
57 Utf8Str lowerIP;
58 Utf8Str upperIP;
59
60 BOOL enabled;
61 bool router;
62 DHCPServerRunner dhcp;
63
64 settings::DhcpOptionMap GlobalDhcpOptions;
65 settings::VmSlot2OptionsMap VmSlot2Options;
66
67 char tempConfigFileName[RTPATH_MAX];
68 com::Utf8Str strLeaseFilename;
69 com::Utf8Str networkName;
70 com::Utf8Str trunkName;
71 com::Utf8Str trunkType;
72};
73
74
75DHCPServer::DHCPServer()
76 : m(NULL)
77 , mVirtualBox(NULL)
78{
79 m = new DHCPServer::Data();
80}
81
82
83DHCPServer::~DHCPServer()
84{
85 if (m)
86 {
87 delete m;
88 m = NULL;
89 }
90}
91
92
93HRESULT DHCPServer::FinalConstruct()
94{
95 return BaseFinalConstruct();
96}
97
98
99void DHCPServer::FinalRelease()
100{
101 uninit ();
102
103 BaseFinalRelease();
104}
105
106
107void DHCPServer::uninit()
108{
109 /* Enclose the state transition Ready->InUninit->NotReady */
110 AutoUninitSpan autoUninitSpan(this);
111 if (autoUninitSpan.uninitDone())
112 return;
113
114 if (m->dhcp.isRunning())
115 stop();
116
117 unconst(mVirtualBox) = NULL;
118}
119
120
121HRESULT DHCPServer::init(VirtualBox *aVirtualBox, const Utf8Str &aName)
122{
123 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
124
125 AutoInitSpan autoInitSpan(this);
126 AssertReturn(autoInitSpan.isOk(), E_FAIL);
127
128 /* share VirtualBox weakly (parent remains NULL so far) */
129 unconst(mVirtualBox) = aVirtualBox;
130
131 unconst(mName) = aName;
132 m->IPAddress = "0.0.0.0";
133 m->GlobalDhcpOptions[DhcpOpt_SubnetMask] = settings::DhcpOptValue("0.0.0.0");
134 m->enabled = FALSE;
135
136 m->lowerIP = "0.0.0.0";
137 m->upperIP = "0.0.0.0";
138
139 /* Confirm a successful initialization */
140 autoInitSpan.setSucceeded();
141
142 return S_OK;
143}
144
145
146HRESULT DHCPServer::init(VirtualBox *aVirtualBox,
147 const settings::DHCPServer &data)
148{
149 /* Enclose the state transition NotReady->InInit->Ready */
150 AutoInitSpan autoInitSpan(this);
151 AssertReturn(autoInitSpan.isOk(), E_FAIL);
152
153 /* share VirtualBox weakly (parent remains NULL so far) */
154 unconst(mVirtualBox) = aVirtualBox;
155
156 unconst(mName) = data.strNetworkName;
157 m->IPAddress = data.strIPAddress;
158 m->enabled = data.fEnabled;
159 m->lowerIP = data.strIPLower;
160 m->upperIP = data.strIPUpper;
161
162 m->GlobalDhcpOptions.clear();
163 m->GlobalDhcpOptions.insert(data.GlobalDhcpOptions.begin(),
164 data.GlobalDhcpOptions.end());
165
166 m->VmSlot2Options.clear();
167 m->VmSlot2Options.insert(data.VmSlot2OptionsM.begin(),
168 data.VmSlot2OptionsM.end());
169
170 autoInitSpan.setSucceeded();
171
172 return S_OK;
173}
174
175
176HRESULT DHCPServer::i_saveSettings(settings::DHCPServer &data)
177{
178 AutoCaller autoCaller(this);
179 if (FAILED(autoCaller.rc())) return autoCaller.rc();
180
181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
182
183 data.strNetworkName = mName;
184 data.strIPAddress = m->IPAddress;
185
186 data.fEnabled = !!m->enabled;
187 data.strIPLower = m->lowerIP;
188 data.strIPUpper = m->upperIP;
189
190 data.GlobalDhcpOptions.clear();
191 data.GlobalDhcpOptions.insert(m->GlobalDhcpOptions.begin(),
192 m->GlobalDhcpOptions.end());
193
194 data.VmSlot2OptionsM.clear();
195 data.VmSlot2OptionsM.insert(m->VmSlot2Options.begin(),
196 m->VmSlot2Options.end());
197
198 return S_OK;
199}
200
201
202HRESULT DHCPServer::getNetworkName(com::Utf8Str &aName)
203{
204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
205
206 aName = mName;
207 return S_OK;
208}
209
210
211HRESULT DHCPServer::getEnabled(BOOL *aEnabled)
212{
213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
214
215 *aEnabled = m->enabled;
216 return S_OK;
217}
218
219
220HRESULT DHCPServer::setEnabled(BOOL aEnabled)
221{
222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
223 m->enabled = aEnabled;
224
225 // save the global settings; for that we should hold only the VirtualBox lock
226 alock.release();
227 AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
228 HRESULT rc = mVirtualBox->i_saveSettings();
229
230 return rc;
231}
232
233
234HRESULT DHCPServer::getIPAddress(com::Utf8Str &aIPAddress)
235{
236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
237
238 aIPAddress = Utf8Str(m->IPAddress);
239 return S_OK;
240}
241
242
243HRESULT DHCPServer::getNetworkMask(com::Utf8Str &aNetworkMask)
244{
245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
246
247 aNetworkMask = m->GlobalDhcpOptions[DhcpOpt_SubnetMask].text;
248 return S_OK;
249}
250
251
252HRESULT DHCPServer::getLowerIP(com::Utf8Str &aIPAddress)
253{
254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
255
256 aIPAddress = Utf8Str(m->lowerIP);
257 return S_OK;
258}
259
260
261HRESULT DHCPServer::getUpperIP(com::Utf8Str &aIPAddress)
262{
263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
264
265 aIPAddress = Utf8Str(m->upperIP);
266 return S_OK;
267}
268
269
270HRESULT DHCPServer::setConfiguration(const com::Utf8Str &aIPAddress,
271 const com::Utf8Str &aNetworkMask,
272 const com::Utf8Str &aLowerIP,
273 const com::Utf8Str &aUpperIP)
274{
275 RTNETADDRIPV4 IPAddress, NetworkMask, LowerIP, UpperIP;
276
277 int vrc = RTNetStrToIPv4Addr(aIPAddress.c_str(), &IPAddress);
278 if (RT_FAILURE(vrc))
279 return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid server address");
280
281 vrc = RTNetStrToIPv4Addr(aNetworkMask.c_str(), &NetworkMask);
282 if (RT_FAILURE(vrc))
283 return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid netmask");
284
285 vrc = RTNetStrToIPv4Addr(aLowerIP.c_str(), &LowerIP);
286 if (RT_FAILURE(vrc))
287 return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid range lower address");
288
289 vrc = RTNetStrToIPv4Addr(aUpperIP.c_str(), &UpperIP);
290 if (RT_FAILURE(vrc))
291 return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid range upper address");
292
293 /*
294 * Insist on continuous mask. May be also accept prefix length
295 * here or address/prefix for aIPAddress?
296 */
297 vrc = RTNetMaskToPrefixIPv4(&NetworkMask, NULL);
298 if (RT_FAILURE(vrc))
299 return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid netmask");
300
301 /* It's more convenient to convert to host order once */
302 IPAddress.u = RT_N2H_U32(IPAddress.u);
303 NetworkMask.u = RT_N2H_U32(NetworkMask.u);
304 LowerIP.u = RT_N2H_U32(LowerIP.u);
305 UpperIP.u = RT_N2H_U32(UpperIP.u);
306
307 /*
308 * Addresses must be unicast and from the same network
309 */
310 if ( (IPAddress.u & UINT32_C(0xe0000000)) == UINT32_C(0xe0000000)
311 || (IPAddress.u & ~NetworkMask.u) == 0
312 || ((IPAddress.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
313 return mVirtualBox->setError(E_INVALIDARG, "Invalid server address");
314
315 if ( (LowerIP.u & UINT32_C(0xe0000000)) == UINT32_C(0xe0000000)
316 || (LowerIP.u & NetworkMask.u) != (IPAddress.u &NetworkMask.u)
317 || (LowerIP.u & ~NetworkMask.u) == 0
318 || ((LowerIP.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
319 return mVirtualBox->setError(E_INVALIDARG, "Invalid range lower address");
320
321 if ( (UpperIP.u & UINT32_C(0xe0000000)) == UINT32_C(0xe0000000)
322 || (UpperIP.u & NetworkMask.u) != (IPAddress.u &NetworkMask.u)
323 || (UpperIP.u & ~NetworkMask.u) == 0
324 || ((UpperIP.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
325 return mVirtualBox->setError(E_INVALIDARG, "Invalid range upper address");
326
327 /* The range should be valid ... */
328 if (LowerIP.u > UpperIP.u)
329 return mVirtualBox->setError(E_INVALIDARG, "Invalid range bounds");
330
331 /* ... and shouldn't contain the server's address */
332 if (LowerIP.u <= IPAddress.u && IPAddress.u <= UpperIP.u)
333 return mVirtualBox->setError(E_INVALIDARG, "Server address within range bounds");
334
335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
336 m->IPAddress = aIPAddress;
337 m->GlobalDhcpOptions[DhcpOpt_SubnetMask] = aNetworkMask;
338
339 m->lowerIP = aLowerIP;
340 m->upperIP = aUpperIP;
341
342 // save the global settings; for that we should hold only the VirtualBox lock
343 alock.release();
344 AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
345 return mVirtualBox->i_saveSettings();
346}
347
348
349HRESULT DHCPServer::encodeOption(com::Utf8Str &aEncoded,
350 uint32_t aOptCode,
351 const settings::DhcpOptValue &aOptValue)
352{
353 switch (aOptValue.encoding)
354 {
355 case DhcpOptEncoding_Legacy:
356 {
357 /*
358 * This is original encoding which assumed that for each
359 * option we know its format and so we know how option
360 * "value" text is to be interpreted.
361 *
362 * "2:10800" # integer 32
363 * "6:1.2.3.4 8.8.8.8" # array of ip-address
364 */
365 aEncoded = Utf8StrFmt("%d:%s", aOptCode, aOptValue.text.c_str());
366 break;
367 }
368
369 case DhcpOptEncoding_Hex:
370 {
371 /*
372 * This is a bypass for any option - preformatted value as
373 * hex string with no semantic involved in formatting the
374 * value for the DHCP reply.
375 *
376 * 234=68:65:6c:6c:6f:2c:20:77:6f:72:6c:64
377 */
378 aEncoded = Utf8StrFmt("%d=%s", aOptCode, aOptValue.text.c_str());
379 break;
380 }
381
382 default:
383 {
384 /*
385 * Try to be forward compatible.
386 *
387 * "254@42=i hope you know what this means"
388 */
389 aEncoded = Utf8StrFmt("%d@%d=%s", aOptCode, (int)aOptValue.encoding,
390 aOptValue.text.c_str());
391 break;
392 }
393 }
394
395 return S_OK;
396}
397
398
399int DHCPServer::addOption(settings::DhcpOptionMap &aMap,
400 DhcpOpt_T aOption, const com::Utf8Str &aValue)
401{
402 settings::DhcpOptValue OptValue;
403
404 if (aOption != 0)
405 {
406 OptValue = settings::DhcpOptValue(aValue, DhcpOptEncoding_Legacy);
407 }
408 /*
409 * This is a kludge to sneak in option encoding information
410 * through existing API. We use option 0 and supply the real
411 * option/value in the same format that encodeOption() above
412 * produces for getter methods.
413 */
414 else
415 {
416 uint8_t u8Code;
417 char *pszNext;
418 int vrc = RTStrToUInt8Ex(aValue.c_str(), &pszNext, 10, &u8Code);
419 if (!RT_SUCCESS(vrc))
420 return VERR_PARSE_ERROR;
421
422 uint32_t u32Enc;
423 switch (*pszNext)
424 {
425 case ':': /* support legacy format too */
426 {
427 u32Enc = DhcpOptEncoding_Legacy;
428 break;
429 }
430
431 case '=':
432 {
433 u32Enc = DhcpOptEncoding_Hex;
434 break;
435 }
436
437 case '@':
438 {
439 vrc = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &u32Enc);
440 if (!RT_SUCCESS(vrc))
441 return VERR_PARSE_ERROR;
442 if (*pszNext != '=')
443 return VERR_PARSE_ERROR;
444 break;
445 }
446
447 default:
448 return VERR_PARSE_ERROR;
449 }
450
451 aOption = (DhcpOpt_T)u8Code;
452 OptValue = settings::DhcpOptValue(pszNext + 1, (DhcpOptEncoding_T)u32Enc);
453 }
454
455 aMap[aOption] = OptValue;
456 return VINF_SUCCESS;
457}
458
459
460HRESULT DHCPServer::addGlobalOption(DhcpOpt_T aOption, const com::Utf8Str &aValue)
461{
462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
463
464 int rc = addOption(m->GlobalDhcpOptions, aOption, aValue);
465 if (!RT_SUCCESS(rc))
466 return E_INVALIDARG;
467
468 /* Indirect way to understand that we're on NAT network */
469 if (aOption == DhcpOpt_Router)
470 {
471 m->router = true;
472 }
473
474 alock.release();
475
476 AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
477 return mVirtualBox->i_saveSettings();
478}
479
480
481HRESULT DHCPServer::removeGlobalOption(DhcpOpt_T aOption)
482{
483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
484
485 settings::DhcpOptionMap::size_type cErased = m->GlobalDhcpOptions.erase(aOption);
486 if (!cErased)
487 return E_INVALIDARG;
488
489 alock.release();
490
491 AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
492 return mVirtualBox->i_saveSettings();
493}
494
495
496HRESULT DHCPServer::removeGlobalOptions()
497{
498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
499 m->GlobalDhcpOptions.clear();
500
501 alock.release();
502
503 AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
504 return mVirtualBox->i_saveSettings();
505}
506
507
508HRESULT DHCPServer::getGlobalOptions(std::vector<com::Utf8Str> &aValues)
509{
510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
511 aValues.resize(m->GlobalDhcpOptions.size());
512 settings::DhcpOptionMap::const_iterator it;
513 size_t i = 0;
514 for (it = m->GlobalDhcpOptions.begin(); it != m->GlobalDhcpOptions.end(); ++it, ++i)
515 {
516 uint32_t OptCode = (*it).first;
517 const settings::DhcpOptValue &OptValue = (*it).second;
518
519 encodeOption(aValues[i], OptCode, OptValue);
520 }
521
522 return S_OK;
523}
524
525HRESULT DHCPServer::getVmConfigs(std::vector<com::Utf8Str> &aValues)
526{
527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
528 aValues.resize(m->VmSlot2Options.size());
529 settings::VmSlot2OptionsMap::const_iterator it;
530 size_t i = 0;
531 for (it = m->VmSlot2Options.begin(); it != m->VmSlot2Options.end(); ++it, ++i)
532 {
533 aValues[i] = Utf8StrFmt("[%s]:%d", it->first.VmName.c_str(), it->first.Slot);
534 }
535
536 return S_OK;
537}
538
539
540HRESULT DHCPServer::addVmSlotOption(const com::Utf8Str &aVmName,
541 LONG aSlot,
542 DhcpOpt_T aOption,
543 const com::Utf8Str &aValue)
544{
545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
546
547 settings::DhcpOptionMap &map = m->VmSlot2Options[settings::VmNameSlotKey(aVmName, aSlot)];
548 int rc = addOption(map, aOption, aValue);
549 if (!RT_SUCCESS(rc))
550 return E_INVALIDARG;
551
552 alock.release();
553
554 AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
555 return mVirtualBox->i_saveSettings();
556}
557
558
559HRESULT DHCPServer::removeVmSlotOption(const com::Utf8Str &aVmName, LONG aSlot, DhcpOpt_T aOption)
560{
561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
562 settings::DhcpOptionMap &map = i_findOptMapByVmNameSlot(aVmName, aSlot);
563 settings::DhcpOptionMap::size_type cErased = map.erase(aOption);
564 if (!cErased)
565 return E_INVALIDARG;
566
567 alock.release();
568
569 AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
570 return mVirtualBox->i_saveSettings();
571}
572
573
574HRESULT DHCPServer::removeVmSlotOptions(const com::Utf8Str &aVmName, LONG aSlot)
575{
576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
577 settings::DhcpOptionMap &map = i_findOptMapByVmNameSlot(aVmName, aSlot);
578 map.clear();
579
580 alock.release();
581
582 AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
583 return mVirtualBox->i_saveSettings();
584}
585
586/**
587 * this is mapping (vm, slot)
588 */
589HRESULT DHCPServer::getVmSlotOptions(const com::Utf8Str &aVmName,
590 LONG aSlot,
591 std::vector<com::Utf8Str> &aValues)
592{
593
594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
595 settings::DhcpOptionMap &map = i_findOptMapByVmNameSlot(aVmName, aSlot);
596 aValues.resize(map.size());
597 size_t i = 0;
598 settings::DhcpOptionMap::const_iterator it;
599 for (it = map.begin(); it != map.end(); ++it, ++i)
600 {
601 uint32_t OptCode = (*it).first;
602 const settings::DhcpOptValue &OptValue = (*it).second;
603
604 encodeOption(aValues[i], OptCode, OptValue);
605 }
606
607 return S_OK;
608}
609
610
611HRESULT DHCPServer::getMacOptions(const com::Utf8Str &aMAC, std::vector<com::Utf8Str> &aOption)
612{
613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
614 HRESULT hrc = S_OK;
615 ComPtr<IMachine> machine;
616 ComPtr<INetworkAdapter> nic;
617 settings::VmSlot2OptionsIterator it;
618 for(it = m->VmSlot2Options.begin(); it != m->VmSlot2Options.end(); ++it)
619 {
620 alock.release();
621 hrc = mVirtualBox->FindMachine(Bstr(it->first.VmName).raw(), machine.asOutParam());
622 alock.acquire();
623
624 if (FAILED(hrc))
625 continue;
626
627 alock.release();
628 hrc = machine->GetNetworkAdapter(it->first.Slot, nic.asOutParam());
629 alock.acquire();
630
631 if (FAILED(hrc))
632 continue;
633
634 com::Bstr mac;
635
636 alock.release();
637 hrc = nic->COMGETTER(MACAddress)(mac.asOutParam());
638 alock.acquire();
639
640 if (FAILED(hrc)) /* no MAC address ??? */
641 break;
642 if (!RTStrICmp(com::Utf8Str(mac).c_str(), aMAC.c_str()))
643 return getVmSlotOptions(it->first.VmName,
644 it->first.Slot,
645 aOption);
646 } /* end of for */
647
648 return hrc;
649}
650
651HRESULT DHCPServer::getEventSource(ComPtr<IEventSource> &aEventSource)
652{
653 NOREF(aEventSource);
654 ReturnComNotImplemented();
655}
656
657
658DECLINLINE(void) addOptionChild(xml::ElementNode *pParent, uint32_t OptCode, const settings::DhcpOptValue &OptValue)
659{
660 xml::ElementNode *pOption = pParent->createChild("Option");
661 pOption->setAttribute("name", OptCode);
662 pOption->setAttribute("encoding", OptValue.encoding);
663 pOption->setAttribute("value", OptValue.text.c_str());
664}
665
666
667HRESULT DHCPServer::restart()
668{
669 if (!m->dhcp.isRunning())
670 return setErrorBoth(E_FAIL, VERR_PROCESS_NOT_FOUND, tr("not running"));
671
672 /*
673 * Disabled servers will be brought down, but won't be restarted.
674 * (see DHCPServer::start)
675 */
676 HRESULT hrc = stop();
677 if (SUCCEEDED(hrc))
678 hrc = start(m->networkName, m->trunkName, m->trunkType);
679 return hrc;
680}
681
682
683HRESULT DHCPServer::start(const com::Utf8Str &aNetworkName,
684 const com::Utf8Str &aTrunkName,
685 const com::Utf8Str &aTrunkType)
686{
687 /* Silently ignore attempts to run disabled servers. */
688 if (!m->enabled)
689 return S_OK;
690
691 /**
692 * @todo: the existing code cannot handle concurrent attempts to start DHCP server.
693 * Note that technically it may receive different parameters from different callers.
694 */
695 m->networkName = aNetworkName;
696 m->trunkName = aTrunkName;
697 m->trunkType = aTrunkType;
698 HRESULT hrc = i_calcLeaseFilename(aNetworkName);
699 if (FAILED(hrc))
700 return hrc;
701
702 m->dhcp.clearOptions(); /* (Not DHCP options, but command line options for the service) */
703
704#ifdef VBOX_WITH_DHCPD
705
706 /*
707 * Create configuration file path.
708 */
709 /** @todo put this next to the leases file. */
710 int rc = RTPathTemp(m->tempConfigFileName, sizeof(m->tempConfigFileName));
711 if (RT_SUCCESS(rc))
712 rc = RTPathAppend(m->tempConfigFileName, sizeof(m->tempConfigFileName), "dhcp-config-XXXXX.xml");
713 if (RT_SUCCESS(rc))
714 rc = RTFileCreateTemp(m->tempConfigFileName, 0600);
715 if (RT_FAILURE(rc))
716 {
717 m->tempConfigFileName[0] = '\0';
718 return E_FAIL;
719 }
720
721 /*
722 * Produce the DHCP server configuration.
723 */
724 xml::Document doc;
725 xml::ElementNode *pElmRoot = doc.createRootElement("DHCPServer");
726 pElmRoot->setAttribute("networkName", m->networkName);
727 if (m->trunkName.isNotEmpty())
728 pElmRoot->setAttribute("trunkName", m->trunkName);
729 pElmRoot->setAttribute("trunkType", m->trunkType);
730 pElmRoot->setAttribute("IPAddress", m->IPAddress);
731 pElmRoot->setAttribute("networkMask", m->GlobalDhcpOptions[DhcpOpt_SubnetMask].text);
732 pElmRoot->setAttribute("lowerIP", m->lowerIP);
733 pElmRoot->setAttribute("upperIP", m->upperIP);
734 pElmRoot->setAttribute("leaseFilename", m->strLeaseFilename);
735
736 /* Process global options */
737 xml::ElementNode *pOptions = pElmRoot->createChild("Options");
738 for (settings::DhcpOptionMap::const_iterator it = m->GlobalDhcpOptions.begin(); it != m->GlobalDhcpOptions.end(); ++it)
739 addOptionChild(pOptions, (*it).first, (*it).second);
740
741 /* Process network-adapter-specific options */
742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
743 hrc = S_OK;
744 ComPtr<IMachine> machine;
745 ComPtr<INetworkAdapter> nic;
746 settings::VmSlot2OptionsIterator it;
747 for (it = m->VmSlot2Options.begin(); it != m->VmSlot2Options.end(); ++it)
748 {
749 alock.release();
750 hrc = mVirtualBox->FindMachine(Bstr(it->first.VmName).raw(), machine.asOutParam());
751 alock.acquire();
752
753 if (FAILED(hrc))
754 continue;
755
756 alock.release();
757 hrc = machine->GetNetworkAdapter(it->first.Slot, nic.asOutParam());
758 alock.acquire();
759
760 if (FAILED(hrc))
761 continue;
762
763 com::Bstr mac;
764
765 alock.release();
766 hrc = nic->COMGETTER(MACAddress)(mac.asOutParam());
767 alock.acquire();
768
769 if (FAILED(hrc)) /* no MAC address ??? */
770 continue;
771
772 /* Convert MAC address from XXXXXXXXXXXX to XX:XX:XX:XX:XX:XX */
773 Utf8Str strMacWithoutColons(mac);
774 const char *pszSrc = strMacWithoutColons.c_str();
775 RTMAC binaryMac;
776 if (RTStrConvertHexBytes(pszSrc, &binaryMac, sizeof(binaryMac), 0) != VINF_SUCCESS)
777 continue;
778 char szMac[18]; /* "XX:XX:XX:XX:XX:XX" */
779 if (RTStrPrintHexBytes(szMac, sizeof(szMac), &binaryMac, sizeof(binaryMac), RTSTRPRINTHEXBYTES_F_SEP_COLON) != VINF_SUCCESS)
780 continue;
781
782 xml::ElementNode *pMacConfig = pElmRoot->createChild("Config");
783 pMacConfig->setAttribute("MACAddress", szMac);
784
785 com::Utf8Str encodedOption;
786 settings::DhcpOptionMap &map = i_findOptMapByVmNameSlot(it->first.VmName, it->first.Slot);
787 settings::DhcpOptionMap::const_iterator itAdapterOption;
788 for (itAdapterOption = map.begin(); itAdapterOption != map.end(); ++itAdapterOption)
789 addOptionChild(pMacConfig, (*itAdapterOption).first, (*itAdapterOption).second);
790 }
791
792 xml::XmlFileWriter writer(doc);
793 writer.write(m->tempConfigFileName, false);
794
795 m->dhcp.setOption(DHCPServerRunner::kDsrKeyConfig, m->tempConfigFileName); /* command line options, not dhcp ones. */
796 m->dhcp.setOption(DHCPServerRunner::kDsrKeyComment, m->networkName.c_str());
797
798#else /* !VBOX_WITH_DHCPD */
799 /* Main is needed for NATNetwork */
800 if (m->router)
801 m->dhcp.setOption(NetworkServiceRunner::kNsrKeyNeedMain, "on");
802
803 /* Commmon Network Settings */
804 m->dhcp.setOption(NetworkServiceRunner::kNsrKeyNetwork, aNetworkName.c_str());
805
806 if (!aTrunkName.isEmpty())
807 m->dhcp.setOption(NetworkServiceRunner::kNsrTrunkName, aTrunkName.c_str());
808
809 m->dhcp.setOption(NetworkServiceRunner::kNsrKeyTrunkType, aTrunkType.c_str());
810
811 /* XXX: should this MAC default initialization moved to NetworkServiceRunner? */
812 char strMAC[32];
813 Guid guid;
814 guid.create();
815 RTStrPrintf (strMAC, sizeof(strMAC), "08:00:27:%02X:%02X:%02X",
816 guid.raw()->au8[0],
817 guid.raw()->au8[1],
818 guid.raw()->au8[2]);
819 m->dhcp.setOption(NetworkServiceRunner::kNsrMacAddress, strMAC);
820 m->dhcp.setOption(NetworkServiceRunner::kNsrIpAddress, Utf8Str(m->IPAddress).c_str());
821 m->dhcp.setOption(NetworkServiceRunner::kNsrIpNetmask, Utf8Str(m->GlobalDhcpOptions[DhcpOpt_SubnetMask].text).c_str());
822 m->dhcp.setOption(DHCPServerRunner::kDsrKeyLowerIp, Utf8Str(m->lowerIP).c_str());
823 m->dhcp.setOption(DHCPServerRunner::kDsrKeyUpperIp, Utf8Str(m->upperIP).c_str());
824#endif /* !VBOX_WITH_DHCPD */
825
826 /* XXX: This parameters Dhcp Server will fetch via API */
827 return RT_FAILURE(m->dhcp.start(!m->router /* KillProcOnExit */)) ? E_FAIL : S_OK;
828 //m->dhcp.detachFromServer(); /* need to do this to avoid server shutdown on runner destruction */
829}
830
831
832HRESULT DHCPServer::stop (void)
833{
834#ifdef VBOX_WITH_DHCPD
835 if (m->tempConfigFileName[0])
836 {
837 RTFileDelete(m->tempConfigFileName);
838 m->tempConfigFileName[0] = 0;
839 }
840#endif /* VBOX_WITH_DHCPD */
841 return RT_FAILURE(m->dhcp.stop()) ? E_FAIL : S_OK;
842}
843
844
845HRESULT DHCPServer::findLeaseByMAC(const com::Utf8Str &aMac, LONG aType,
846 com::Utf8Str &aAddress, com::Utf8Str &aState, LONG64 *aIssued, LONG64 *aExpire)
847{
848 /* Reset output before we start */
849 *aIssued = 0;
850 *aExpire = 0;
851 aAddress.setNull();
852 aState.setNull();
853
854 /*
855 * Convert and check input.
856 */
857 RTMAC MacAddress;
858 int vrc = RTStrConvertHexBytes(aMac.c_str(), &MacAddress, sizeof(MacAddress), RTSTRCONVERTHEXBYTES_F_SEP_COLON);
859 if (vrc != VINF_SUCCESS)
860 return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid MAC address '%s': %Rrc"), aMac.c_str(), vrc);
861 if (aType != 0)
862 return setError(E_INVALIDARG, tr("flags must be zero (not %#x)"), aType);
863
864 /*
865 * Make sure we've got a lease filename to work with.
866 */
867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
868 if (m->strLeaseFilename.isEmpty())
869 {
870 HRESULT hrc = i_calcLeaseFilename(m->networkName.isEmpty() ? mName : m->networkName);
871 if (FAILED(hrc))
872 return hrc;
873 }
874
875 /*
876 * Try at least twice to read the lease database, more if busy.
877 */
878 uint64_t const nsStart = RTTimeNanoTS();
879 for (uint32_t uReadAttempt = 0; ; uReadAttempt++)
880 {
881 /*
882 * Try read the file.
883 */
884 xml::Document doc;
885 try
886 {
887 xml::XmlFileParser parser;
888 parser.read(m->strLeaseFilename.c_str(), doc);
889 }
890 catch (const xml::EIPRTFailure &e)
891 {
892 vrc = e.rc();
893 LogThisFunc(("caught xml::EIPRTFailure: rc=%Rrc (attempt %u, msg=%s)\n", vrc, uReadAttempt, e.what()));
894 if ( ( vrc == VERR_FILE_NOT_FOUND
895 || vrc == VERR_OPEN_FAILED
896 || vrc == VERR_ACCESS_DENIED
897 || vrc == VERR_SHARING_VIOLATION
898 || vrc == VERR_READ_ERROR /*?*/)
899 && ( uReadAttempt == 0
900 || ( uReadAttempt < 64
901 && RTTimeNanoTS() - nsStart < RT_NS_1SEC / 4)) )
902 {
903 alock.release();
904
905 if (uReadAttempt > 0)
906 RTThreadYield();
907 RTThreadSleep(8/*ms*/);
908
909 alock.acquire();
910 LogThisFunc(("Retrying...\n"));
911 continue;
912 }
913 return setErrorBoth(VBOX_E_FILE_ERROR, vrc, "Reading '%s' failed: %Rrc - %s",
914 m->strLeaseFilename.c_str(), vrc, e.what());
915 }
916 catch (const RTCError &e)
917 {
918 if (e.what())
919 return setError(VBOX_E_FILE_ERROR, "Reading '%s' failed: %s", m->strLeaseFilename.c_str(), e.what());
920 return setError(VBOX_E_FILE_ERROR, "Reading '%s' failed: RTCError", m->strLeaseFilename.c_str());
921 }
922 catch (std::bad_alloc &)
923 {
924 return E_OUTOFMEMORY;
925 }
926 catch (...)
927 {
928 AssertFailed();
929 return setError(VBOX_E_FILE_ERROR, tr("Reading '%s' failed: Unexpected exception"), m->strLeaseFilename.c_str());
930 }
931
932 /*
933 * Look for that mac address.
934 */
935 xml::ElementNode *pElmRoot = doc.getRootElement();
936 if (pElmRoot && pElmRoot->nameEquals("Leases"))
937 {
938 xml::NodesLoop it(*pElmRoot);
939 const xml::ElementNode *pElmLease;
940 while ((pElmLease = it.forAllNodes()) != NULL)
941 if (pElmLease->nameEquals("Lease"))
942 {
943 const char *pszCurMacAddress = pElmLease->findAttributeValue("mac");
944 RTMAC CurMacAddress;
945 if ( pszCurMacAddress
946 && RT_SUCCESS(RTNetStrToMacAddr(pszCurMacAddress, &CurMacAddress))
947 && memcmp(&CurMacAddress, &MacAddress, sizeof(MacAddress)) == 0)
948 {
949 /*
950 * Found it!
951 */
952 xml::ElementNode const *pElmTime = pElmLease->findChildElement("Time");
953 int64_t secIssued = 0;
954 uint32_t cSecsToLive = 0;
955 if (pElmTime)
956 {
957 pElmTime->getAttributeValue("issued", &secIssued);
958 pElmTime->getAttributeValue("expiration", &cSecsToLive);
959 *aIssued = secIssued;
960 *aExpire = secIssued + cSecsToLive;
961 }
962 try
963 {
964 aAddress = pElmLease->findChildElementAttributeValue("Address", "value");
965 aState = pElmLease->findAttributeValue("state");
966 }
967 catch (std::bad_alloc &)
968 {
969 return E_OUTOFMEMORY;
970 }
971
972 /* Check if the lease has expired in the mean time. */
973 HRESULT hrc = S_OK;
974 RTTIMESPEC Now;
975 if ( (aState.equals("acked") || aState.equals("offered") || aState.isEmpty())
976 && secIssued + cSecsToLive < RTTimeSpecGetSeconds(RTTimeNow(&Now)))
977 hrc = aState.assignNoThrow("expired");
978 return hrc;
979 }
980 }
981 }
982 break;
983 }
984
985 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("Could not find a lease for %RTmac"), &MacAddress);
986}
987
988
989/**
990 * Calculates and updates the value of strLeaseFilename given @a aNetwork.
991 */
992HRESULT DHCPServer::i_calcLeaseFilename(const com::Utf8Str &aNetwork)
993{
994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
995
996 /* The lease file must be the same as we used the last time, so careful when changing this code. */
997 int vrc = m->strLeaseFilename.assignNoThrow(mVirtualBox->i_homeDir());
998 if (RT_SUCCESS(vrc))
999 vrc = RTPathAppendCxx(m->strLeaseFilename, aNetwork);
1000 if (RT_SUCCESS(vrc))
1001 vrc = m->strLeaseFilename.appendNoThrow("-Dhcpd.leases");
1002 if (RT_SUCCESS(vrc))
1003 {
1004 RTPathPurgeFilename(RTPathFilename(m->strLeaseFilename.mutableRaw()), RTPATH_STR_F_STYLE_HOST);
1005 m->strLeaseFilename.jolt();
1006 return S_OK;
1007 }
1008 return setErrorBoth(E_FAIL, vrc, tr("Failed to construct lease filename: %Rrc"), vrc);
1009}
1010
1011settings::DhcpOptionMap &DHCPServer::i_findOptMapByVmNameSlot(const com::Utf8Str &aVmName,
1012 LONG aSlot)
1013{
1014 return m->VmSlot2Options[settings::VmNameSlotKey(aVmName, aSlot)];
1015}
1016
Note: See TracBrowser for help on using the repository browser.

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