VirtualBox

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

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

Dhcpd: Eliminated use of std::string (probably my mistake in the original code). Various other cleanups. bugref:9288

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