Index: /trunk/src/VBox/Main/include/DHCPConfigImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/DHCPConfigImpl.h	(revision 79799)
+++ /trunk/src/VBox/Main/include/DHCPConfigImpl.h	(revision 79800)
@@ -235,4 +235,11 @@
                                           VirtualBoxBase *pErrorDst);
 
+    /** @name Internal accessors
+     * @{ */
+    bool                        i_getInclusive() const RT_NOEXCEPT  { return m_fInclusive; }
+    DHCPGroupConditionType_T    i_getType() const RT_NOEXCEPT       { return m_enmType; }
+    com::Utf8Str const         &i_getValue() const RT_NOEXCEPT      { return m_strValue; }
+    /** @} */
+
 protected:
     /** @name Wrapped IDHCPGroupCondition properties
@@ -288,4 +295,5 @@
     HRESULT i_saveSettings(settings::DHCPGroupConfig &a_rDst);
     HRESULT i_removeCondition(DHCPGroupCondition *a_pCondition);
+    void    i_writeDhcpdConfig(xml::ElementNode *a_pElmGroup) RT_OVERRIDE;
 
 protected:
Index: /trunk/src/VBox/Main/src-server/DHCPConfigImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/DHCPConfigImpl.cpp	(revision 79799)
+++ /trunk/src/VBox/Main/src-server/DHCPConfigImpl.cpp	(revision 79800)
@@ -865,4 +865,52 @@
 
 
+/**
+ * Overridden to add a 'name' attribute and emit condition child elements.
+ */
+void DHCPGroupConfig::i_writeDhcpdConfig(xml::ElementNode *a_pElmGroup)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    /* The name attribute: */
+    a_pElmGroup->setAttribute("name", m_strName);
+
+    /*
+     * Conditions:
+     */
+    for (ConditionsIterator it = m_Conditions.begin(); it != m_Conditions.end(); ++it)
+    {
+        xml::ElementNode *pElmCondition;
+        switch ((*it)->i_getType())
+        {
+            case DHCPGroupConditionType_MAC:
+                pElmCondition = a_pElmGroup->createChild("ConditionMAC");
+                break;
+            case DHCPGroupConditionType_MACWildcard:
+                pElmCondition = a_pElmGroup->createChild("ConditionMACWildcard");
+                break;
+            case DHCPGroupConditionType_vendorClassID:
+                pElmCondition = a_pElmGroup->createChild("ConditionVendorClassID");
+                break;
+            case DHCPGroupConditionType_vendorClassIDWildcard:
+                pElmCondition = a_pElmGroup->createChild("ConditionVendorClassIDWildcard");
+                break;
+            case DHCPGroupConditionType_userClassID:
+                pElmCondition = a_pElmGroup->createChild("ConditionUserClassID");
+                break;
+            case DHCPGroupConditionType_userClassIDWildcard:
+                pElmCondition = a_pElmGroup->createChild("ConditionUserClassIDWildcard");
+                break;
+            default:
+                AssertLogRelMsgFailed(("m_enmType=%d\n", (*it)->i_getType()));
+                continue;
+        }
+        pElmCondition->setAttribute("inclusive", (*it)->i_getInclusive());
+        pElmCondition->setAttribute("value", (*it)->i_getValue());
+    }
+
+    DHCPConfig::i_writeDhcpdConfig(a_pElmGroup);
+}
+
+
 HRESULT DHCPGroupConfig::getName(com::Utf8Str &aName)
 {
Index: /trunk/src/VBox/NetworkServices/Dhcpd/Config.cpp
===================================================================
--- /trunk/src/VBox/NetworkServices/Dhcpd/Config.cpp	(revision 79799)
+++ /trunk/src/VBox/NetworkServices/Dhcpd/Config.cpp	(revision 79800)
@@ -39,5 +39,6 @@
 *   Global Variables                                                                                                             *
 *********************************************************************************************************************************/
-/*static*/ bool Config::g_fInitializedLog = false;
+/*static*/ bool         Config::g_fInitializedLog = false;
+/*static*/ uint32_t     GroupConfig::s_uGroupNo   = 0;
 
 
@@ -55,4 +56,18 @@
 #endif
 
+    explicit ConfigFileError(xml::Node const *pNode, const char *a_pszMsgFmt, ...)
+        : RTCError((char *)NULL)
+    {
+
+        i_buildPath(pNode);
+        m_strMsg.append(": ");
+
+        va_list va;
+        va_start(va, a_pszMsgFmt);
+        m_strMsg.appendPrintf(a_pszMsgFmt, va);
+        va_end(va);
+    }
+
+
     ConfigFileError(const char *a_pszMsgFmt, ...)
         : RTCError((char *)NULL)
@@ -67,4 +82,30 @@
         : RTCError(a_rstrMessage)
     {}
+
+private:
+    void i_buildPath(xml::Node const *pNode)
+    {
+        if (pNode)
+        {
+            i_buildPath(pNode->getParent());
+            m_strMsg.append('/');
+            m_strMsg.append(pNode->getName());
+            if (pNode->isElement())
+            {
+                xml::ElementNode const *pElm = (xml::ElementNode const *)pNode;
+                for (xml::Node const *pNodeChild = pElm->getFirstChild(); pNodeChild != NULL;
+                    pNodeChild = pNodeChild->getNextSibiling())
+                    if (pNodeChild->isAttribute())
+                    {
+                        m_strMsg.append("[@");
+                        m_strMsg.append(pNodeChild->getName());
+                        m_strMsg.append('=');
+                        m_strMsg.append(pNodeChild->getValue());
+                        m_strMsg.append(']');
+                    }
+            }
+        }
+    }
+
 };
 
@@ -83,6 +124,7 @@
     , m_IPv4PoolFirst()
     , m_IPv4PoolLast()
-    , m_GlobalOptions()
-    , m_VMMap()
+    , m_GlobalConfig()
+    , m_GroupConfigs()
+    , m_HostConfigs()
 {
 }
@@ -327,4 +369,6 @@
         { "--log-flags",            'f', RTGETOPT_REQ_STRING },
         { "--log-group-settings",   'g', RTGETOPT_REQ_STRING },
+        { "--relaxed",              'r', RTGETOPT_REQ_NOTHING },
+        { "--strict",               's', RTGETOPT_REQ_NOTHING },
     };
 
@@ -339,4 +383,5 @@
     const char *pszConfig           = NULL;
     const char *pszComment          = NULL;
+    bool        fStrict             = true;
 
     for (;;)
@@ -367,4 +412,12 @@
             case 'g':
                 pszLogGroupSettings = ValueUnion.psz;
+                break;
+
+            case 'r':
+                fStrict = false;
+                break;
+
+            case 's':
+                fStrict = true;
                 break;
 
@@ -376,8 +429,4 @@
                 break;
 
-            case VINF_GETOPT_NOT_OPTION:
-                RTMsgError("Unexpected command line argument: '%s'", ValueUnion.psz);
-                return NULL;
-
             default:
                 RTGetOptPrintError(rc, &ValueUnion);
@@ -418,5 +467,5 @@
     RTMsgInfo("reading config from '%s'...\n", pszConfig);
     std::unique_ptr<Config> ptrConfig;
-    ptrConfig.reset(Config::i_read(pszConfig));
+    ptrConfig.reset(Config::i_read(pszConfig, fStrict));
     if (ptrConfig.get() != NULL)
     {
@@ -433,5 +482,5 @@
  * @note The release log has is not operational when this method is called.
  */
-Config *Config::i_read(const char *pszFileName) RT_NOEXCEPT
+Config *Config::i_read(const char *pszFileName, bool fStrict) RT_NOEXCEPT
 {
     if (pszFileName == NULL || pszFileName[0] == '\0')
@@ -468,5 +517,5 @@
     try
     {
-        config->i_parseConfig(doc.getRootElement());
+        config->i_parseConfig(doc.getRootElement(), fStrict);
     }
     catch (const RTCError &e)
@@ -491,11 +540,57 @@
 
 /**
+ * Helper for retrieving a IPv4 attribute.
+ *
+ * @param   pElm            The element to get the attribute from.
+ * @param   pszAttrName     The name of the attribute
+ * @param   pAddr           Where to return the address.
+ * @throws  ConfigFileError
+ */
+static void getIPv4AddrAttribute(const xml::ElementNode *pElm, const char *pszAttrName, PRTNETADDRIPV4 pAddr)
+{
+    const char *pszAttrValue;
+    if (pElm->getAttributeValue(pszAttrName, &pszAttrValue))
+    {
+        int rc = RTNetStrToIPv4Addr(pszAttrValue, pAddr);
+        if (RT_SUCCESS(rc))
+            return;
+        throw ConfigFileError(pElm, "Attribute %s is not a valid IPv4 address: '%s' -> %Rrc", pszAttrName, pszAttrValue, rc);
+    }
+    throw ConfigFileError(pElm, "Required %s attribute missing", pszAttrName);
+}
+
+
+/**
+ * Helper for retrieving a MAC address attribute.
+ *
+ * @param   pElm            The element to get the attribute from.
+ * @param   pszAttrName     The name of the attribute
+ * @param   pMacAddr        Where to return the MAC address.
+ * @throws  ConfigFileError
+ */
+static void getMacAddressAttribute(const xml::ElementNode *pElm, const char *pszAttrName, PRTMAC pMacAddr)
+{
+    const char *pszAttrValue;
+    if (pElm->getAttributeValue(pszAttrName, &pszAttrValue))
+    {
+        int rc = RTNetStrToMacAddr(pszAttrValue, pMacAddr);
+        if (RT_SUCCESS(rc) && rc != VWRN_TRAILING_CHARS)
+            return;
+        throw ConfigFileError(pElm, "attribute %s is not a valid MAC address: '%s' -> %Rrc", pszAttrName, pszAttrValue, rc);
+    }
+    throw ConfigFileError(pElm, "Required %s attribute missing", pszAttrName);
+}
+
+
+/**
  * Internal worker for i_read() that parses the root element and everything
  * below it.
  *
- * @param   pElmRoot    The root element.
+ * @param   pElmRoot            The root element.
+ * @param   fStrict             Set if we're in strict mode, clear if we just
+ *                              want to get on with it if we can.
  * @throws  std::bad_alloc, ConfigFileError
  */
-void Config::i_parseConfig(const xml::ElementNode *pElmRoot)
+void Config::i_parseConfig(const xml::ElementNode *pElmRoot, bool fStrict)
 {
     /*
@@ -510,5 +605,5 @@
         throw ConfigFileError("Unexpected root element '%s'", pElmRoot->getName());
 
-    i_parseServer(pElmRoot);
+    i_parseServer(pElmRoot, fStrict);
 
 #if 0 /** @todo convert to LogRel2 stuff */
@@ -539,7 +634,9 @@
  *
  * @param   pElmServer          The DHCPServer element.
+ * @param   fStrict             Set if we're in strict mode, clear if we just
+ *                              want to get on with it if we can.
  * @throws  std::bad_alloc, ConfigFileError
  */
-void Config::i_parseServer(const xml::ElementNode *pElmServer)
+void Config::i_parseServer(const xml::ElementNode *pElmServer, bool fStrict)
 {
     /*
@@ -588,8 +685,8 @@
     }
 
-    i_getIPv4AddrAttribute(pElmServer, "IPAddress", &m_IPv4Address);
-    i_getIPv4AddrAttribute(pElmServer, "networkMask", &m_IPv4Netmask);
-    i_getIPv4AddrAttribute(pElmServer, "lowerIP", &m_IPv4PoolFirst);
-    i_getIPv4AddrAttribute(pElmServer, "upperIP", &m_IPv4PoolLast);
+    ::getIPv4AddrAttribute(pElmServer, "IPAddress", &m_IPv4Address);
+    ::getIPv4AddrAttribute(pElmServer, "networkMask", &m_IPv4Netmask);
+    ::getIPv4AddrAttribute(pElmServer, "lowerIP", &m_IPv4PoolFirst);
+    ::getIPv4AddrAttribute(pElmServer, "upperIP", &m_IPv4PoolLast);
 
     /*
@@ -600,111 +697,65 @@
     while ((pElmChild = it.forAllNodes()) != NULL)
     {
+        /* Global options: */
+        if (pElmChild->nameEquals("Options"))
+            m_GlobalConfig.initFromXml(pElmChild, fStrict);
+        /* Group w/ options: */
+        else if (pElmChild->nameEquals("Group"))
+        {
+            std::unique_ptr<GroupConfig> ptrGroup(new GroupConfig());
+            ptrGroup->initFromXml(pElmChild, fStrict);
+            if (m_GroupConfigs.find(ptrGroup->getGroupName()) == m_GroupConfigs.end())
+            {
+                m_GroupConfigs[ptrGroup->getGroupName()] = ptrGroup.get();
+                ptrGroup.release();
+            }
+            else if (!fStrict)
+                LogRelFunc(("Ignoring duplicate group name: %s", ptrGroup->getGroupName().c_str()));
+            else
+                throw ConfigFileError("Duplicate group name: %s", ptrGroup->getGroupName().c_str());
+        }
         /*
-         * Global options
-         */
-        if (pElmChild->nameEquals("Options"))
-            i_parseGlobalOptions(pElmChild);
-        /*
-         * Per-VM configuration
+         * MAC address and per VM NIC configurations:
          */
         else if (pElmChild->nameEquals("Config"))
-            i_parseVMConfig(pElmChild);
+        {
+            std::unique_ptr<HostConfig> ptrHost(new HostConfig());
+            ptrHost->initFromXml(pElmChild, fStrict);
+            if (m_HostConfigs.find(ptrHost->getMACAddress()) == m_HostConfigs.end())
+            {
+                m_HostConfigs[ptrHost->getMACAddress()] = ptrHost.get();
+                ptrHost.release();
+            }
+            else if (!fStrict)
+                LogRelFunc(("Ignorining duplicate MAC address (Config): %RTmac", &ptrHost->getMACAddress()));
+            else
+                throw ConfigFileError("Duplicate MAC address (Config): %RTmac", &ptrHost->getMACAddress());
+        }
+        else if (!fStrict)
+            LogRel(("Ignoring unexpected DHCPServer child: %s\n", pElmChild->getName()));
         else
-            LogRel(("Ignoring unexpected DHCPServer child: %s\n", pElmChild->getName()));
-    }
-}
-
-
-/**
- * Internal worker for parsing the elements under /DHCPServer/Options/.
- *
- * @param   pElmServer          The <Options> element.
+            throw ConfigFileError("Unexpected DHCPServer child <%s>'", pElmChild->getName());
+    }
+}
+
+
+/**
+ * Internal worker for parsing \<Option\> elements found under
+ * /DHCPServer/Options/, /DHCPServer/Group/ and /DHCPServer/Config/.
+ *
+ * @param   pElmOption          An \<Option\> element.
  * @throws  std::bad_alloc, ConfigFileError
  */
-void Config::i_parseGlobalOptions(const xml::ElementNode *options)
-{
-    xml::NodesLoop it(*options);
-    const xml::ElementNode *pElmChild;
-    while ((pElmChild = it.forAllNodes()) != NULL)
-    {
-        if (pElmChild->nameEquals("Option"))
-            i_parseOption(pElmChild, m_GlobalOptions);
-        else
-            throw ConfigFileError("Unexpected element <%s>", pElmChild->getName());
-    }
-}
-
-
-/**
- * Internal worker for parsing the elements under /DHCPServer/Config/.
- *
- * VM Config entries are generated automatically from VirtualBox.xml
- * with the MAC fetched from the VM config.  The client id is nowhere
- * in the picture there, so VM config is indexed with plain RTMAC, not
- * ClientId (also see getOptions below).
- *
- * @param   pElmServer          The <Config> element.
- * @throws  std::bad_alloc, ConfigFileError
- */
-void Config::i_parseVMConfig(const xml::ElementNode *pElmConfig)
-{
-    /*
-     * Attributes:
-     */
-    /* The MAC address: */
-    RTMAC MacAddr;
-    i_getMacAddressAttribute(pElmConfig, "MACAddress", &MacAddr);
-
-    vmmap_t::iterator vmit( m_VMMap.find(MacAddr) );
-    if (vmit != m_VMMap.end())
-        throw ConfigFileError("Duplicate Config for MACAddress %RTmac", &MacAddr);
-
-    optmap_t &vmopts = m_VMMap[MacAddr];
-
-    /* Name - optional: */
-    const char *pszName = NULL;
-    if (pElmConfig->getAttributeValue("name", &pszName))
-    {
-        /** @todo */
-    }
-
-    /* Fixed IP address assignment - optional: */
-    if (pElmConfig->findAttribute("FixedIPAddress") != NULL)
-    {
-        /** @todo */
-    }
-
-    /*
-     * Process the children.
-     */
-    xml::NodesLoop it(*pElmConfig);
-    const xml::ElementNode *pElmChild;
-    while ((pElmChild = it.forAllNodes()) != NULL)
-        if (pElmChild->nameEquals("Option"))
-            i_parseOption(pElmChild, vmopts);
-        else
-            throw ConfigFileError("Unexpected element '%s' under '%s'", pElmChild->getName(), pElmConfig->getName());
-}
-
-
-/**
- * Internal worker for parsing <Option> elements found under
- * /DHCPServer/Options/ and /DHCPServer/Config/
- *
- * @param   pElmServer          The <Option> element.
- * @param   optmap              The option map to add the option to.
- * @throws  std::bad_alloc, ConfigFileError
- */
-void Config::i_parseOption(const xml::ElementNode *pElmOption, optmap_t &optmap)
+void ConfigLevelBase::i_parseOption(const xml::ElementNode *pElmOption)
 {
     /* The 'name' attribute: */
     const char *pszName;
     if (!pElmOption->getAttributeValue("name", &pszName))
-        throw ConfigFileError("missing option name");
+        throw ConfigFileError(pElmOption, "missing option name");
 
     uint8_t u8Opt;
     int rc = RTStrToUInt8Full(pszName, 10, &u8Opt);
     if (rc != VINF_SUCCESS) /* no warnings either */
-        throw ConfigFileError("Bad option name '%s': %Rrc", pszName, rc);
+        throw ConfigFileError(pElmOption, "Bad option name '%s': %Rrc", pszName, rc);
 
     /* The opional 'encoding' attribute: */
@@ -715,5 +766,5 @@
         rc = RTStrToUInt32Full(pszEncoding, 10, &u32Enc);
         if (rc != VINF_SUCCESS) /* no warnings either */
-            throw ConfigFileError("Bad option encoding '%s': %Rrc", pszEncoding, rc);
+            throw ConfigFileError(pElmOption, "Bad option encoding '%s': %Rrc", pszEncoding, rc);
 
         switch (u32Enc)
@@ -723,5 +774,5 @@
                 break;
             default:
-                throw ConfigFileError("Unknown encoding '%s'", pszEncoding);
+                throw ConfigFileError(pElmOption, "Unknown encoding '%s'", pszEncoding);
         }
     }
@@ -735,56 +786,214 @@
     DhcpOption *opt = DhcpOption::parse(u8Opt, u32Enc, pszValue);
     if (opt == NULL)
-        throw ConfigFileError("Bad option '%s' (encoding %u): '%s' ", pszName, u32Enc, pszValue ? pszValue : "");
+        throw ConfigFileError(pElmOption, "Bad option '%s' (encoding %u): '%s' ", pszName, u32Enc, pszValue ? pszValue : "");
 
     /* Add it to the map: */
-    optmap << opt;
-}
-
-
-/**
- * Helper for retrieving a IPv4 attribute.
- *
- * @param   pElm            The element to get the attribute from.
- * @param   pszAttrName     The name of the attribute
- * @param   pAddr           Where to return the address.
- * @throws  ConfigFileError
- */
-/*static*/ void Config::i_getIPv4AddrAttribute(const xml::ElementNode *pElm, const char *pszAttrName, PRTNETADDRIPV4 pAddr)
-{
-    const char *pszAttrValue;
-    if (pElm->getAttributeValue(pszAttrName, &pszAttrValue))
-    {
-        int rc = RTNetStrToIPv4Addr(pszAttrValue, pAddr);
-        if (RT_SUCCESS(rc))
-            return;
-        throw ConfigFileError("%s attribute %s is not a valid IPv4 address: '%s' -> %Rrc",
-                              pElm->getName(), pszAttrName, pszAttrValue, rc);
+    m_Options << opt;
+}
+
+
+/**
+ * Final children parser, handling only \<Option\> and barfing at anything else.
+ *
+ * @param   pElmChild           The child element to handle.
+ * @param   fStrict             Set if we're in strict mode, clear if we just
+ *                              want to get on with it if we can.
+ * @throws  std::bad_alloc, ConfigFileError
+ */
+void ConfigLevelBase::i_parseChild(const xml::ElementNode *pElmChild, bool fStrict)
+{
+    if (pElmChild->nameEquals("Option"))
+    {
+        try
+        {
+            i_parseOption(pElmChild);
+        }
+        catch (ConfigFileError &rXcpt)
+        {
+            if (fStrict)
+                throw rXcpt;
+            LogRelFunc(("Ignoring option: %s\n", rXcpt.what()));
+        }
+    }
+    else if (!fStrict)
+    {
+        ConfigFileError Dummy(pElmChild->getParent(), "Unexpected child '%s'", pElmChild->getName());
+        LogRelFunc(("%s\n", Dummy.what()));
     }
     else
-        throw ConfigFileError("Required %s attribute missing on element %s", pszAttrName, pElm->getName());
-}
-
-
-/**
- * Helper for retrieving a MAC address attribute.
- *
- * @param   pElm            The element to get the attribute from.
- * @param   pszAttrName     The name of the attribute
- * @param   pMacAddr        Where to return the MAC address.
- * @throws  ConfigFileError
- */
-/*static*/ void Config::i_getMacAddressAttribute(const xml::ElementNode *pElm, const char *pszAttrName, PRTMAC pMacAddr)
-{
-    const char *pszAttrValue;
-    if (pElm->getAttributeValue(pszAttrName, &pszAttrValue))
-    {
-        int rc = RTNetStrToMacAddr(pszAttrValue, pMacAddr);
-        if (RT_SUCCESS(rc) && rc != VWRN_TRAILING_CHARS)
-            return;
-        throw ConfigFileError("%s attribute %s is not a valid MAC address: '%s' -> %Rrc",
-                              pElm->getName(), pszAttrName, pszAttrValue, rc);
-    }
+        throw ConfigFileError(pElmChild->getParent(), "Unexpected child '%s'", pElmChild->getName());
+}
+
+
+/**
+ * Base class initialization taking a /DHCPServer/Options, /DHCPServer/Group or
+ * /DHCPServer/Config element as input and handling common attributes as well as
+ * any \<Option\> children.
+ *
+ * @param   pElmConfig          The configuration element to parse.
+ * @param   fStrict             Set if we're in strict mode, clear if we just
+ *                              want to get on with it if we can.
+ * @throws  std::bad_alloc, ConfigFileError
+ */
+void ConfigLevelBase::initFromXml(const xml::ElementNode *pElmConfig, bool fStrict)
+{
+    /*
+     * Common attributes:
+     */
+    if (!pElmConfig->getAttributeValue("secMinLeaseTime", &m_secMinLeaseTime))
+        m_secMinLeaseTime = 0;
+    if (!pElmConfig->getAttributeValue("secDefaultLeaseTime", &m_secDefaultLeaseTime))
+        m_secDefaultLeaseTime = 0;
+    if (!pElmConfig->getAttributeValue("secMaxLeaseTime", &m_secMaxLeaseTime))
+        m_secMaxLeaseTime = 0;
+
+    /*
+     * Parse children.
+     */
+    xml::NodesLoop it(*pElmConfig);
+    const xml::ElementNode *pElmChild;
+    while ((pElmChild = it.forAllNodes()) != NULL)
+        i_parseChild(pElmChild, fStrict);
+}
+
+
+/**
+ * Internal worker for parsing the elements under /DHCPServer/Options/.
+ *
+ * @param   pElmOptions         The <Options> element.
+ * @param   fStrict             Set if we're in strict mode, clear if we just
+ *                              want to get on with it if we can.
+ * @throws  std::bad_alloc, ConfigFileError
+ */
+void GlobalConfig::initFromXml(const xml::ElementNode *pElmOptions, bool fStrict)
+{
+    ConfigLevelBase::initFromXml(pElmOptions, fStrict);
+}
+
+
+/**
+ * Overrides base class to handle the condition elements under \<Group\>.
+ *
+ * @param   pElmChild           The child element.
+ * @param   fStrict             Set if we're in strict mode, clear if we just
+ *                              want to get on with it if we can.
+ * @throws  std::bad_alloc, ConfigFileError
+ */
+void GroupConfig::i_parseChild(const xml::ElementNode *pElmChild, bool fStrict)
+{
+    if (pElmChild->nameEquals("ConditionMAC"))
+    { }
+    else if (pElmChild->nameEquals("ConditionMACWildcard"))
+    { }
+    else if (pElmChild->nameEquals("ConditionVendorClassID"))
+    { }
+    else if (pElmChild->nameEquals("ConditionVendorClassIDWildcard"))
+    { }
+    else if (pElmChild->nameEquals("ConditionUserClassID"))
+    { }
+    else if (pElmChild->nameEquals("ConditionUserClassIDWildcard"))
+    { }
     else
-        throw ConfigFileError("Required %s attribute missing on element %s", pszAttrName, pElm->getName());
+    {
+        ConfigLevelBase::i_parseChild(pElmChild, fStrict);
+        return;
+    }
+}
+
+
+/**
+ * Internal worker for parsing the elements under /DHCPServer/Group/.
+ *
+ * @param   pElmGroup           The \<Group\> element.
+ * @param   fStrict             Set if we're in strict mode, clear if we just
+ *                              want to get on with it if we can.
+ * @throws  std::bad_alloc, ConfigFileError
+ */
+void GroupConfig::initFromXml(const xml::ElementNode *pElmGroup, bool fStrict)
+{
+    /*
+     * Attributes:
+     */
+    if (!pElmGroup->getAttributeValue("name", m_strName) || m_strName.isEmpty())
+    {
+        if (fStrict)
+            throw ConfigFileError(pElmGroup, "Group as no name or the name is empty");
+        m_strName.printf("Group#%u", s_uGroupNo++);
+    }
+
+    /*
+     * Do common initialization (including children).
+     */
+    ConfigLevelBase::initFromXml(pElmGroup, fStrict);
+}
+
+
+/**
+ * Internal worker for parsing the elements under /DHCPServer/Config/.
+ *
+ * VM Config entries are generated automatically from VirtualBox.xml
+ * with the MAC fetched from the VM config.  The client id is nowhere
+ * in the picture there, so VM config is indexed with plain RTMAC, not
+ * ClientId (also see getOptions below).
+ *
+ * @param   pElmConfig          The \<Config\> element.
+ * @param   fStrict             Set if we're in strict mode, clear if we just
+ *                              want to get on with it if we can.
+ * @throws  std::bad_alloc, ConfigFileError
+ */
+void HostConfig::initFromXml(const xml::ElementNode *pElmConfig, bool fStrict)
+{
+    /*
+     * Attributes:
+     */
+    /* The MAC address: */
+    ::getMacAddressAttribute(pElmConfig, "MACAddress", &m_MACAddress);
+
+    /* Name - optional: */
+    if (!pElmConfig->getAttributeValue("name", m_strName))
+        m_strName.printf("MAC:%RTmac", m_MACAddress);
+
+    /* Fixed IP address assignment - optional: */
+    const char *pszFixedAddress = pElmConfig->findAttributeValue("FixedIPAddress");
+    if (!pszFixedAddress || *RTStrStripL(pszFixedAddress) == '\0')
+        m_fHaveFixedAddress = false;
+    else
+    {
+        m_fHaveFixedAddress = false;
+        ::getIPv4AddrAttribute(pElmConfig, "FixedIPAddress", &m_FixedAddress);
+    }
+
+    /*
+     * Do common initialization.
+     */
+    ConfigLevelBase::initFromXml(pElmConfig, fStrict);
+}
+
+
+/**
+ * Assembles a priorities vector of configurations for the client.
+ *
+ * @returns a_rRetConfigs for convenience.
+ * @param   a_rRetConfigs       Where to return the configurations.
+ * @param   a_ridClient         The client ID.
+ * @param   a_ridVendorClass    The vendor class ID if present.
+ * @param   a_ridUserClass      The user class ID if present
+ */
+Config::ConfigVec &Config::getConfigsForClient(Config::ConfigVec &a_rRetConfigs, const ClientId &a_ridClient,
+                                               const OptVendorClassId &a_ridVendorClass,
+                                               const OptUserClassId &a_ridUserClass) const
+{
+    /* Host specific config first: */
+    HostConfigMap::const_iterator itHost = m_HostConfigs.find(a_ridClient.mac());
+    if (itHost != m_HostConfigs.end())
+        a_rRetConfigs.push_back(itHost->second);
+
+    /* Groups: */
+    RT_NOREF(a_ridVendorClass, a_ridUserClass); /* not yet */
+
+    /* Global: */
+    a_rRetConfigs.push_back(&m_GlobalConfig);
+
+    return a_rRetConfigs;
 }
 
@@ -795,56 +1004,45 @@
  * @returns a_rRetOpts for convenience
  * @param   a_rRetOpts      Where to put the requested options.
- * @param   reqOpts         The requested options.
- * @param   id              The client ID.
- * @param   idVendorClass   The vendor class ID.
- * @param   idUserClass     The user class ID.
+ * @param   a_rReqOpts      The requested options.
+ * @param   a_rConfigs      Relevant configurations returned by
+ *                          Config::getConfigsForClient().
  *
  * @throws  std::bad_alloc
  */
-optmap_t &Config::getOptions(optmap_t &a_rRetOpts, const OptParameterRequest &reqOpts, const ClientId &id,
-                             const OptVendorClassId &idVendorClass /*= OptVendorClassId()*/,
-                             const OptUserClassId &idUserClass /*= OptUserClassId()*/) const
-{
-    const optmap_t *vmopts = NULL;
-    vmmap_t::const_iterator vmit( m_VMMap.find(id.mac()) );
-    if (vmit != m_VMMap.end())
-        vmopts = &vmit->second;
-
-    RT_NOREF(idVendorClass, idUserClass); /* not yet */
-
+optmap_t &Config::getOptionsForClient(optmap_t &a_rRetOpts, const OptParameterRequest &a_rReqOpts, ConfigVec &a_rConfigs) const
+{
+    /*
+     * Always supply the subnet:
+     */
     a_rRetOpts << new OptSubnetMask(m_IPv4Netmask);
 
-    const OptParameterRequest::value_t& reqValue = reqOpts.value();
+    /*
+     * Try provide the requested options:
+     */
+    const OptParameterRequest::value_t &reqValue = a_rReqOpts.value();
     for (octets_t::const_iterator itOptReq = reqValue.begin(); itOptReq != reqValue.end(); ++itOptReq)
     {
-        uint8_t optreq = *itOptReq;
-        LogRel2((">>> requested option %d (%#x)\n", optreq, optreq));
-
-        if (optreq == OptSubnetMask::optcode)
+        uint8_t bOptReq = *itOptReq;
+        LogRel2((">>> requested option %d (%#x)\n", bOptReq, bOptReq));
+
+        if (bOptReq != OptSubnetMask::optcode)
         {
+            bool fFound = false;
+            for (size_t i = 0; i < a_rConfigs.size(); i++)
+            {
+                optmap_t::const_iterator itFound;
+                if (a_rConfigs[i]->findOption(bOptReq, itFound)) /* crap interface */
+                {
+                    LogRel2(("... found in %s (type %s)\n", a_rConfigs[i]->getName(), a_rConfigs[i]->getType()));
+                    a_rRetOpts << itFound->second;
+                    fFound = true;
+                    break;
+                }
+            }
+            if (!fFound)
+                LogRel3(("... not found\n"));
+        }
+        else
             LogRel2(("... always supplied\n"));
-            continue;
-        }
-
-        if (vmopts != NULL)
-        {
-            optmap_t::const_iterator it( vmopts->find(optreq) );
-            if (it != vmopts->end())
-            {
-                a_rRetOpts << it->second;
-                LogRel2(("... found in VM options\n"));
-                continue;
-            }
-        }
-
-        optmap_t::const_iterator it( m_GlobalOptions.find(optreq) );
-        if (it != m_GlobalOptions.end())
-        {
-            a_rRetOpts << it->second;
-            LogRel2(("... found in global options\n"));
-            continue;
-        }
-
-        LogRel3(("... not found\n"));
     }
 
Index: /trunk/src/VBox/NetworkServices/Dhcpd/Config.h
===================================================================
--- /trunk/src/VBox/NetworkServices/Dhcpd/Config.h	(revision 79799)
+++ /trunk/src/VBox/NetworkServices/Dhcpd/Config.h	(revision 79800)
@@ -35,10 +35,158 @@
 
 /**
+ * Base configuration
+ *
+ * @author bird (2019-07-15)
+ */
+class ConfigLevelBase
+{
+private:
+    /** DHCP options. */
+    optmap_t        m_Options;
+    /** Minimum lease time, zero means try next level up. */
+    uint32_t        m_secMinLeaseTime;
+    /** Default lease time, zero means try next level up. */
+    uint32_t        m_secDefaultLeaseTime;
+    /** Maximum lease time, zero means try next level up. */
+    uint32_t        m_secMaxLeaseTime;
+
+public:
+    ConfigLevelBase()
+        : m_Options()
+        , m_secMinLeaseTime(0)
+        , m_secDefaultLeaseTime(0)
+        , m_secMaxLeaseTime(0)
+    { }
+
+    virtual ~ConfigLevelBase()
+    { }
+
+    virtual void        initFromXml(xml::ElementNode const *pElm, bool fStrict);
+    virtual const char *getType() const RT_NOEXCEPT = 0;
+    virtual const char *getName() const RT_NOEXCEPT = 0;
+
+    /**
+     * Tries to find DHCP option @a bOpt, returning an success indicator and
+     * iterator to the result.
+     */
+    bool            findOption(uint8_t bOpt, optmap_t::const_iterator &a_rItRet) const RT_NOEXCEPT
+    {
+        a_rItRet = m_Options.find(bOpt);
+        return a_rItRet != m_Options.end();
+    }
+
+protected:
+    void            i_parseOption(const xml::ElementNode *pElmOption);
+    virtual void    i_parseChild(const xml::ElementNode *pElmChild, bool fStrict);
+};
+
+
+/**
+ * Global config
+ */
+class GlobalConfig : public ConfigLevelBase
+{
+public:
+    GlobalConfig()
+        : ConfigLevelBase()
+    { }
+    void initFromXml(xml::ElementNode const *pElm, bool fStrict) RT_OVERRIDE;
+    const char *getType() const RT_NOEXCEPT RT_OVERRIDE { return "global"; }
+    const char *getName() const RT_NOEXCEPT RT_OVERRIDE { return "GlobalConfig"; }
+};
+
+
+#if 0 /* later */
+/**
+ * Group membership condition.
+ */
+class GroupConditionBase
+{
+protected:
+    /** The value. */
+    RTCString   m_strValue;
+
+public:
+
+};
+#endif
+
+
+/**
+ * Group config
+ */
+class GroupConfig : public ConfigLevelBase
+{
+public:
+    /** The group name. */
+    RTCString       m_strName;
+
+public:
+    GroupConfig()
+        : ConfigLevelBase()
+    {
+    }
+
+    void initFromXml(xml::ElementNode const *pElm, bool fStrict) RT_OVERRIDE;
+    const char *getType() const RT_NOEXCEPT RT_OVERRIDE { return "group"; }
+    const char *getName() const RT_NOEXCEPT RT_OVERRIDE { return m_strName.c_str(); }
+
+    /** @name Accessors
+     * @{ */
+    RTCString const    &getGroupName() const RT_NOEXCEPT        { return m_strName; }
+    /** @} */
+
+protected:
+    void                i_parseChild(const xml::ElementNode *pElmChild, bool fStrict) RT_OVERRIDE;
+    /** Used to name unnamed groups. */
+    static uint32_t     s_uGroupNo;
+};
+
+
+/**
+ * Host (MAC address) specific configuration.
+ */
+class HostConfig : public ConfigLevelBase
+{
+protected:
+    /** The MAC address. */
+    RTMAC           m_MACAddress;
+    /** Name annotating the entry. */
+    RTCString       m_strName;
+    /** Fixed address assignment when m_fHaveFixedAddress is true. */
+    RTNETADDRIPV4   m_FixedAddress;
+    /** Set if we have a fixed address asignment. */
+    bool            m_fHaveFixedAddress;
+
+public:
+    HostConfig()
+        : ConfigLevelBase()
+        , m_fHaveFixedAddress(false)
+    {
+        RT_ZERO(m_MACAddress);
+        RT_ZERO(m_FixedAddress);
+    }
+
+    void initFromXml(xml::ElementNode const *pElm, bool fStrict) RT_OVERRIDE;
+    const char *getType() const RT_NOEXCEPT RT_OVERRIDE { return "host"; }
+    const char *getName() const RT_NOEXCEPT RT_OVERRIDE { return m_strName.c_str(); }
+
+    /** @name Accessors
+     * @{ */
+    RTMAC const        &getMACAddress() const RT_NOEXCEPT       { return m_MACAddress; }
+    /** @} */
+};
+
+
+/**
  * DHCP server configuration.
  */
 class Config
 {
-    /** @todo XXX: also store fixed address assignments, etc? */
-    typedef std::map<RTMAC, optmap_t> vmmap_t;
+    /** Group configuration map. */
+    typedef std::map<RTCString, GroupConfig * > GroupConfigMap;
+    /** Host configuration map. */
+    typedef std::map<RTMAC,     HostConfig *  > HostConfigMap;
+
 
     RTCString       m_strHome;          /**< path of ~/.VirtualBox or equivalent, */
@@ -59,13 +207,10 @@
 
 
-    optmap_t        m_GlobalOptions;    /**< Global DHCP option. */
-    vmmap_t         m_VMMap;            /**< Per MAC address (VM) DHCP options. */
-    /** @todo r=bird: optmap_t is too narrow for adding configuration options such
-     *        as max-lease-time, min-lease-time, default-lease-time and such like
-     *        that does not translate directly to any specific DHCP option. */
-    /** @todo r=bird: Additionally, I'd like to have a more generic option groups
-     *        that fits inbetween m_VMMap (mac based) and m_GlobalOptions.
-     *        Pattern/wildcard matching on MAC address, possibly also client ID,
-     *        vendor class and user class, including simple lists of these. */
+    /** The global configuration. */
+    GlobalConfig    m_GlobalConfig;
+    /** The group configurations, indexed by group name. */
+    GroupConfigMap  m_GroupConfigs;
+    /** The host configurations, indexed by MAC address. */
+    HostConfigMap   m_HostConfigs;
 
     /** Set if we've initialized the log already (via command line). */
@@ -109,20 +254,18 @@
     /** @} */
 
-    optmap_t           &getOptions(optmap_t &a_rRetOpts, const OptParameterRequest &reqOpts, const ClientId &id,
-                                   const OptVendorClassId &idVendorClass = OptVendorClassId(),
-                                   const OptUserClassId &idUserClass = OptUserClassId()) const;
+    /** Configuration vector. */
+    typedef std::vector<ConfigLevelBase const *> ConfigVec;
+    ConfigVec          &getConfigsForClient(ConfigVec &a_rRetConfigs, const ClientId &a_ridClient,
+                                            const OptVendorClassId &a_ridVendorClass,
+                                            const OptUserClassId &a_ridUserClass) const;
+    optmap_t           &getOptionsForClient(optmap_t &a_rRetOpts, const OptParameterRequest &a_rReqOpts,
+                                            ConfigVec &a_rConfigs) const;
 
 private:
     /** @name Configuration file reading and parsing
      * @{ */
-    static Config      *i_read(const char *pszFileName) RT_NOEXCEPT;
-    void                i_parseConfig(const xml::ElementNode *root);
-    void                i_parseServer(const xml::ElementNode *server);
-    void                i_parseGlobalOptions(const xml::ElementNode *options);
-    void                i_parseVMConfig(const xml::ElementNode *config);
-    void                i_parseOption(const xml::ElementNode *option, optmap_t &optmap);
-
-    static void         i_getIPv4AddrAttribute(const xml::ElementNode *pElm, const char *pcszAttrName, PRTNETADDRIPV4 pAddr);
-    static void         i_getMacAddressAttribute(const xml::ElementNode *pElm, const char *pszAttrName, PRTMAC pMacAddr);
+    static Config      *i_read(const char *pszFilename, bool fStrict) RT_NOEXCEPT;
+    void                i_parseConfig(const xml::ElementNode *pElmRoot, bool fStrict);
+    void                i_parseServer(const xml::ElementNode *pElmServer, bool fStrict);
     /** @} */
 };
Index: /trunk/src/VBox/NetworkServices/Dhcpd/DHCPD.cpp
===================================================================
--- /trunk/src/VBox/NetworkServices/Dhcpd/DHCPD.cpp	(revision 79799)
+++ /trunk/src/VBox/NetworkServices/Dhcpd/DHCPD.cpp	(revision 79800)
@@ -242,4 +242,7 @@
         return NULL;
 
+    Config::ConfigVec vecConfigs;
+    m_pConfig->getConfigsForClient(vecConfigs, req.clientId(), OptVendorClassId(req), OptUserClassId(req));
+
     Binding *b = m_db.allocateBinding(req);
     if (b == NULL)
@@ -270,8 +273,7 @@
     reply->addOption(OptLeaseTime(b->leaseTime()));
 
-
     OptParameterRequest optlist(req);
     optmap_t replyOptions;
-    reply->addOptions(m_pConfig->getOptions(replyOptions, optlist, req.clientId()));
+    reply->addOptions(m_pConfig->getOptionsForClient(replyOptions, optlist, vecConfigs));
 
     // reply->maybeUnicast(req); /** @todo XXX: we reject ciaddr != 0 above */
@@ -307,4 +309,6 @@
     }
 
+    Config::ConfigVec vecConfigs;
+    m_pConfig->getConfigsForClient(vecConfigs, req.clientId(), OptVendorClassId(req), OptUserClassId(req));
 
     Binding *b = m_db.allocateBinding(req);
@@ -314,5 +318,4 @@
     }
 
-
     std::unique_ptr<DhcpServerMessage> ack(i_createMessage(RTNET_DHCP_MT_ACK, req));
 
@@ -325,5 +328,5 @@
     OptParameterRequest optlist(req);
     optmap_t replyOptions;
-    ack->addOptions(m_pConfig->getOptions(replyOptions, optlist, req.clientId()));
+    ack->addOptions(m_pConfig->getOptionsForClient(replyOptions, optlist, vecConfigs));
 
     /** @todo r=bird: Sec 9.9 in rfc-2132 indicates the server only sends this in NACKs. Test code? */
@@ -351,10 +354,12 @@
         return NULL;
 
-    const OptParameterRequest params(req);
-    if (!params.present())
-        return NULL;
-
+    OptParameterRequest optlist(req);
+    if (!optlist.present())
+        return NULL;
+
+    Config::ConfigVec vecConfigs;
     optmap_t info;
-    m_pConfig->getOptions(info, params, req.clientId());
+    m_pConfig->getOptionsForClient(info, optlist, m_pConfig->getConfigsForClient(vecConfigs, req.clientId(),
+                                                                                 OptVendorClassId(req), OptUserClassId(req)));
     if (info.empty())
         return NULL;
