VirtualBox

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

Last change on this file since 71740 was 71689, checked in by vboxsync, 7 years ago

NetworkServices/Dhcpd: implement reading config from a file.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.5 KB
Line 
1/* $Id: Config.cpp 71689 2018-04-05 15:20:32Z vboxsync $ */
2/** @file
3 * DHCP server - server configuration
4 */
5
6/*
7 * Copyright (C) 2017-2018 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
456static const RTGETOPTDEF g_aOptions[] =
457{
458 { "--config", 'c', 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 VINF_GETOPT_NOT_OPTION:
498 RTMsgError("Unexpected command line argument: '%s'", Val.psz);
499 return NULL;
500
501 default:
502 RTGetOptPrintError(rc, &Val);
503 return NULL;
504 }
505 }
506
507 if (config.get() == NULL)
508 return NULL;
509
510 rc = config->complete();
511 if (RT_FAILURE(rc))
512 return NULL;
513
514 return config.release();
515}
516
517
518Config *Config::read(const char *pszFileName)
519{
520 int rc;
521
522 if (pszFileName == NULL || pszFileName[0] == '\0')
523 return NULL;
524
525 xml::Document doc;
526 try
527 {
528 xml::XmlFileParser parser;
529 parser.read(pszFileName, doc);
530 }
531 catch (const xml::EIPRTFailure &e)
532 {
533 LogDHCP(("%s\n", e.what()));
534 return NULL;
535 }
536 catch (const RTCError &e)
537 {
538 LogDHCP(("%s\n", e.what()));
539 return NULL;
540 }
541 catch (...)
542 {
543 LogDHCP(("Unknown exception while reading and parsing '%s'\n",
544 pszFileName));
545 return NULL;
546 }
547
548 std::unique_ptr<Config> config(new Config());
549 rc = config->init();
550 if (RT_FAILURE(rc))
551 return NULL;
552
553 try
554 {
555 config->parseConfig(doc.getRootElement());
556 }
557 catch (const RTCError &e)
558 {
559 LogDHCP(("%s\n", e.what()));
560 return NULL;
561 }
562 catch (...)
563 {
564 LogDHCP(("Unexpected exception\n"));
565 return NULL;
566 }
567
568 return config.release();
569}
570
571
572void Config::parseConfig(const xml::ElementNode *root)
573{
574 if (root == NULL)
575 throw ConfigFileError("Empty config file");
576
577 /*
578 * XXX: NAMESPACE API IS COMPLETELY BROKEN, SO IGNORE IT FOR NOW
579 */
580 if (!root->nameEquals("DHCPServer"))
581 {
582 const char *name = root->getName();
583 throw ConfigFileError(RTCStringFmt("Unexpected root element \"%s\"",
584 name ? name : "(null)"));
585 }
586
587 parseServer(root);
588
589 // XXX: debug
590 for (auto it: m_GlobalOptions) {
591 std::shared_ptr<DhcpOption> opt(it.second);
592
593 octets_t data;
594 opt->encode(data);
595
596 bool space = false;
597 for (auto c: data) {
598 if (space)
599 std::cout << " ";
600 else
601 space = true;
602 std::cout << (int)c;
603 }
604 std::cout << std::endl;
605 }
606}
607
608
609static void getIPv4AddrAttribute(const xml::ElementNode *pNode, const char *pcszAttrName,
610 RTNETADDRIPV4 *pAddr)
611{
612 RTCString strAddr;
613 bool fHasAttr = pNode->getAttributeValue(pcszAttrName, &strAddr);
614 if (!fHasAttr)
615 throw ConfigFileError(RTCStringFmt("%s attribute missing",
616 pcszAttrName));
617
618 int rc = RTNetStrToIPv4Addr(strAddr.c_str(), pAddr);
619 if (RT_FAILURE(rc))
620 throw ConfigFileError(RTCStringFmt("%s attribute invalid",
621 pcszAttrName));
622}
623
624
625void Config::parseServer(const xml::ElementNode *server)
626{
627 /*
628 * DHCPServer attributes
629 */
630 RTCString strNetworkName;
631 bool fHasNetworkName = server->getAttributeValue("networkName", &strNetworkName);
632 if (!fHasNetworkName)
633 throw ConfigFileError("DHCPServer/@networkName missing");
634
635 setNetwork(strNetworkName.c_str());
636
637 getIPv4AddrAttribute(server, "IPAddress", &m_IPv4Address);
638 getIPv4AddrAttribute(server, "networkMask", &m_IPv4Netmask);
639 getIPv4AddrAttribute(server, "lowerIP", &m_IPv4PoolFirst);
640 getIPv4AddrAttribute(server, "upperIP", &m_IPv4PoolLast);
641
642 /*
643 * DHCPServer children
644 */
645 xml::NodesLoop it(*server);
646 const xml::ElementNode *node;
647 while ((node = it.forAllNodes()) != NULL)
648 {
649 /*
650 * Global options
651 */
652 if (node->nameEquals("Options"))
653 {
654 parseGlobalOptions(node);
655 }
656
657 /*
658 * Per-VM configuration
659 */
660 else if (node->nameEquals("Config"))
661 {
662 parseVMConfig(node);
663 }
664 }
665}
666
667
668void Config::parseGlobalOptions(const xml::ElementNode *options)
669{
670 xml::NodesLoop it(*options);
671 const xml::ElementNode *node;
672 while ((node = it.forAllNodes()) != NULL)
673 {
674 if (node->nameEquals("Option"))
675 {
676 parseOption(node, m_GlobalOptions);
677 }
678 else
679 {
680 throw ConfigFileError(RTCStringFmt("Unexpected element \"%s\"",
681 node->getName()));
682 }
683 }
684}
685
686
687/*
688 * VM Config entries are generated automatically from VirtualBox.xml
689 * with the MAC fetched from the VM config. The client id is nowhere
690 * in the picture there, so VM config is indexed with plain RTMAC, not
691 * ClientId (also see getOptions below).
692 */
693void Config::parseVMConfig(const xml::ElementNode *config)
694{
695 RTMAC mac;
696 int rc;
697
698 RTCString strMac;
699 bool fHasMac = config->getAttributeValue("MACAddress", &strMac);
700 if (!fHasMac)
701 throw ConfigFileError(RTCStringFmt("Config missing MACAddress attribute"));
702
703 rc = parseMACAddress(mac, strMac);
704 if (RT_FAILURE(rc))
705 {
706 throw ConfigFileError(RTCStringFmt("Malformed MACAddress attribute \"%s\"",
707 strMac.c_str()));
708 }
709
710 vmmap_t::iterator vmit( m_VMMap.find(mac) );
711 if (vmit != m_VMMap.end())
712 {
713 throw ConfigFileError(RTCStringFmt("Duplicate Config for MACAddress \"%s\"",
714 strMac.c_str()));
715 }
716
717 optmap_t &vmopts = m_VMMap[mac];
718
719 xml::NodesLoop it(*config);
720 const xml::ElementNode *node;
721 while ((node = it.forAllNodes()) != NULL)
722 {
723 if (node->nameEquals("Option"))
724 {
725 parseOption(node, vmopts);
726 }
727 else
728 {
729 throw ConfigFileError(RTCStringFmt("Unexpected element \"%s\"",
730 node->getName()));
731 }
732 }
733}
734
735
736int Config::parseMACAddress(RTMAC &aMac, const RTCString &aStr)
737{
738 RTMAC mac;
739 int rc;
740
741 rc = RTNetStrToMacAddr(aStr.c_str(), &mac);
742 if (RT_FAILURE(rc))
743 return rc;
744 if (rc == VWRN_TRAILING_CHARS)
745 return VERR_INVALID_PARAMETER;
746
747 aMac = mac;
748 return VINF_SUCCESS;
749}
750
751
752int Config::parseClientId(OptClientId &aId, const RTCString &aStr)
753{
754 RT_NOREF(aId, aStr);
755 return VERR_GENERAL_FAILURE;
756}
757
758
759/*
760 * Parse <Option/> element and add the option to the specified optmap.
761 */
762void Config::parseOption(const xml::ElementNode *option, optmap_t &optmap)
763{
764 int rc;
765
766 uint8_t u8Opt;
767 RTCString strName;
768 bool fHasName = option->getAttributeValue("name", &strName);
769 if (fHasName)
770 {
771 const char *pcszName = strName.c_str();
772
773 rc = RTStrToUInt8Full(pcszName, 10, &u8Opt);
774 if (rc != VINF_SUCCESS) /* no warnings either */
775 throw ConfigFileError(RTCStringFmt("Bad option \"%s\"", pcszName));
776
777 }
778 else
779 throw ConfigFileError("missing option name");
780
781
782 uint32_t u32Enc = 0; /* XXX: DhcpOptEncoding_Legacy */
783 RTCString strEncoding;
784 bool fHasEncoding = option->getAttributeValue("encoding", &strEncoding);
785 if (fHasEncoding)
786 {
787 const char *pcszEnc = strEncoding.c_str();
788
789 rc = RTStrToUInt32Full(pcszEnc, 10, &u32Enc);
790 if (rc != VINF_SUCCESS) /* no warnings either */
791 throw ConfigFileError(RTCStringFmt("Bad encoding \"%s\"", pcszEnc));
792
793 switch (u32Enc)
794 {
795 case 0: /* XXX: DhcpOptEncoding_Legacy */
796 case 1: /* XXX: DhcpOptEncoding_Hex */
797 break;
798 default:
799 throw ConfigFileError(RTCStringFmt("Unknown encoding \"%s\"", pcszEnc));
800 }
801 }
802
803
804 /* value may be omitted for OptNoValue options like rapid commit */
805 RTCString strValue;
806 option->getAttributeValue("value", &strValue);
807
808 /* XXX: TODO: encoding, handle hex */
809 DhcpOption *opt = DhcpOption::parse(u8Opt, u32Enc, strValue.c_str());
810 if (opt == NULL)
811 throw ConfigFileError(RTCStringFmt("Bad option \"%s\"", strName.c_str()));
812
813 optmap << opt;
814}
815
816
817/*
818 * Set m_strBaseName to sanitized version of m_strNetwork that can be
819 * used in a path component.
820 */
821void Config::sanitizeBaseName()
822{
823 int rc;
824
825 if (m_strNetwork.empty())
826 return;
827
828 char szBaseName[RTPATH_MAX];
829 rc = RTStrCopy(szBaseName, sizeof(szBaseName), m_strNetwork.c_str());
830 if (RT_FAILURE(rc))
831 return;
832
833 for (char *p = szBaseName; *p != '\0'; ++p)
834 {
835 if (RTPATH_IS_SEP(*p))
836 {
837 *p = '_';
838 }
839 }
840
841 m_strBaseName.assign(szBaseName);
842}
843
844
845optmap_t Config::getOptions(const OptParameterRequest &reqOpts,
846 const ClientId &id,
847 const OptVendorClassId &vendor) const
848{
849 optmap_t optmap;
850
851 const optmap_t *vmopts = NULL;
852 vmmap_t::const_iterator vmit( m_VMMap.find(id.mac()) );
853 if (vmit != m_VMMap.end())
854 vmopts = &vmit->second;
855
856 RT_NOREF(vendor); /* not yet */
857
858
859 optmap << new OptSubnetMask(m_IPv4Netmask);
860
861 for (auto optreq: reqOpts.value())
862 {
863 std::cout << ">>> requested option " << (int)optreq << std::endl;
864
865 if (optreq == OptSubnetMask::optcode)
866 {
867 std::cout << "... always supplied" << std::endl;
868 continue;
869 }
870
871 if (vmopts != NULL)
872 {
873 optmap_t::const_iterator it( vmopts->find(optreq) );
874 if (it != vmopts->end())
875 {
876 optmap << it->second;
877 std::cout << "... found in VM options" << std::endl;
878 continue;
879 }
880 }
881
882 optmap_t::const_iterator it( m_GlobalOptions.find(optreq) );
883 if (it != m_GlobalOptions.end())
884 {
885 optmap << it->second;
886 std::cout << "... found in global options" << std::endl;
887 continue;
888 }
889
890 // std::cout << "... not found" << std::endl;
891 }
892
893
894 /* XXX: testing ... */
895 if (vmopts != NULL)
896 {
897 for (auto it: *vmopts) {
898 std::shared_ptr<DhcpOption> opt(it.second);
899 if (optmap.count(opt->optcode()) == 0 && opt->optcode() > 127)
900 {
901 optmap << opt;
902 std::cout << "... forcing VM option " << (int)opt->optcode() << std::endl;
903 }
904 }
905 }
906
907 for (auto it: m_GlobalOptions) {
908 std::shared_ptr<DhcpOption> opt(it.second);
909 if (optmap.count(opt->optcode()) == 0 && opt->optcode() > 127)
910 {
911 optmap << opt;
912 std::cout << "... forcing global option " << (int)opt->optcode() << std::endl;
913 }
914 }
915
916 return optmap;
917}
Note: See TracBrowser for help on using the repository browser.

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