VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/Dhcpd/Config.cpp@ 76690

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

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.4 KB
Line 
1/* $Id: Config.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * DHCP server - server configuration
4 */
5
6/*
7 * Copyright (C) 2017-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#include "Config.h"
19
20#include <iprt/types.h>
21#include <iprt/net.h> /* NB: must come before getopt.h */
22#include <iprt/getopt.h>
23#include <iprt/path.h>
24#include <iprt/message.h>
25#include <iprt/string.h>
26#include <iprt/uuid.h>
27
28#include <VBox/com/com.h>
29
30#include <iostream>
31
32class ConfigFileError
33 : public RTCError
34{
35public:
36 ConfigFileError(const char *pszMessage)
37 : RTCError(pszMessage) {}
38
39 ConfigFileError(const RTCString &a_rstrMessage)
40 : RTCError(a_rstrMessage) {}
41};
42
43
44Config::Config()
45 : m_strHome(),
46 m_strNetwork(),
47 m_strBaseName(),
48 m_strTrunk(),
49 m_enmTrunkType(kIntNetTrunkType_Invalid),
50 m_MacAddress(),
51 m_IPv4Address(),
52 m_IPv4Netmask(),
53 m_IPv4PoolFirst(),
54 m_IPv4PoolLast(),
55 m_GlobalOptions(),
56 m_VMMap()
57{
58 return;
59}
60
61
62int Config::init()
63{
64 int rc;
65
66 rc = homeInit();
67 if (RT_FAILURE(rc))
68 return rc;
69
70 return VINF_SUCCESS;
71}
72
73
74int Config::homeInit()
75{
76 int rc;
77
78 /* pathname of ~/.VirtualBox or equivalent */
79 char szHome[RTPATH_MAX];
80 rc = com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome), false);
81 if (RT_FAILURE(rc))
82 {
83 LogDHCP(("unable to find VirtualBox home directory: %Rrs", rc));
84 return rc;
85 }
86
87 m_strHome.assign(szHome);
88 return VINF_SUCCESS;
89}
90
91
92void Config::setNetwork(const std::string &aStrNetwork)
93{
94 AssertReturnVoid(m_strNetwork.empty());
95
96 m_strNetwork = aStrNetwork;
97 sanitizeBaseName();
98}
99
100
101/*
102 * Requires network name to be known as the log file name depends on
103 * it. Alternatively, consider passing the log file name via the
104 * command line?
105 */
106int Config::logInit()
107{
108 int rc;
109 size_t cch;
110
111 if (m_strHome.empty() || m_strBaseName.empty())
112 return VERR_GENERAL_FAILURE;
113
114 /* default log file name */
115 char szLogFile[RTPATH_MAX];
116 cch = RTStrPrintf(szLogFile, sizeof(szLogFile),
117 "%s%c%s-Dhcpd.log",
118 m_strHome.c_str(), RTPATH_DELIMITER, m_strBaseName.c_str());
119 if (cch >= sizeof(szLogFile))
120 return VERR_BUFFER_OVERFLOW;
121
122
123 /* get a writable copy of the base name */
124 char szBaseName[RTPATH_MAX];
125 rc = RTStrCopy(szBaseName, sizeof(szBaseName), m_strBaseName.c_str());
126 if (RT_FAILURE(rc))
127 return rc;
128
129 /* sanitize base name some more to be usable in an environment variable name */
130 for (char *p = szBaseName; *p != '\0'; ++p)
131 {
132 if ( *p != '_'
133 && (*p < '0' || '9' < *p)
134 && (*p < 'a' || 'z' < *p)
135 && (*p < 'A' || 'Z' < *p))
136 {
137 *p = '_';
138 }
139 }
140
141
142 /* name of the environment variable to control logging */
143 char szEnvVarBase[128];
144 cch = RTStrPrintf(szEnvVarBase, sizeof(szEnvVarBase),
145 "VBOXDHCP_%s_RELEASE_LOG", szBaseName);
146 if (cch >= sizeof(szEnvVarBase))
147 return VERR_BUFFER_OVERFLOW;
148
149
150 rc = com::VBoxLogRelCreate("DHCP Server",
151 szLogFile,
152 RTLOGFLAGS_PREFIX_TIME_PROG,
153 "all all.restrict -default.restrict",
154 szEnvVarBase,
155 RTLOGDEST_FILE
156#ifdef DEBUG
157 | RTLOGDEST_STDERR
158#endif
159 ,
160 32768 /* cMaxEntriesPerGroup */,
161 0 /* cHistory */,
162 0 /* uHistoryFileTime */,
163 0 /* uHistoryFileSize */,
164 NULL /* pErrInfo */);
165
166 return rc;
167}
168
169
170int Config::complete()
171{
172 int rc;
173
174 if (m_strNetwork.empty())
175 {
176 LogDHCP(("network name is not specified\n"));
177 return false;
178 }
179
180 logInit();
181
182 bool fMACGenerated = false;
183 if ( m_MacAddress.au16[0] == 0
184 && m_MacAddress.au16[1] == 0
185 && m_MacAddress.au16[2] == 0)
186 {
187 RTUUID Uuid;
188 RTUuidCreate(&Uuid);
189
190 m_MacAddress.au8[0] = 0x08;
191 m_MacAddress.au8[1] = 0x00;
192 m_MacAddress.au8[2] = 0x27;
193 m_MacAddress.au8[3] = Uuid.Gen.au8Node[3];
194 m_MacAddress.au8[4] = Uuid.Gen.au8Node[4];
195 m_MacAddress.au8[5] = Uuid.Gen.au8Node[5];
196
197 LogDHCP(("MAC address is not specified: will use generated MAC %RTmac\n", &m_MacAddress));
198 fMACGenerated = true;
199 }
200
201 /* unicast MAC address */
202 if (m_MacAddress.au8[0] & 0x01)
203 {
204 LogDHCP(("MAC address is not unicast: %RTmac\n", &m_MacAddress));
205 return VERR_GENERAL_FAILURE;
206 }
207
208 /* unicast IP address */
209 if ((m_IPv4Address.au8[0] & 0xe0) == 0xe0)
210 {
211 LogDHCP(("IP address is not unicast: %RTnaipv4\n", m_IPv4Address.u));
212 return VERR_GENERAL_FAILURE;
213 }
214
215 /* valid netmask */
216 int iPrefixLengh;
217 rc = RTNetMaskToPrefixIPv4(&m_IPv4Netmask, &iPrefixLengh);
218 if (RT_FAILURE(rc) || iPrefixLengh == 0)
219 {
220 LogDHCP(("IP mask is not valid: %RTnaipv4\n", m_IPv4Netmask.u));
221 return VERR_GENERAL_FAILURE;
222 }
223
224 /* first IP is from the same network */
225 if ((m_IPv4PoolFirst.u & m_IPv4Netmask.u) != (m_IPv4Address.u & m_IPv4Netmask.u))
226 {
227 LogDHCP(("first pool address is outside the network %RTnaipv4/%d: %RTnaipv4\n",
228 (m_IPv4Address.u & m_IPv4Netmask.u), iPrefixLengh,
229 m_IPv4PoolFirst.u));
230 return VERR_GENERAL_FAILURE;
231 }
232
233 /* last IP is from the same network */
234 if ((m_IPv4PoolLast.u & m_IPv4Netmask.u) != (m_IPv4Address.u & m_IPv4Netmask.u))
235 {
236 LogDHCP(("last pool address is outside the network %RTnaipv4/%d: %RTnaipv4\n",
237 (m_IPv4Address.u & m_IPv4Netmask.u), iPrefixLengh,
238 m_IPv4PoolLast.u));
239 return VERR_GENERAL_FAILURE;
240 }
241
242 /* the pool is valid */
243 if (RT_N2H_U32(m_IPv4PoolLast.u) < RT_N2H_U32(m_IPv4PoolFirst.u))
244 {
245 LogDHCP(("pool range is invalid: %RTnaipv4 - %RTnaipv4\n",
246 m_IPv4PoolFirst.u, m_IPv4PoolLast.u));
247 return VERR_GENERAL_FAILURE;
248 }
249
250 /* our own address is not inside the pool */
251 if ( RT_N2H_U32(m_IPv4PoolFirst.u) <= RT_N2H_U32(m_IPv4Address.u)
252 && RT_N2H_U32(m_IPv4Address.u) <= RT_N2H_U32(m_IPv4PoolLast.u))
253 {
254 LogDHCP(("server address inside the pool range %RTnaipv4 - %RTnaipv4: %RTnaipv4\n",
255 m_IPv4PoolFirst.u, m_IPv4PoolLast.u, m_IPv4Address.u));
256 return VERR_GENERAL_FAILURE;
257 }
258
259 if (!fMACGenerated)
260 LogDHCP(("MAC address %RTmac\n", &m_MacAddress));
261 LogDHCP(("IP address %RTnaipv4/%d\n", m_IPv4Address.u, iPrefixLengh));
262 LogDHCP(("address pool %RTnaipv4 - %RTnaipv4\n", m_IPv4PoolFirst.u, m_IPv4PoolLast.u));
263
264 return VINF_SUCCESS;
265}
266
267
268Config *Config::hardcoded()
269{
270 int rc;
271
272 std::unique_ptr<Config> config(new Config());
273 rc = config->init();
274 if (RT_FAILURE(rc))
275 return NULL;
276
277 config->setNetwork("HostInterfaceNetworking-vboxnet0");
278 config->m_strTrunk.assign("vboxnet0");
279 config->m_enmTrunkType = kIntNetTrunkType_NetFlt;
280
281 config->m_MacAddress.au8[0] = 0x08;
282 config->m_MacAddress.au8[1] = 0x00;
283 config->m_MacAddress.au8[2] = 0x27;
284 config->m_MacAddress.au8[3] = 0xa9;
285 config->m_MacAddress.au8[4] = 0xcf;
286 config->m_MacAddress.au8[5] = 0xef;
287
288
289 config->m_IPv4Address.u = RT_H2N_U32_C(0xc0a838fe); /* 192.168.56.254 */
290 config->m_IPv4Netmask.u = RT_H2N_U32_C(0xffffff00); /* 255.255.255.0 */
291
292 /* flip to test naks */
293#if 1
294 config->m_IPv4PoolFirst.u = RT_H2N_U32_C(0xc0a8385a); /* 192.168.56.90 */
295 config->m_IPv4PoolLast.u = RT_H2N_U32_C(0xc0a83863); /* 192.168.56.99 */
296#else
297 config->m_IPv4PoolFirst.u = RT_H2N_U32_C(0xc0a838c9); /* 192.168.56.201 */
298 config->m_IPv4PoolLast.u = RT_H2N_U32_C(0xc0a838dc); /* 192.168.56.220 */
299#endif
300
301 rc = config->complete();
302 AssertRCReturn(rc, NULL);
303
304 return config.release();
305}
306
307
308/* compatibility with old VBoxNetDHCP */
309static const RTGETOPTDEF g_aCompatOptions[] =
310{
311 { "--ip-address", 'i', RTGETOPT_REQ_IPV4ADDR },
312 { "--lower-ip", 'l', RTGETOPT_REQ_IPV4ADDR },
313 { "--mac-address", 'a', RTGETOPT_REQ_MACADDR },
314 { "--need-main", 'M', RTGETOPT_REQ_BOOL },
315 { "--netmask", 'm', RTGETOPT_REQ_IPV4ADDR },
316 { "--network", 'n', RTGETOPT_REQ_STRING },
317 { "--trunk-name", 't', RTGETOPT_REQ_STRING },
318 { "--trunk-type", 'T', RTGETOPT_REQ_STRING },
319 { "--upper-ip", 'u', RTGETOPT_REQ_IPV4ADDR },
320};
321
322
323Config *Config::compat(int argc, char **argv)
324{
325 RTGETOPTSTATE State;
326 int rc;
327
328 rc = RTGetOptInit(&State, argc, argv,
329 g_aCompatOptions, RT_ELEMENTS(g_aCompatOptions), 1,
330 RTGETOPTINIT_FLAGS_NO_STD_OPTS);
331 AssertRCReturn(rc, NULL);
332
333 std::unique_ptr<Config> config(new Config());
334 rc = config->init();
335 if (RT_FAILURE(rc))
336 return NULL;
337
338 for (;;)
339 {
340 RTGETOPTUNION Val;
341
342 rc = RTGetOpt(&State, &Val);
343 if (rc == 0) /* done */
344 break;
345
346 switch (rc)
347 {
348 case 'a': /* --mac-address */
349 if ( config->m_MacAddress.au16[0] != 0
350 || config->m_MacAddress.au16[1] != 0
351 || config->m_MacAddress.au16[2] != 0)
352 {
353 RTMsgError("Duplicate --mac-address option");
354 return NULL;
355 }
356 config->m_MacAddress = Val.MacAddr;
357 break;
358
359 case 'i': /* --ip-address */
360 if (config->m_IPv4Address.u != 0)
361 {
362 RTMsgError("Duplicate --ip-address option");
363 return NULL;
364 }
365 config->m_IPv4Address = Val.IPv4Addr;
366 break;
367
368 case 'l': /* --lower-ip */
369 if (config->m_IPv4PoolFirst.u != 0)
370 {
371 RTMsgError("Duplicate --lower-ip option");
372 return NULL;
373 }
374 config->m_IPv4PoolFirst = Val.IPv4Addr;
375 break;
376
377 case 'M': /* --need-main */
378 /* for backward compatibility, ignored */
379 break;
380
381 case 'm': /* --netmask */
382 if (config->m_IPv4Netmask.u != 0)
383 {
384 RTMsgError("Duplicate --netmask option");
385 return NULL;
386 }
387 config->m_IPv4Netmask = Val.IPv4Addr;
388 break;
389
390 case 'n': /* --network */
391 if (!config->m_strNetwork.empty())
392 {
393 RTMsgError("Duplicate --network option");
394 return NULL;
395 }
396 config->setNetwork(Val.psz);
397 break;
398
399 case 't': /* --trunk-name */
400 if (!config->m_strTrunk.empty())
401 {
402 RTMsgError("Duplicate --trunk-name option");
403 return NULL;
404 }
405 config->m_strTrunk.assign(Val.psz);
406 break;
407
408 case 'T': /* --trunk-type */
409 if (config->m_enmTrunkType != kIntNetTrunkType_Invalid)
410 {
411 RTMsgError("Duplicate --trunk-type option");
412 return NULL;
413 }
414 else if (strcmp(Val.psz, "none") == 0)
415 config->m_enmTrunkType = kIntNetTrunkType_None;
416 else if (strcmp(Val.psz, "whatever") == 0)
417 config->m_enmTrunkType = kIntNetTrunkType_WhateverNone;
418 else if (strcmp(Val.psz, "netflt") == 0)
419 config->m_enmTrunkType = kIntNetTrunkType_NetFlt;
420 else if (strcmp(Val.psz, "netadp") == 0)
421 config->m_enmTrunkType = kIntNetTrunkType_NetAdp;
422 else
423 {
424 RTMsgError("Unknown trunk type '%s'", Val.psz);
425 return NULL;
426 }
427 break;
428
429 case 'u': /* --upper-ip */
430 if (config->m_IPv4PoolLast.u != 0)
431 {
432 RTMsgError("Duplicate --upper-ip option");
433 return NULL;
434 }
435 config->m_IPv4PoolLast = Val.IPv4Addr;
436 break;
437
438 case VINF_GETOPT_NOT_OPTION:
439 RTMsgError("%s: Unexpected command line argument", Val.psz);
440 return NULL;
441
442 default:
443 RTGetOptPrintError(rc, &Val);
444 return NULL;
445 }
446 }
447
448 rc = config->complete();
449 if (RT_FAILURE(rc))
450 return NULL;
451
452 return config.release();
453}
454
455
456#define DHCPD_GETOPT_COMMENT 256 /* No short option for --comment */
457static const RTGETOPTDEF g_aOptions[] =
458{
459 { "--config", 'c', RTGETOPT_REQ_STRING },
460 { "--comment", DHCPD_GETOPT_COMMENT, RTGETOPT_REQ_STRING }
461};
462
463
464Config *Config::create(int argc, char **argv)
465{
466 RTGETOPTSTATE State;
467 int rc;
468
469 rc = RTGetOptInit(&State, argc, argv,
470 g_aOptions, RT_ELEMENTS(g_aOptions), 1,
471 RTGETOPTINIT_FLAGS_NO_STD_OPTS);
472 AssertRCReturn(rc, NULL);
473
474 std::unique_ptr<Config> config;
475 for (;;)
476 {
477 RTGETOPTUNION Val;
478
479 rc = RTGetOpt(&State, &Val);
480 if (rc == 0) /* done */
481 break;
482
483 switch (rc)
484 {
485 case 'c': /* --config */
486 if (config.get() != NULL)
487 {
488 printf("Duplicate option: --config '%s'\n", Val.psz);
489 return NULL;
490 }
491
492 printf("reading config from %s\n", Val.psz);
493 config.reset(Config::read(Val.psz));
494 if (config.get() == NULL)
495 return NULL;
496
497 break;
498
499 case DHCPD_GETOPT_COMMENT: /* --comment */
500 /* The sole purpose of this option is to allow identification of DHCP
501 * server instances in the process list. We ignore the required string
502 * argument of this option.
503 */
504 continue;
505
506 case VINF_GETOPT_NOT_OPTION:
507 RTMsgError("Unexpected command line argument: '%s'", Val.psz);
508 return NULL;
509
510 default:
511 RTGetOptPrintError(rc, &Val);
512 return NULL;
513 }
514 }
515
516 if (config.get() == NULL)
517 return NULL;
518
519 rc = config->complete();
520 if (RT_FAILURE(rc))
521 return NULL;
522
523 return config.release();
524}
525
526
527Config *Config::read(const char *pszFileName)
528{
529 int rc;
530
531 if (pszFileName == NULL || pszFileName[0] == '\0')
532 return NULL;
533
534 xml::Document doc;
535 try
536 {
537 xml::XmlFileParser parser;
538 parser.read(pszFileName, doc);
539 }
540 catch (const xml::EIPRTFailure &e)
541 {
542 LogDHCP(("%s\n", e.what()));
543 return NULL;
544 }
545 catch (const RTCError &e)
546 {
547 LogDHCP(("%s\n", e.what()));
548 return NULL;
549 }
550 catch (...)
551 {
552 LogDHCP(("Unknown exception while reading and parsing '%s'\n",
553 pszFileName));
554 return NULL;
555 }
556
557 std::unique_ptr<Config> config(new Config());
558 rc = config->init();
559 if (RT_FAILURE(rc))
560 return NULL;
561
562 try
563 {
564 config->parseConfig(doc.getRootElement());
565 }
566 catch (const RTCError &e)
567 {
568 LogDHCP(("%s\n", e.what()));
569 return NULL;
570 }
571 catch (...)
572 {
573 LogDHCP(("Unexpected exception\n"));
574 return NULL;
575 }
576
577 return config.release();
578}
579
580
581void Config::parseConfig(const xml::ElementNode *root)
582{
583 if (root == NULL)
584 throw ConfigFileError("Empty config file");
585
586 /*
587 * XXX: NAMESPACE API IS COMPLETELY BROKEN, SO IGNORE IT FOR NOW
588 */
589 if (!root->nameEquals("DHCPServer"))
590 {
591 const char *name = root->getName();
592 throw ConfigFileError(RTCStringFmt("Unexpected root element \"%s\"",
593 name ? name : "(null)"));
594 }
595
596 parseServer(root);
597
598 /** @todo r=bird: Visual C++ 2010 does not grok this use of 'auto'. */
599 // XXX: debug
600 for (optmap_t::const_iterator it = m_GlobalOptions.begin(); it != m_GlobalOptions.end(); ++it) {
601 std::shared_ptr<DhcpOption> opt(it->second);
602
603 octets_t data;
604 opt->encode(data);
605
606 bool space = false;
607 for (octets_t::const_iterator itData = data.begin(); itData != data.end(); ++itData) {
608 uint8_t c = *itData;
609 if (space)
610 std::cout << " ";
611 else
612 space = true;
613 std::cout << (int)c;
614 }
615 std::cout << std::endl;
616 }
617}
618
619
620static void getIPv4AddrAttribute(const xml::ElementNode *pNode, const char *pcszAttrName,
621 RTNETADDRIPV4 *pAddr)
622{
623 RTCString strAddr;
624 bool fHasAttr = pNode->getAttributeValue(pcszAttrName, &strAddr);
625 if (!fHasAttr)
626 throw ConfigFileError(RTCStringFmt("%s attribute missing",
627 pcszAttrName));
628
629 int rc = RTNetStrToIPv4Addr(strAddr.c_str(), pAddr);
630 if (RT_FAILURE(rc))
631 throw ConfigFileError(RTCStringFmt("%s attribute invalid",
632 pcszAttrName));
633}
634
635
636void Config::parseServer(const xml::ElementNode *server)
637{
638 /*
639 * DHCPServer attributes
640 */
641 RTCString strNetworkName;
642 bool fHasNetworkName = server->getAttributeValue("networkName", &strNetworkName);
643 if (!fHasNetworkName)
644 throw ConfigFileError("DHCPServer/@networkName missing");
645
646 setNetwork(strNetworkName.c_str());
647
648 RTCString strTrunkType;
649 if (!server->getAttributeValue("trunkType", &strTrunkType))
650 throw ConfigFileError("DHCPServer/@trunkType missing");
651 if (strTrunkType == "none")
652 m_enmTrunkType = kIntNetTrunkType_None;
653 else if (strTrunkType == "whatever")
654 m_enmTrunkType = kIntNetTrunkType_WhateverNone;
655 else if (strTrunkType == "netflt")
656 m_enmTrunkType = kIntNetTrunkType_NetFlt;
657 else if (strTrunkType == "netadp")
658 m_enmTrunkType = kIntNetTrunkType_NetAdp;
659 else
660 throw ConfigFileError(RTCStringFmt("Invalid DHCPServer/@trunkType value: %s", strTrunkType.c_str()));
661
662 if ( m_enmTrunkType == kIntNetTrunkType_NetFlt
663 || m_enmTrunkType == kIntNetTrunkType_NetAdp)
664 {
665 RTCString strTrunk;
666 if (!server->getAttributeValue("trunkName", &strTrunk))
667 throw ConfigFileError("DHCPServer/@trunkName missing");
668 m_strTrunk = strTrunk.c_str();
669 }
670 else
671 m_strTrunk = "";
672
673 getIPv4AddrAttribute(server, "IPAddress", &m_IPv4Address);
674 getIPv4AddrAttribute(server, "networkMask", &m_IPv4Netmask);
675 getIPv4AddrAttribute(server, "lowerIP", &m_IPv4PoolFirst);
676 getIPv4AddrAttribute(server, "upperIP", &m_IPv4PoolLast);
677
678 /*
679 * DHCPServer children
680 */
681 xml::NodesLoop it(*server);
682 const xml::ElementNode *node;
683 while ((node = it.forAllNodes()) != NULL)
684 {
685 /*
686 * Global options
687 */
688 if (node->nameEquals("Options"))
689 {
690 parseGlobalOptions(node);
691 }
692
693 /*
694 * Per-VM configuration
695 */
696 else if (node->nameEquals("Config"))
697 {
698 parseVMConfig(node);
699 }
700 }
701}
702
703
704void Config::parseGlobalOptions(const xml::ElementNode *options)
705{
706 xml::NodesLoop it(*options);
707 const xml::ElementNode *node;
708 while ((node = it.forAllNodes()) != NULL)
709 {
710 if (node->nameEquals("Option"))
711 {
712 parseOption(node, m_GlobalOptions);
713 }
714 else
715 {
716 throw ConfigFileError(RTCStringFmt("Unexpected element \"%s\"",
717 node->getName()));
718 }
719 }
720}
721
722
723/*
724 * VM Config entries are generated automatically from VirtualBox.xml
725 * with the MAC fetched from the VM config. The client id is nowhere
726 * in the picture there, so VM config is indexed with plain RTMAC, not
727 * ClientId (also see getOptions below).
728 */
729void Config::parseVMConfig(const xml::ElementNode *config)
730{
731 RTMAC mac;
732 int rc;
733
734 RTCString strMac;
735 bool fHasMac = config->getAttributeValue("MACAddress", &strMac);
736 if (!fHasMac)
737 throw ConfigFileError(RTCStringFmt("Config missing MACAddress attribute"));
738
739 rc = parseMACAddress(mac, strMac);
740 if (RT_FAILURE(rc))
741 {
742 throw ConfigFileError(RTCStringFmt("Malformed MACAddress attribute \"%s\"",
743 strMac.c_str()));
744 }
745
746 vmmap_t::iterator vmit( m_VMMap.find(mac) );
747 if (vmit != m_VMMap.end())
748 {
749 throw ConfigFileError(RTCStringFmt("Duplicate Config for MACAddress \"%s\"",
750 strMac.c_str()));
751 }
752
753 optmap_t &vmopts = m_VMMap[mac];
754
755 xml::NodesLoop it(*config);
756 const xml::ElementNode *node;
757 while ((node = it.forAllNodes()) != NULL)
758 if (node->nameEquals("Option"))
759 parseOption(node, vmopts);
760 else
761 throw ConfigFileError(RTCStringFmt("Unexpected element \"%s\"",
762 node->getName()));
763}
764
765
766int Config::parseMACAddress(RTMAC &aMac, const RTCString &aStr)
767{
768 RTMAC mac;
769 int rc;
770
771 rc = RTNetStrToMacAddr(aStr.c_str(), &mac);
772 if (RT_FAILURE(rc))
773 return rc;
774 if (rc == VWRN_TRAILING_CHARS)
775 return VERR_INVALID_PARAMETER;
776
777 aMac = mac;
778 return VINF_SUCCESS;
779}
780
781
782int Config::parseClientId(OptClientId &aId, const RTCString &aStr)
783{
784 RT_NOREF(aId, aStr);
785 return VERR_GENERAL_FAILURE;
786}
787
788
789/*
790 * Parse <Option/> element and add the option to the specified optmap.
791 */
792void Config::parseOption(const xml::ElementNode *option, optmap_t &optmap)
793{
794 int rc;
795
796 uint8_t u8Opt;
797 RTCString strName;
798 bool fHasName = option->getAttributeValue("name", &strName);
799 if (fHasName)
800 {
801 const char *pcszName = strName.c_str();
802
803 rc = RTStrToUInt8Full(pcszName, 10, &u8Opt);
804 if (rc != VINF_SUCCESS) /* no warnings either */
805 throw ConfigFileError(RTCStringFmt("Bad option \"%s\"", pcszName));
806
807 }
808 else
809 throw ConfigFileError("missing option name");
810
811
812 uint32_t u32Enc = 0; /* XXX: DhcpOptEncoding_Legacy */
813 RTCString strEncoding;
814 bool fHasEncoding = option->getAttributeValue("encoding", &strEncoding);
815 if (fHasEncoding)
816 {
817 const char *pcszEnc = strEncoding.c_str();
818
819 rc = RTStrToUInt32Full(pcszEnc, 10, &u32Enc);
820 if (rc != VINF_SUCCESS) /* no warnings either */
821 throw ConfigFileError(RTCStringFmt("Bad encoding \"%s\"", pcszEnc));
822
823 switch (u32Enc)
824 {
825 case 0: /* XXX: DhcpOptEncoding_Legacy */
826 case 1: /* XXX: DhcpOptEncoding_Hex */
827 break;
828 default:
829 throw ConfigFileError(RTCStringFmt("Unknown encoding \"%s\"", pcszEnc));
830 }
831 }
832
833
834 /* value may be omitted for OptNoValue options like rapid commit */
835 RTCString strValue;
836 option->getAttributeValue("value", &strValue);
837
838 /* XXX: TODO: encoding, handle hex */
839 DhcpOption *opt = DhcpOption::parse(u8Opt, u32Enc, strValue.c_str());
840 if (opt == NULL)
841 throw ConfigFileError(RTCStringFmt("Bad option \"%s\"", strName.c_str()));
842
843 optmap << opt;
844}
845
846
847/*
848 * Set m_strBaseName to sanitized version of m_strNetwork that can be
849 * used in a path component.
850 */
851void Config::sanitizeBaseName()
852{
853 int rc;
854
855 if (m_strNetwork.empty())
856 return;
857
858 char szBaseName[RTPATH_MAX];
859 rc = RTStrCopy(szBaseName, sizeof(szBaseName), m_strNetwork.c_str());
860 if (RT_FAILURE(rc))
861 return;
862
863 for (char *p = szBaseName; *p != '\0'; ++p)
864 {
865 if (RTPATH_IS_SEP(*p))
866 {
867 *p = '_';
868 }
869 }
870
871 m_strBaseName.assign(szBaseName);
872}
873
874
875optmap_t Config::getOptions(const OptParameterRequest &reqOpts,
876 const ClientId &id,
877 const OptVendorClassId &vendor) const
878{
879 optmap_t optmap;
880
881 const optmap_t *vmopts = NULL;
882 vmmap_t::const_iterator vmit( m_VMMap.find(id.mac()) );
883 if (vmit != m_VMMap.end())
884 vmopts = &vmit->second;
885
886 RT_NOREF(vendor); /* not yet */
887
888
889 optmap << new OptSubnetMask(m_IPv4Netmask);
890
891 const OptParameterRequest::value_t& reqValue = reqOpts.value();
892 for (octets_t::const_iterator itOptReq = reqValue.begin(); itOptReq != reqValue.end(); ++itOptReq)
893 {
894 uint8_t optreq = *itOptReq;
895 std::cout << ">>> requested option " << (int)optreq << std::endl;
896
897 if (optreq == OptSubnetMask::optcode)
898 {
899 std::cout << "... always supplied" << std::endl;
900 continue;
901 }
902
903 if (vmopts != NULL)
904 {
905 optmap_t::const_iterator it( vmopts->find(optreq) );
906 if (it != vmopts->end())
907 {
908 optmap << it->second;
909 std::cout << "... found in VM options" << std::endl;
910 continue;
911 }
912 }
913
914 optmap_t::const_iterator it( m_GlobalOptions.find(optreq) );
915 if (it != m_GlobalOptions.end())
916 {
917 optmap << it->second;
918 std::cout << "... found in global options" << std::endl;
919 continue;
920 }
921
922 // std::cout << "... not found" << std::endl;
923 }
924
925
926 /* XXX: testing ... */
927 if (vmopts != NULL)
928 {
929 for (optmap_t::const_iterator it = vmopts->begin(); it != vmopts->end(); ++it) {
930 std::shared_ptr<DhcpOption> opt(it->second);
931 if (optmap.count(opt->optcode()) == 0 && opt->optcode() > 127)
932 {
933 optmap << opt;
934 std::cout << "... forcing VM option " << (int)opt->optcode() << std::endl;
935 }
936 }
937 }
938
939 for (optmap_t::const_iterator it = m_GlobalOptions.begin(); it != m_GlobalOptions.end(); ++it) {
940 std::shared_ptr<DhcpOption> opt(it->second);
941 if (optmap.count(opt->optcode()) == 0 && opt->optcode() > 127)
942 {
943 optmap << opt;
944 std::cout << "... forcing global option " << (int)opt->optcode() << std::endl;
945 }
946 }
947
948 return optmap;
949}
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