VirtualBox

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

Last change on this file since 54971 was 54705, checked in by vboxsync, 10 years ago

Main: VBoxNetNAT/VBoxNetDHCP: sometimes it's required to kill the runner process (in case of no Main clients)

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