VirtualBox

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

Last change on this file since 79509 was 79509, checked in by vboxsync, 5 years ago

Dhcpd: Ditched std:vector<char> + catch with wrong slash non-sense in VBoxNetDhcpd::vmmInit(). Misc cleanups.

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