Index: /trunk/include/VBox/log.h
===================================================================
--- /trunk/include/VBox/log.h	(revision 79731)
+++ /trunk/include/VBox/log.h	(revision 79732)
@@ -371,4 +371,14 @@
     /** Main group, IDataStream. */
     LOG_GROUP_MAIN_DATASTREAM,
+    /** Main group, IDHCPConfig. */
+    LOG_GROUP_MAIN_DHCPCONFIG,
+    /** Main group, IDHCPGlobalConfig. */
+    LOG_GROUP_MAIN_DHCPGLOBALCONFIG,
+    /** Main group, IDHCPGroupCondition. */
+    LOG_GROUP_MAIN_DHCPGROUPCONDITION,
+    /** Main group, IDHCPGroupConfig. */
+    LOG_GROUP_MAIN_DHCPGROUPCONFIG,
+    /** Main group, IDHCPIndividualConfig. */
+    LOG_GROUP_MAIN_DHCPINDIVIDUALCONFIG,
     /** Main group, IDHCPServer. */
     LOG_GROUP_MAIN_DHCPSERVER,
@@ -892,4 +902,9 @@
     "MAIN_DATAMODEL", \
     "MAIN_DATASTREAM", \
+    "MAIN_DHCPCONFIG", \
+    "MAIN_DHCPGLOBALCONFIG", \
+    "MAIN_DHCPGROUPCONDITION", \
+    "MAIN_DHCPGROUPCONFIG", \
+    "MAIN_DHCPINDIVIDUALCONFIG", \
     "MAIN_DHCPSERVER", \
     "MAIN_DIRECTORY", \
Index: /trunk/include/VBox/settings.h
===================================================================
--- /trunk/include/VBox/settings.h	(revision 79731)
+++ /trunk/include/VBox/settings.h	(revision 79732)
@@ -338,8 +338,8 @@
 {
     DhcpOptValue();
-    DhcpOptValue(const com::Utf8Str &aText, DhcpOptEncoding_T aEncoding = DhcpOptEncoding_Legacy);
-
-    com::Utf8Str text;
-    DhcpOptEncoding_T encoding;
+    DhcpOptValue(const com::Utf8Str &aText, DHCPOptionEncoding_T aEncoding = DHCPOptionEncoding_Legacy);
+
+    com::Utf8Str            strValue;
+    DHCPOptionEncoding_T    enmEncoding;
 };
 
@@ -349,18 +349,25 @@
 typedef DhcpOptionMap::const_iterator DhcpOptConstIterator;
 
-typedef struct VmNameSlotKey
-{
-    VmNameSlotKey(const com::Utf8Str& aVmName, LONG aSlot);
-
-    bool operator<(const VmNameSlotKey& that) const;
-
-    const com::Utf8Str VmName;
-    LONG      Slot;
-} VmNameSlotKey;
-
-typedef std::map<VmNameSlotKey, DhcpOptionMap> VmSlot2OptionsMap;
-typedef VmSlot2OptionsMap::value_type VmSlot2OptionsPair;
-typedef VmSlot2OptionsMap::iterator VmSlot2OptionsIterator;
-typedef VmSlot2OptionsMap::const_iterator VmSlot2OptionsConstIterator;
+struct DHCPConfig
+{
+    DHCPConfig();
+
+    DhcpOptionMap           OptionMap;
+    uint32_t                secMinLeaseTime;
+    uint32_t                secDefaultLeaseTime;
+    uint32_t                secMaxLeaseTime;
+};
+
+struct DHCPIndividualConfig : DHCPConfig
+{
+    DHCPIndividualConfig();
+
+    com::Utf8Str            strMACAddress;
+    com::Utf8Str            strVMName;
+    ULONG                   uSlot;
+    com::Utf8Str            strFixedAddress;
+};
+
+typedef std::map<com::Utf8Str, DHCPIndividualConfig> DHCPIndividualConfigMap;
 
 struct DHCPServer
@@ -368,11 +375,11 @@
     DHCPServer();
 
-    com::Utf8Str    strNetworkName,
-                    strIPAddress,
-                    strIPLower,
-                    strIPUpper;
-    bool            fEnabled;
-    DhcpOptionMap   GlobalDhcpOptions;
-    VmSlot2OptionsMap VmSlot2OptionsM;
+    com::Utf8Str            strNetworkName;
+    com::Utf8Str            strIPAddress;
+    com::Utf8Str            strIPLower;
+    com::Utf8Str            strIPUpper;
+    bool                    fEnabled;
+    DHCPConfig              GlobalConfig;
+    DHCPIndividualConfigMap IndividualConfigs;
 };
 
@@ -409,6 +416,4 @@
 
     void readMachineRegistry(const xml::ElementNode &elmMachineRegistry);
-    void readDHCPServers(const xml::ElementNode &elmDHCPServers);
-    void readDhcpOptions(DhcpOptionMap& map, const xml::ElementNode& options);
     void readNATNetworks(const xml::ElementNode &elmNATNetworks);
 
@@ -427,4 +432,8 @@
     void buildUSBDeviceSources(xml::ElementNode &elmParent, const USBDeviceSourcesList &ll);
     void readUSBDeviceSources(const xml::ElementNode &elmDeviceSources, USBDeviceSourcesList &ll);
+    void buildDHCPServers(xml::ElementNode &elmDHCPServers, DHCPServersList const &ll);
+    void buildDHCPOptions(xml::ElementNode &elmOptions, DHCPConfig const &rConfig, bool fIgnoreSubnetMask);
+    void readDHCPServers(const xml::ElementNode &elmDHCPServers);
+    void readDHCPOptions(DHCPConfig &rConfig, const xml::ElementNode &elmOptions, bool fIgnoreSubnetMask);
     bool convertGuiProxySettings(const com::Utf8Str &strUIProxySettings);
 };
Index: /trunk/src/VBox/Main/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Main/Makefile.kmk	(revision 79731)
+++ /trunk/src/VBox/Main/Makefile.kmk	(revision 79732)
@@ -550,4 +550,5 @@
 	src-server/DataStreamImpl.cpp \
 	src-server/DHCPServerImpl.cpp \
+	src-server/DHCPConfigImpl.cpp \
 	src-server/NetworkServiceRunner.cpp \
 	src-server/NATNetworkImpl.cpp \
Index: /trunk/src/VBox/Main/idl/VirtualBox.xidl
===================================================================
--- /trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 79731)
+++ /trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 79732)
@@ -1824,8 +1824,29 @@
 
   <enum
-      name="DhcpOptEncoding"
-      uuid="88ea6d70-8648-4871-ba30-1f49c61cfaa2">
+      name="DHCPOptionEncoding"
+      uuid="84b6d460-2838-4682-c0d6-ef5b573ef28a">
     <const name="Legacy" value="0"/>
-    <const name="Hex" value="1"/>
+    <const name="Hex"    value="1"/>
+  </enum>
+
+  <enum
+      name="DHCPConfigScope"
+      uuid="151df7cd-e596-42dd-235a-b164a781c099">
+    <const name="Global"      value="0"><desc><link to="IDHCPServer::globalConfig"/></desc></const>
+    <const name="MachineNIC"  value="1"><desc><link to="IDHCPServer::individualConfigs"/></desc></const>
+    <const name="MAC"         value="2"><desc><link to="IDHCPServer::individualConfigs"/></desc></const>
+    <const name="Group"       value="3"><desc><link to="IDHCPServer::groupConfigs"/></desc></const>
+  </enum>
+
+  <enum
+    name="DHCPGroupFilterType"
+    uuid="0639cd66-94b7-41ef-18ac-485636e46d97"
+    >
+    <const name="MAC"                     value="0"><desc>MAC address</desc></const>
+    <const name="MACWildcard"             value="1"><desc>MAC address wildcard pattern.</desc></const>
+    <const name="vendorClassID"           value="2"><desc>Vendor class ID</desc></const>
+    <const name="vendorClassIDWildcard"   value="3"><desc>Vendor class ID wildcard pattern.</desc></const>
+    <const name="userClassID"             value="4"><desc>User class ID</desc></const>
+    <const name="userClassIDWildcard"     value="5"><desc>User class ID wildcard pattern.</desc></const>
   </enum>
 
@@ -1834,5 +1855,5 @@
     uuid="ea2d467f-b6c2-4b9a-8eb5-6e2f275dd72e"
     wsmap="managed"
-    reservedMethods="1" reservedAttributes="6"
+    reservedMethods="0" reservedAttributes="3"
     >
     <desc>
@@ -1881,4 +1902,5 @@
 
     <method name="addGlobalOption">
+      <desc>6.0 legacy, will be removed in 6.1.</desc>
       <param name="option" type="DhcpOpt" dir="in"/>
       <param name="value" type="wstring" dir="in" />
@@ -1886,27 +1908,33 @@
 
     <method name="removeGlobalOption">
-      <desc>
-        removes the specified option
-        <result name="E_INVALIDARG">
-          invalid option id supplied
-        </result>
-      </desc>
+      <desc>6.0 legacy, will be removed in 6.1.</desc>
       <param name="option" type="DhcpOpt" dir="in"/>
     </method>
 
     <method name="removeGlobalOptions">
-      <desc>
-        removes all global options
-        <result name="E_FAIL">
-          failed to remove global options
-        </result>
-      </desc>
+      <desc>Legacy interface, will be removed in 6.1.</desc>
     </method>
 
     <!-- string in format: "option_id:value" -->
-    <attribute name="globalOptions" type="wstring" safearray="yes" readonly="yes"/>
+    <attribute name="globalOptions" type="wstring" safearray="yes" readonly="yes">
+      <desc>Legacy interface, will be removed in 6.1.</desc>
+    </attribute>
 
     <!-- string in format: "[vm name]:slot" -->
-    <attribute name="vmConfigs" type="wstring" safearray="yes" readonly="yes"/>
+    <attribute name="vmConfigs" type="wstring" safearray="yes" readonly="yes">
+      <desc>Legacy interface, will be removed in 6.1.</desc>
+    </attribute>
+
+    <attribute name="globalConfig" type="IDHCPGlobalConfig" readonly="yes">
+      <desc>Global configuration that applies to all clients.</desc>
+    </attribute>
+
+    <attribute name="groupConfigs" type="IDHCPGroupConfig" safearray="yes" readonly="yes">
+      <desc>Configuration groups that applies to selected clients, selection is flexible.</desc>
+    </attribute>
+
+    <attribute name="individualConfigs" type="IDHCPIndividualConfig" safearray="yes" readonly="yes">
+      <desc>Individual NIC configurations either by MAC address or VM + NIC number.</desc>
+    </attribute>
 
     <!-- VM-slot settings
@@ -1931,4 +1959,5 @@
     -->
     <method name="addVmSlotOption">
+      <desc>6.0 legacy, will be removed in 6.1.</desc>
       <param name="vmname" type="wstring" dir="in"/>
       <param name="slot" type="long" dir="in"/>
@@ -1938,10 +1967,5 @@
 
     <method name="removeVmSlotOption">
-      <desc>
-        removes the specified option
-        <result name="E_INVALIDARG">
-          invalid VM, slot, or option id supplied
-        </result>
-      </desc>
+      <desc>6.0 legacy, will be removed in 6.1.</desc>
       <param name="vmname" type="wstring" dir="in"/>
       <param name="slot" type="long" dir="in"/>
@@ -1950,10 +1974,5 @@
 
     <method name="removeVmSlotOptions">
-      <desc>
-        removes all option for the specified adapter
-        <result name="E_INVALIDARG">
-          invalid VM or slot supplied
-        </result>
-      </desc>
+      <desc>6.0 legacy, will be removed in 6.1.</desc>
       <param name="vmname" type="wstring" dir="in"/>
       <param name="slot" type="long" dir="in"/>
@@ -1962,4 +1981,5 @@
     <!-- returns array of strings in format: "option_id:value" for a given pair (vm, slot) -->
     <method name="getVmSlotOptions">
+      <desc>6.0 legacy, will be removed in 6.1.</desc>
       <param name="vmname" type="wstring" dir="in"/>
       <param name="slot" type="long" dir="in"/>
@@ -1969,4 +1989,5 @@
     <!-- Returns options by MAC address -->
     <method name="getMacOptions">
+      <desc>6.0 legacy, will be removed in 6.1.</desc>
       <param name="mac" type="wstring" dir="in"/>
       <param name="option" type="wstring" safearray="yes" dir="return"/>
@@ -2074,11 +2095,224 @@
     </method>
 
+    <method name="getConfig">
+      <desc>
+        Gets or adds a configuration.
+      </desc>
+      <param name="scope"  dir="in" type="DHCPConfigScope">
+        <desc>The kind of configuration being sought or added.</desc>
+      </param>
+      <param name="name"   dir="in" type="wstring">
+        <desc>
+          Meaning depends on the @a scope:
+            - Ignored when the @a scope is <link to="DHCPConfigScope::Global"/>.
+            - A VM name or UUID for <link to="DHCPConfigScope::MachineNIC"/>.
+            - A MAC address for <link to="DHCPConfigScope::MAC"/>.
+            - A group name for <link to="DHCPConfigScope::Group"/>.
+        </desc>
+      </param>
+      <param name="slot"   dir="in" type="unsigned long">
+        <desc>The NIC slot when @a scope is set to <link to="DHCPConfigScope::MachineNIC"/>,
+          must be zero for all other scope values.
+        </desc>
+      </param>
+      <param name="mayAdd" dir="in" type="boolean">
+        <desc>
+          Set to @c TRUE if the configuration should be added if not found.
+          If set to @c FALSE the method will fail with VBOX_E_OBJECT_NOT_FOUND.
+        </desc>
+      </param>
+      <param name="config" dir="return" type="IDHCPConfig">
+        <desc>The requested configuration.</desc>
+      </param>
+    </method>
+
     <!-- @todo Add variant of queryLeaseByMac that goes by VM name and optionally slot. -->
-    <!-- @todo Add min/default/max lease time settings -->
     <!-- @todo Add lease database attribute (readonly) -->
-    <!-- @todo Add methods for complex group operations, that includes lists of mac address,
-               client ids, vendor/user class ids as selection criteria -->
-    <!-- @todo Add fake DHCP option for assigning fixed IP addresses.  Figure out a way to
-               do it for the host only trunk interface too (possible special group operation). -->
+
+  </interface>
+
+  <interface
+    name="IDHCPConfig" extends="$unknown"
+    uuid="38fdb664-f65e-4905-ee56-a8a912871bbf"
+    wsmap="managed"
+    reservedMethods="8" reservedAttributes="16"
+    >
+    <desc>
+      The DHCP server has several configuration levels: global, group and
+      individual MAC.  This interface implements the settings common to
+      each level.
+    </desc>
+
+    <attribute name="scope" type="DHCPConfigScope" readonly="yes">
+      <desc>
+        Indicates the kind of config this is (mostly for IDHCPIndividualConfig).
+      </desc>
+    </attribute>
+
+    <attribute name="minLeaseTime" type="unsigned long" readonly="no">
+      <desc>The minimum lease time in seconds, ignored if zero.</desc>
+    </attribute>
+    <attribute name="defaultLeaseTime" type="unsigned long" readonly="no">
+      <desc>The default lease time in seconds, ignored if zero.</desc>
+    </attribute>
+    <attribute name="maxLeaseTime" type="unsigned long" readonly="no">
+      <desc>The maximum lease time in seconds, ignored if zero.</desc>
+    </attribute>
+
+    <method name="setOption">
+      <desc>
+        Sets a DHCP option.
+      </desc>
+      <param name="option"    dir="in" type="DhcpOpt">
+        <desc>The DHCP option.</desc>
+      </param>
+      <param name="encoding"  dir="in" type="DHCPOptionEncoding">
+        <desc>The value encoding.</desc>
+      </param>
+      <param name="value"     dir="in" type="wstring">
+        <desc>The DHCP option value.  The exact format depends on the DHCP
+          option and should be documented in <link to="DhcpOpt"/>.
+        </desc>
+      </param>
+    </method>
+
+    <method name="removeOption">
+      <desc>Removes the given DHCP option.</desc>
+      <param name="option"    dir="in" type="DhcpOpt"/>
+    </method>
+
+    <method name="removeAllOptions">
+      <desc>
+        Removes all the options.
+
+        One exception here is the DhcpOpt_SubnetMask option in the global scope
+        that is linked to the <link to="IDHCPServer::networkMask"/> attribute
+        and therefore cannot be removed.
+      </desc>
+    </method>
+
+    <method name="getOption">
+      <desc>Gets the value of a single DHCP option.</desc>
+      <param name="option"    dir="in" type="DhcpOpt">
+        <desc>The DHCP option being sought.</desc>
+      </param>
+      <param name="encoding"  dir="out" type="DHCPOptionEncoding">
+        <desc>The value encoding.</desc>
+      </param>
+      <param name="value"     dir="return" type="wstring">
+        <desc>The value of the requested DHCP option.</desc>
+      </param>
+    </method>
+
+    <method name="getAllOptions">
+      <desc>Gets all DHCP options and their values</desc>
+      <param name="options"   dir="out" type="DhcpOpt" safearray="yes">
+        <desc>Array containing the DHCP option numbers.</desc>
+      </param>
+      <param name="encodings" dir="out" type="DHCPOptionEncoding" safearray="yes">
+        <desc>Array of value encodings that runs parallel to @a options.</desc>
+      </param>
+      <param name="values"    dir="return" type="wstring" safearray="yes">
+        <desc>Array of values that runs parallel to @a options and @a encodings.</desc>
+      </param>
+    </method>
+  </interface>
+
+  <interface
+    name="IDHCPGlobalConfig" extends="IDHCPConfig"
+    uuid="5d7bb1ea-27ed-4e01-3859-4302dd55f796"
+    wsmap="managed"
+    reservedMethods="4" reservedAttributes="4"
+    >
+    <desc>
+      The global DHCP server configuration, see <link to="IDHCPServer::globalConfig"/>.
+    </desc>
+
+    <attribute name="midlDoesNotLikeEmptyInterfaces" readonly="yes" type="boolean"/>
+  </interface>
+
+  <interface
+    name="IDHCPGroupCondition" extends="$unknown"
+    uuid="5ca9e537-5a1d-43f1-6f27-6a0db298a9a8"
+    wsmap="managed"
+    reservedMethods="3" reservedAttributes="3"
+    >
+    <attribute name="inclusive" type="boolean" readonly="no">
+      <desc>Whether this is an inclusive or exclusive group membership condition</desc>
+    </attribute>
+    <attribute name="type" type="DHCPGroupFilterType" readonly="no">
+      <desc>Defines how the <link to="IDHCPGroupCondition::value"/> is interpreted.</desc>
+    </attribute>
+    <attribute name="value" type="wstring" readonly="no">
+      <desc>The condition value.</desc>
+    </attribute>
+    <method name="remove">
+      <desc>Remove this condition from the group.</desc>
+    </method>
+  </interface>
+
+  <interface
+    name="IDHCPGroupConfig" extends="IDHCPConfig"
+    uuid="406a5eca-d032-4f65-a882-6bb626a424a5"
+    wsmap="managed"
+    reservedMethods="8" reservedAttributes="8"
+    >
+    <desc>
+      A configuration that applies to a group of NICs.
+    </desc>
+
+    <attribute name="name" type="wstring" readonly="no">
+      <desc>The group name.</desc>
+    </attribute>
+
+    <attribute name="conditions" type="IDHCPGroupCondition" safearray="yes" readonly="yes">
+      <desc>
+        Group membership conditions.
+
+        Add new conditions by calling <link to="IDHCPGroupConfig::addCondition"/>
+        and use <link to="IDHCPGroupCondition::remove"/> to remove.
+      </desc>
+    </attribute>
+
+    <method name="addCondition">
+      <param name="inclusive"  dir="in" type="boolean"/>
+      <param name="type"       dir="in" type="DHCPGroupFilterType"/>
+      <param name="value"      dir="in" type="wstring"/>
+    </method>
+
+  </interface>
+
+  <interface
+    name="IDHCPIndividualConfig" extends="IDHCPConfig"
+    uuid="c40c2b86-73a5-46cc-8227-93fe57d006a6"
+    wsmap="managed"
+    reservedMethods="8" reservedAttributes="8"
+    >
+    <desc>
+      Configuration for a single NIC, either given directly by MAC address or by
+      VM + adaptor slot number.
+    </desc>
+
+    <attribute name="MACAddress" type="wstring" readonly="yes">
+      <desc>
+        The MAC address.  If a <link to="DHCPConfigScope::MachineNIC"/> config, this
+        will be queried via the VM ID.
+      </desc>
+    </attribute>
+
+    <attribute name="machineId" type="uuid" mod="string" readonly="yes">
+      <desc>
+        The virtual machine ID if a <link to="DHCPConfigScope::MachineNIC"/> config,
+        null UUID for <link to="DHCPConfigScope::MAC"/>.
+      </desc>
+    </attribute>
+
+    <attribute name="slot" type="unsigned long" readonly="yes">
+      <desc>The NIC slot number of the VM if a <link to="DHCPConfigScope::MachineNIC"/> config.</desc>
+    </attribute>
+
+    <attribute name="fixedAddress" type="wstring" readonly="no">
+      <desc>Fixed IPv4 address assignment, dynamic if empty.</desc>
+    </attribute>
 
   </interface>
Index: /trunk/src/VBox/Main/include/DHCPConfigImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/DHCPConfigImpl.h	(revision 79732)
+++ /trunk/src/VBox/Main/include/DHCPConfigImpl.h	(revision 79732)
@@ -0,0 +1,318 @@
+/* $Id$ */
+/** @file
+ * VirtualBox Main - IDHCPConfig, IDHCPConfigGlobal, IDHCPConfigGroup, IDHCPConfigIndividual header.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef MAIN_INCLUDED_DHCPServerConfig_h
+#define MAIN_INCLUDED_DHCPServerConfig_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "DHCPGlobalConfigWrap.h"
+#include "DHCPGroupConfigWrap.h"
+#include "DHCPIndividualConfigWrap.h"
+#include <VBox/settings.h>
+
+
+class DHCPServer;
+
+
+/**
+ * Base class for the a DHCP configration layer.
+ *
+ * This does not inherit from DHCPConfigWrap because its children need to
+ * inherit from children of DHCPConfigWrap, which smells like trouble and thus
+ * wasn't even attempted.  Instead, we have a hack for passing a pointer that we
+ * can call setError and such on.
+ */
+class DHCPConfig
+{
+protected:
+    /** Config scope (global, group, vm+nic, mac).  */
+    DHCPConfigScope_T const m_enmScope;
+    /** Minimum lease time. */
+    ULONG                   m_secMinLeaseTime;
+    /** Default lease time. */
+    ULONG                   m_secDefaultLeaseTime;
+    /** Maximum lease time. */
+    ULONG                   m_secMaxLeaseTime;
+    /** DHCP option map. */
+    settings::DhcpOptionMap m_OptionMap;
+    /** The DHCP server parent (weak).   */
+    DHCPServer * const      m_pParent;
+    /** The DHCP server parent (weak).   */
+    VirtualBox * const      m_pVirtualBox;
+private:
+    /** For setError and such. */
+    VirtualBoxBase * const  m_pHack;
+
+protected:
+    /** @name Constructors and destructors.
+     * @{ */
+    DHCPConfig(DHCPConfigScope_T a_enmScope, VirtualBoxBase *a_pHack)
+        : m_enmScope(a_enmScope), m_secMinLeaseTime(0), m_secDefaultLeaseTime(0), m_secMaxLeaseTime(0), m_OptionMap()
+        , m_pParent(NULL), m_pVirtualBox(NULL), m_pHack(a_pHack)
+    {}
+    DHCPConfig();
+    virtual ~DHCPConfig()
+    {}
+    HRESULT i_initWithDefaults(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent);
+    HRESULT i_initWithSettings(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent, const settings::DHCPConfig &rConfig);
+    /** @} */
+
+    /** @name IDHCPConfig properties
+     * @{ */
+    HRESULT i_getScope(DHCPConfigScope_T *aScope);
+    HRESULT i_getMinLeaseTime(ULONG *aMinLeaseTime);
+    HRESULT i_setMinLeaseTime(ULONG aMinLeaseTime);
+    HRESULT i_getDefaultLeaseTime(ULONG *aDefaultLeaseTime);
+    HRESULT i_setDefaultLeaseTime(ULONG aDefaultLeaseTime);
+    HRESULT i_getMaxLeaseTime(ULONG *aMaxLeaseTime);
+    HRESULT i_setMaxLeaseTime(ULONG aMaxLeaseTime);
+    /** @} */
+
+public:
+    /** @name IDHCPConfig methods
+     * @note public because the DHCPServer needs them for 6.0 interfaces.
+     * @todo Make protected again when IDHCPServer is cleaned up.
+     * @{ */
+    virtual HRESULT i_setOption(DhcpOpt_T aOption, DHCPOptionEncoding_T aEncoding, const com::Utf8Str &aValue);
+
+    virtual HRESULT i_removeOption(DhcpOpt_T aOption);
+    virtual HRESULT i_removeAllOptions();
+    HRESULT         i_getOption(DhcpOpt_T aOption, DHCPOptionEncoding_T *aEncoding, com::Utf8Str &aValue);
+    HRESULT         i_getAllOptions(std::vector<DhcpOpt_T> &aOptions, std::vector<DHCPOptionEncoding_T> &aEncodings,
+                                    std::vector<com::Utf8Str> &aValues);
+    /** @} */
+
+protected:
+    HRESULT             i_doWriteConfig();
+
+public:
+    HRESULT             i_saveSettings(settings::DHCPConfig &a_rDst);
+    DHCPConfigScope_T   i_getScope() const RT_NOEXCEPT { return m_enmScope; }
+    virtual void        i_writeDhcpdConfig(xml::ElementNode *pElm);
+};
+
+
+/**
+ * Global DHCP configuration.
+ */
+class DHCPGlobalConfig : public DHCPGlobalConfigWrap, public DHCPConfig
+{
+public:
+    /** @name Constructors and destructors.
+     * @{ */
+    DHCPGlobalConfig()
+        : DHCPConfig(DHCPConfigScope_Global, this)
+    { }
+    HRESULT FinalConstruct()
+    {
+        return BaseFinalConstruct();
+    }
+    void    FinalRelease()
+    {
+        uninit();
+        BaseFinalRelease();
+    }
+    HRESULT initWithDefaults(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent);
+    HRESULT initWithSettings(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent, const settings::DHCPConfig &rConfig);
+    void    uninit();
+    /** @} */
+
+    HRESULT i_saveSettings(settings::DHCPConfig &a_rDst);
+    HRESULT i_getNetworkMask(com::Utf8Str &a_rDst);
+    HRESULT i_setNetworkMask(const com::Utf8Str &a_rSrc);
+
+protected:
+    /** @name wrapped IDHCPConfig properties
+     * @{ */
+    HRESULT getScope(DHCPConfigScope_T *aScope) RT_OVERRIDE             { return i_getScope(aScope); }
+    HRESULT getMinLeaseTime(ULONG *aMinLeaseTime) RT_OVERRIDE           { return i_getMinLeaseTime(aMinLeaseTime); }
+    HRESULT setMinLeaseTime(ULONG aMinLeaseTime) RT_OVERRIDE            { return i_setMinLeaseTime(aMinLeaseTime); }
+    HRESULT getDefaultLeaseTime(ULONG *aDefaultLeaseTime) RT_OVERRIDE   { return i_getDefaultLeaseTime(aDefaultLeaseTime); }
+    HRESULT setDefaultLeaseTime(ULONG aDefaultLeaseTime) RT_OVERRIDE    { return i_setDefaultLeaseTime(aDefaultLeaseTime); }
+    HRESULT getMaxLeaseTime(ULONG *aMaxLeaseTime) RT_OVERRIDE           { return i_getMaxLeaseTime(aMaxLeaseTime); }
+    HRESULT setMaxLeaseTime(ULONG aMaxLeaseTime) RT_OVERRIDE            { return i_setMaxLeaseTime(aMaxLeaseTime); }
+    /** @} */
+
+    /** @name wrapped IDHCPConfig methods
+     * @{ */
+    HRESULT setOption(DhcpOpt_T aOption, DHCPOptionEncoding_T aEncoding, const com::Utf8Str &aValue) RT_OVERRIDE
+    {
+        return i_setOption(aOption, aEncoding, aValue);
+    }
+
+    HRESULT removeOption(DhcpOpt_T aOption) RT_OVERRIDE
+    {
+        return i_removeOption(aOption);
+    }
+
+    HRESULT removeAllOptions() RT_OVERRIDE
+    {
+        return i_removeAllOptions();
+    }
+
+    HRESULT getOption(DhcpOpt_T aOption, DHCPOptionEncoding_T *aEncoding, com::Utf8Str &aValue) RT_OVERRIDE
+    {
+        return i_getOption(aOption, aEncoding, aValue);
+    }
+
+    HRESULT getAllOptions(std::vector<DhcpOpt_T> &aOptions, std::vector<DHCPOptionEncoding_T> &aEncodings,
+                          std::vector<com::Utf8Str> &aValues) RT_OVERRIDE
+    {
+        return i_getAllOptions(aOptions, aEncodings, aValues);
+    }
+    /** @} */
+
+public:
+    HRESULT i_setOption(DhcpOpt_T aOption, DHCPOptionEncoding_T aEncoding, const com::Utf8Str &aValue) RT_OVERRIDE;
+    HRESULT i_removeOption(DhcpOpt_T aOption) RT_OVERRIDE;
+    HRESULT i_removeAllOptions() RT_OVERRIDE;
+};
+
+
+/**
+ * Group configuration.
+ */
+class DHCPGroupConfig : public VirtualBoxBase, public DHCPConfig
+{
+    /** @todo later */
+};
+
+
+/**
+ * Individual DHCP configuration.
+ */
+class DHCPIndividualConfig : public DHCPIndividualConfigWrap, public DHCPConfig
+{
+private:
+    /** The MAC address or all zeros. */
+    RTMAC               m_MACAddress;
+    /** The VM ID or all zeros. */
+    com::Guid const     m_idMachine;
+    /** The VM NIC slot number, or ~(ULONG)0. */
+    ULONG const         m_uSlot;
+    /** This is part of a hack to resolve the MAC address for
+     * DHCPConfigScope_MachineNIC instances.  If non-zero, we m_MACAddress is valid.
+     * To deal with the impossibly theoretical scenario that the DHCP server is
+     * being started by more than one thread, this is a version number and not just
+     * a boolean indicator.   */
+    uint32_t volatile   m_uMACAddressResolvedVersion;
+
+    /** The fixed IPv4 address, empty if dynamic. */
+    com::Utf8Str        m_strFixedAddress;
+
+public:
+    /** @name Constructors and destructors.
+     * @{ */
+    DHCPIndividualConfig()
+        : DHCPConfig(DHCPConfigScope_MAC, this)
+        , m_uSlot(~(ULONG)0)
+        , m_uMACAddressResolvedVersion(false)
+    {
+        RT_ZERO(m_MACAddress);
+    }
+    HRESULT FinalConstruct()
+    {
+        return BaseFinalConstruct();
+    }
+    void    FinalRelease()
+    {
+        uninit();
+        BaseFinalRelease();
+    }
+    HRESULT initWithMachineIdAndSlot(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent, com::Guid const &a_idMachine,
+                                     ULONG a_uSlot, uint32_t a_uMACAddressVersion);
+    HRESULT initWithMACAddress(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent, PCRTMAC a_pMACAddress);
+    HRESULT initWithSettingsAndMachineIdAndSlot(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent,
+                                                const settings::DHCPIndividualConfig &rData, com::Guid const &a_idMachine,
+                                                ULONG a_uSlot, uint32_t a_uMACAddressVersion);
+    HRESULT initWithSettingsAndMACAddress(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent,
+                                          const settings::DHCPIndividualConfig &rData, PCRTMAC a_pMACAddress);
+    void    uninit();
+    /** @} */
+
+    /** @name Internal methods that are public for various reasons
+     * @{ */
+    HRESULT             i_saveSettings(settings::DHCPIndividualConfig &a_rDst);
+    RTMAC const        &i_getMACAddress() const RT_NOEXCEPT    { return m_MACAddress; }
+    com::Guid const    &i_getMachineId() const RT_NOEXCEPT     { return m_idMachine; }
+    ULONG               i_getSlot() const RT_NOEXCEPT          { return m_uSlot; }
+    HRESULT             i_getMachineMAC(PRTMAC pMACAddress);
+
+    HRESULT             i_resolveMACAddress(uint32_t uVersion);
+    /** This is used to avoid producing bogus Dhcpd configuration elements. */
+    bool                i_isMACAddressResolved(uint32_t uVersion) const
+    {
+        return m_enmScope != DHCPConfigScope_MachineNIC || (int32_t)(m_uMACAddressResolvedVersion - uVersion) >= 0;
+    }
+    void                i_writeDhcpdConfig(xml::ElementNode *pElm) RT_OVERRIDE;
+    /** @} */
+
+protected:
+    /** @name wrapped IDHCPConfig properties
+     * @{ */
+    HRESULT getScope(DHCPConfigScope_T *aScope) RT_OVERRIDE             { return i_getScope(aScope); }
+    HRESULT getMinLeaseTime(ULONG *aMinLeaseTime) RT_OVERRIDE           { return i_getMinLeaseTime(aMinLeaseTime); }
+    HRESULT setMinLeaseTime(ULONG aMinLeaseTime) RT_OVERRIDE            { return i_setMinLeaseTime(aMinLeaseTime); }
+    HRESULT getDefaultLeaseTime(ULONG *aDefaultLeaseTime) RT_OVERRIDE   { return i_getDefaultLeaseTime(aDefaultLeaseTime); }
+    HRESULT setDefaultLeaseTime(ULONG aDefaultLeaseTime) RT_OVERRIDE    { return i_setDefaultLeaseTime(aDefaultLeaseTime); }
+    HRESULT getMaxLeaseTime(ULONG *aMaxLeaseTime) RT_OVERRIDE           { return i_getMaxLeaseTime(aMaxLeaseTime); }
+    HRESULT setMaxLeaseTime(ULONG aMaxLeaseTime) RT_OVERRIDE            { return i_setMaxLeaseTime(aMaxLeaseTime); }
+    /** @} */
+
+    /** @name wrapped IDHCPConfig methods
+     * @{ */
+    HRESULT setOption(DhcpOpt_T aOption, DHCPOptionEncoding_T aEncoding, const com::Utf8Str &aValue) RT_OVERRIDE
+    {
+        return i_setOption(aOption, aEncoding, aValue);
+    }
+
+    HRESULT removeOption(DhcpOpt_T aOption) RT_OVERRIDE
+    {
+        return i_removeOption(aOption);
+    }
+
+    HRESULT removeAllOptions() RT_OVERRIDE
+    {
+        return i_removeAllOptions();
+    }
+
+    HRESULT getOption(DhcpOpt_T aOption, DHCPOptionEncoding_T *aEncoding, com::Utf8Str &aValue) RT_OVERRIDE
+    {
+        return i_getOption(aOption, aEncoding, aValue);
+    }
+
+    HRESULT getAllOptions(std::vector<DhcpOpt_T> &aOptions, std::vector<DHCPOptionEncoding_T> &aEncodings,
+                          std::vector<com::Utf8Str> &aValues) RT_OVERRIDE
+    {
+        return i_getAllOptions(aOptions, aEncodings, aValues);
+    }
+    /** @} */
+
+    /** @name IDHCPIndividualConfig properties
+     * @{ */
+    HRESULT getMACAddress(com::Utf8Str &aMacAddress) RT_OVERRIDE;
+    HRESULT getMachineId(com::Guid &aId) RT_OVERRIDE;
+    HRESULT getSlot(ULONG *aSlot) RT_OVERRIDE;
+    HRESULT getFixedAddress(com::Utf8Str &aFixedAddress) RT_OVERRIDE;
+    HRESULT setFixedAddress(const com::Utf8Str &aFixedAddress) RT_OVERRIDE;
+    /** @} */
+};
+
+#endif /* !MAIN_INCLUDED_DHCPServerImpl_h */
+
Index: /trunk/src/VBox/Main/include/DHCPServerImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/DHCPServerImpl.h	(revision 79731)
+++ /trunk/src/VBox/Main/include/DHCPServerImpl.h	(revision 79732)
@@ -32,6 +32,12 @@
 }
 
+class DHCPConfig;
+class DHCPIndividualConfig;
 
 /**
+ * A DHCP server for internal host-only & NAT networks.
+ *
+ * Old notes:
+ *
  *  for server configuration needs, it's perhaps better to use (VM,slot) pair
  *  (vm-name, slot) <----> (MAC)
@@ -49,76 +55,79 @@
 {
 public:
+    /** @name Constructors and destructors
+     * @{ */
+    DECLARE_EMPTY_CTOR_DTOR(DHCPServer)
+    HRESULT FinalConstruct();
+    void    FinalRelease();
 
-    DECLARE_EMPTY_CTOR_DTOR(DHCPServer)
+    HRESULT init(VirtualBox *aVirtualBox, const com::Utf8Str &aName);
+    HRESULT init(VirtualBox *aVirtualBox, const settings::DHCPServer &data);
+    void    uninit();
+    /** @} */
 
-    HRESULT FinalConstruct();
-    void FinalRelease();
-
-    HRESULT init(VirtualBox *aVirtualBox,
-                 const com::Utf8Str &aName);
-    HRESULT init(VirtualBox *aVirtualBox,
-                 const settings::DHCPServer &data);
-    void uninit();
-
-    // Public internal methids.
+    /** @name Public internal methods.
+     * @{ */
     HRESULT i_saveSettings(settings::DHCPServer &data);
-    settings::DhcpOptionMap &i_findOptMapByVmNameSlot(const com::Utf8Str &aVmName, LONG Slot);
+    /** @} */
 
 private:
-    HRESULT encodeOption(com::Utf8Str &aEncoded, uint32_t aOptCode, const settings::DhcpOptValue &aOptValue);
-    int addOption(settings::DhcpOptionMap &aMap, DhcpOpt_T aOption, const com::Utf8Str &aValue);
+    /** @name IDHCPServer Properties
+     * @{ */
+    HRESULT getEventSource(ComPtr<IEventSource> &aEventSource) RT_OVERRIDE;
+    HRESULT getEnabled(BOOL *aEnabled) RT_OVERRIDE;
+    HRESULT setEnabled(BOOL aEnabled) RT_OVERRIDE;
+    HRESULT getIPAddress(com::Utf8Str &aIPAddress) RT_OVERRIDE;
+    HRESULT getNetworkMask(com::Utf8Str &aNetworkMask) RT_OVERRIDE;
+    HRESULT getNetworkName(com::Utf8Str &aName) RT_OVERRIDE;
+    HRESULT getLowerIP(com::Utf8Str &aIPAddress) RT_OVERRIDE;
+    HRESULT getUpperIP(com::Utf8Str &aIPAddress) RT_OVERRIDE;
+    HRESULT getGlobalOptions(std::vector<com::Utf8Str> &aGlobalOptions) RT_OVERRIDE;
+    HRESULT getVmConfigs(std::vector<com::Utf8Str> &aVmConfigs) RT_OVERRIDE;
+    HRESULT getMacOptions(const com::Utf8Str &aMAC, std::vector<com::Utf8Str> &aValues) RT_OVERRIDE;
+    HRESULT setConfiguration(const com::Utf8Str &aIPAddress, const com::Utf8Str &aNetworkMask,
+                             const com::Utf8Str &aFromIPAddress, const com::Utf8Str &aToIPAddress) RT_OVERRIDE;
+    HRESULT getVmSlotOptions(const com::Utf8Str &aVmName, LONG aSlot, std::vector<com::Utf8Str> &aValues) RT_OVERRIDE;
+    HRESULT getGlobalConfig(ComPtr<IDHCPGlobalConfig> &aGlobalConfig) RT_OVERRIDE;
+    HRESULT getGroupConfigs(std::vector<ComPtr<IDHCPGroupConfig> > &aGroupConfigs) RT_OVERRIDE;
+    HRESULT getIndividualConfigs(std::vector<ComPtr<IDHCPIndividualConfig> > &aIndividualConfigs) ;
+    /** @} */
 
-    // wrapped IDHCPServer properties
-    HRESULT getEventSource(ComPtr<IEventSource> &aEventSource);
-    HRESULT getEnabled(BOOL *aEnabled);
-    HRESULT setEnabled(BOOL aEnabled);
-    HRESULT getIPAddress(com::Utf8Str &aIPAddress);
-    HRESULT getNetworkMask(com::Utf8Str &aNetworkMask);
-    HRESULT getNetworkName(com::Utf8Str &aName);
-    HRESULT getLowerIP(com::Utf8Str &aIPAddress);
-    HRESULT getUpperIP(com::Utf8Str &aIPAddress);
-    HRESULT getGlobalOptions(std::vector<com::Utf8Str> &aGlobalOptions);
-    HRESULT getVmConfigs(std::vector<com::Utf8Str> &aVmConfigs);
-    HRESULT getMacOptions(const com::Utf8Str &aMAC, std::vector<com::Utf8Str> &aValues);
-    HRESULT setConfiguration(const com::Utf8Str &aIPAddress,
-                             const com::Utf8Str &aNetworkMask,
-                             const com::Utf8Str &aFromIPAddress,
-                             const com::Utf8Str &aToIPAddress);
-    HRESULT getVmSlotOptions(const com::Utf8Str &aVmName,
-                             LONG aSlot,
-                             std::vector<com::Utf8Str> &aValues);
-
-    // Wrapped IDHCPServer Methods
-    HRESULT addGlobalOption(DhcpOpt_T aOption,
-                            const com::Utf8Str &aValue);
-    HRESULT removeGlobalOption(DhcpOpt_T aOption);
-    HRESULT removeGlobalOptions();
-    HRESULT addVmSlotOption(const com::Utf8Str &aVmName,
-                            LONG aSlot,
-                            DhcpOpt_T aOption,
-                            const com::Utf8Str &aValue);
-    HRESULT removeVmSlotOption(const com::Utf8Str &aVmName,
-                               LONG aSlot,
-                               DhcpOpt_T aOption);
-    HRESULT removeVmSlotOptions(const com::Utf8Str &aVmName,
-                                LONG aSlot);
-    HRESULT start(const com::Utf8Str &aNetworkName,
-                  const com::Utf8Str &aTrunkName,
-                  const com::Utf8Str &aTrunkType);
-    HRESULT stop();
-    HRESULT restart();
-    HRESULT findLeaseByMAC(const com::Utf8Str &aMac, LONG aType,
-                           com::Utf8Str &aAddress, com::Utf8Str &aState, LONG64 *aIssued, LONG64 *aExpire) RT_OVERRIDE;
+    /** @name IDHCPServer Methods
+     * @{ */
+    HRESULT addGlobalOption(DhcpOpt_T aOption, const com::Utf8Str &aValue) RT_OVERRIDE;
+    HRESULT removeGlobalOption(DhcpOpt_T aOption) RT_OVERRIDE;
+    HRESULT removeGlobalOptions() RT_OVERRIDE;
+    HRESULT addVmSlotOption(const com::Utf8Str &aVmName, LONG aSlot, DhcpOpt_T aOption, const com::Utf8Str &aValue) RT_OVERRIDE;
+    HRESULT removeVmSlotOption(const com::Utf8Str &aVmName, LONG aSlot, DhcpOpt_T aOption) RT_OVERRIDE;
+    HRESULT removeVmSlotOptions(const com::Utf8Str &aVmName, LONG aSlot) RT_OVERRIDE;
+    HRESULT start(const com::Utf8Str &aNetworkName, const com::Utf8Str &aTrunkName, const com::Utf8Str &aTrunkType) RT_OVERRIDE;
+    HRESULT stop() RT_OVERRIDE;
+    HRESULT restart() RT_OVERRIDE;
+    HRESULT findLeaseByMAC(const com::Utf8Str &aMac, LONG aType, com::Utf8Str &aAddress, com::Utf8Str &aState,
+                           LONG64 *aIssued, LONG64 *aExpire) RT_OVERRIDE;
+    HRESULT getConfig(DHCPConfigScope_T aScope, const com::Utf8Str &aName, ULONG aSlot, BOOL aMayAdd,
+                      ComPtr<IDHCPConfig> &aConfig) RT_OVERRIDE;
+    /** @} */
 
     /** @name Helpers
      * @{  */
+    HRESULT i_doSaveSettings();
     HRESULT i_calcLeasesFilename(const com::Utf8Str &aNetwork) RT_NOEXCEPT;
+    HRESULT i_writeDhcpdConfig(const char *pszFilename, uint32_t uMACAddressVersion) RT_NOEXCEPT;
+
+    HRESULT i_vmNameToIdAndValidateSlot(const com::Utf8Str &aVmName, LONG aSlot, com::Guid &idMachine);
+    HRESULT i_vmNameAndSlotToConfig(const com::Utf8Str &a_strVmName, LONG a_uSlot, bool a_fCreateIfNeeded,
+                                    ComObjPtr<DHCPIndividualConfig> &a_rPtrConfig);
+
+    HRESULT i_encode60Option(com::Utf8Str &strEncoded, DhcpOpt_T enmOption,
+                             DHCPOptionEncoding_T enmEncoding, const com::Utf8Str &strValue);
+    HRESULT i_getAllOptions60(DHCPConfig &aSourceConfig, std::vector<com::Utf8Str> &aValues);
+    HRESULT i_add60Option(DHCPConfig &aTargetConfig, DhcpOpt_T aOption, const com::Utf8Str &aValue);
     /** @} */
 
+    /** Private data */
     struct Data;
+    /** Private data. */
     Data *m;
-    /** weak VirtualBox parent */
-    VirtualBox *const mVirtualBox;
-    const Utf8Str mName;
 };
 
Index: /trunk/src/VBox/Main/src-server/DHCPConfigImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/DHCPConfigImpl.cpp	(revision 79732)
+++ /trunk/src/VBox/Main/src-server/DHCPConfigImpl.cpp	(revision 79732)
@@ -0,0 +1,671 @@
+/* $Id$ */
+/** @file
+ * VirtualBox Main - IDHCPConfig, IDHCPConfigGlobal, IDHCPConfigGroup, IDHCPConfigIndividual implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN_DHCPCONFIG
+#include "DHCPConfigImpl.h"
+#include "LoggingNew.h"
+
+#include <iprt/err.h>
+#include <iprt/net.h>
+#include <iprt/cpp/utils.h>
+#include <iprt/cpp/xml.h>
+
+#include <VBox/com/array.h>
+#include <VBox/settings.h>
+
+#include "AutoCaller.h"
+#include "MachineImpl.h"
+#include "VirtualBoxImpl.h"
+
+
+
+/*********************************************************************************************************************************
+*   DHCPConfig Implementation                                                                                                    *
+*********************************************************************************************************************************/
+
+HRESULT DHCPConfig::i_initWithDefaults(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent)
+{
+    unconst(m_pVirtualBox) = a_pVirtualBox;
+    unconst(m_pParent)     = a_pParent;
+    return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_initWithSettings(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent, const settings::DHCPConfig &rConfig)
+{
+    unconst(m_pVirtualBox) = a_pVirtualBox;
+    unconst(m_pParent)     = a_pParent;
+
+    m_secMinLeaseTime     = rConfig.secMinLeaseTime;
+    m_secDefaultLeaseTime = rConfig.secDefaultLeaseTime;
+    m_secMaxLeaseTime     = rConfig.secMaxLeaseTime;
+
+    for (settings::DhcpOptionMap::const_iterator it = rConfig.OptionMap.begin(); it != rConfig.OptionMap.end(); ++it)
+    {
+        try
+        {
+            m_OptionMap[it->first] = settings::DhcpOptValue(it->second.strValue, it->second.enmEncoding);
+        }
+        catch (std::bad_alloc &)
+        {
+            return E_OUTOFMEMORY;
+        }
+    }
+
+    return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_saveSettings(settings::DHCPConfig &a_rDst)
+{
+    /* lease times */
+    a_rDst.secMinLeaseTime     = m_secMinLeaseTime;
+    a_rDst.secDefaultLeaseTime = m_secDefaultLeaseTime;
+    a_rDst.secMaxLeaseTime     = m_secMaxLeaseTime;
+
+    /* Options: */
+    try
+    {
+        a_rDst.OptionMap = m_OptionMap;
+    }
+    catch (std::bad_alloc &)
+    {
+        return E_OUTOFMEMORY;
+    }
+    return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_getScope(DHCPConfigScope_T *aScope)
+{
+    /* No locking needed. */
+    *aScope = m_enmScope;
+    return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_getMinLeaseTime(ULONG *aMinLeaseTime)
+{
+    AutoReadLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+    *aMinLeaseTime = m_secMinLeaseTime;
+    return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_setMinLeaseTime(ULONG aMinLeaseTime)
+{
+    {
+        AutoWriteLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+        m_secMinLeaseTime = aMinLeaseTime;
+    }
+    return i_doWriteConfig();
+}
+
+
+HRESULT DHCPConfig::i_getDefaultLeaseTime(ULONG *aDefaultLeaseTime)
+{
+    AutoReadLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+    *aDefaultLeaseTime = m_secDefaultLeaseTime;
+    return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_setDefaultLeaseTime(ULONG aDefaultLeaseTime)
+{
+    {
+        AutoWriteLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+        m_secDefaultLeaseTime = aDefaultLeaseTime;
+    }
+    return i_doWriteConfig();
+}
+
+
+HRESULT DHCPConfig::i_getMaxLeaseTime(ULONG *aMaxLeaseTime)
+{
+    AutoReadLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+    *aMaxLeaseTime = m_secMaxLeaseTime;
+    return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_setMaxLeaseTime(ULONG aMaxLeaseTime)
+{
+    {
+        AutoWriteLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+        m_secMaxLeaseTime = aMaxLeaseTime;
+    }
+    return i_doWriteConfig();
+}
+
+
+HRESULT DHCPConfig::i_setOption(DhcpOpt_T aOption, DHCPOptionEncoding_T aEncoding, const com::Utf8Str &aValue)
+{
+    /** @todo validate option value format. */
+    {
+        AutoWriteLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+        try
+        {
+            m_OptionMap[aOption] = settings::DhcpOptValue(aValue, aEncoding);
+        }
+        catch (std::bad_alloc &)
+        {
+            return E_OUTOFMEMORY;
+        }
+    }
+    i_doWriteConfig();
+    return S_OK;
+}
+
+
+HRESULT DHCPConfig::i_removeOption(DhcpOpt_T aOption)
+{
+    {
+        AutoWriteLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+        settings::DhcpOptionMap::iterator it = m_OptionMap.find(aOption);
+        if (it != m_OptionMap.end())
+            m_OptionMap.erase(it);
+        else
+            return m_pHack->setError(VBOX_E_OBJECT_NOT_FOUND, m_pHack->tr("DHCP option %u was not found"), aOption);
+    }
+    return i_doWriteConfig();
+}
+
+
+HRESULT DHCPConfig::i_removeAllOptions()
+{
+    {
+        AutoWriteLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+        m_OptionMap.erase(m_OptionMap.begin(), m_OptionMap.end());
+    }
+    return i_doWriteConfig();
+}
+
+
+HRESULT DHCPConfig::i_getOption(DhcpOpt_T aOption, DHCPOptionEncoding_T *aEncoding, com::Utf8Str &aValue)
+{
+    AutoReadLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+    settings::DhcpOptionMap::const_iterator it = m_OptionMap.find(aOption);
+    if (it != m_OptionMap.end())
+    {
+        *aEncoding = it->second.enmEncoding;
+        return aValue.assignEx(it->second.strValue);
+    }
+    return m_pHack->setError(VBOX_E_OBJECT_NOT_FOUND, m_pHack->tr("DHCP option %u was not found"), aOption);
+}
+
+
+HRESULT DHCPConfig::i_getAllOptions(std::vector<DhcpOpt_T> &aOptions, std::vector<DHCPOptionEncoding_T> &aEncodings,
+                                    std::vector<com::Utf8Str> &aValues)
+{
+    AutoReadLock alock(m_pHack COMMA_LOCKVAL_SRC_POS);
+    try
+    {
+        aOptions.resize(m_OptionMap.size());
+        aEncodings.resize(m_OptionMap.size());
+        aValues.resize(m_OptionMap.size());
+        size_t i = 0;
+        for (settings::DhcpOptionMap::iterator it = m_OptionMap.begin(); it != m_OptionMap.end(); ++it)
+        {
+            aOptions[i]   = it->first;
+            aEncodings[i] = it->second.enmEncoding;
+            aValues[i]    = it->second.strValue;
+        }
+    }
+    catch (std::bad_alloc &)
+    {
+        return E_OUTOFMEMORY;
+    }
+    return S_OK;
+}
+
+
+/**
+ * Causes the global VirtualBox configuration file to be written
+ *
+ * @returns COM status code.
+ *
+ * @note    Must hold no locks when this is called!
+ */
+HRESULT DHCPConfig::i_doWriteConfig()
+{
+    AssertPtrReturn(m_pVirtualBox, E_FAIL);
+
+    AutoWriteLock alock(m_pVirtualBox COMMA_LOCKVAL_SRC_POS);
+    return m_pVirtualBox->i_saveSettings();
+}
+
+
+/**
+ * Produces the Dhcpd configuration.
+ *
+ * The base class only saves DHCP options.
+ *
+ * @param   pElmConfig  The element where to put the configuration.
+ * @throws  std::bad_alloc
+ */
+void DHCPConfig::i_writeDhcpdConfig(xml::ElementNode *pElmConfig)
+{
+    if (m_secMinLeaseTime > 0 )
+        pElmConfig->setAttribute("secMinLeaseTime", (uint32_t)m_secMinLeaseTime);
+    if (m_secDefaultLeaseTime > 0 )
+        pElmConfig->setAttribute("secDefaultLeaseTime", (uint32_t)m_secDefaultLeaseTime);
+    if (m_secMaxLeaseTime > 0 )
+        pElmConfig->setAttribute("secMaxLeaseTime", (uint32_t)m_secMaxLeaseTime);
+
+    for (settings::DhcpOptionMap::const_iterator it = m_OptionMap.begin(); it != m_OptionMap.end(); ++it)
+    {
+        xml::ElementNode *pOption = pElmConfig->createChild("Option");
+        pOption->setAttribute("name", (int)it->first);
+        pOption->setAttribute("encoding", it->second.enmEncoding);
+        pOption->setAttribute("value", it->second.strValue);
+    }
+}
+
+
+
+/*********************************************************************************************************************************
+*   DHCPGlobalConfig Implementation                                                                                              *
+*********************************************************************************************************************************/
+#undef  LOG_GROUP
+#define LOG_GROUP LOG_GROUP_MAIN_DHCPGLOBALCONFIG
+
+HRESULT DHCPGlobalConfig::initWithDefaults(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent)
+{
+    AutoInitSpan autoInitSpan(this);
+    AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+    HRESULT hrc = DHCPConfig::i_initWithDefaults(a_pVirtualBox, a_pParent);
+    if (SUCCEEDED(hrc))
+        hrc = i_setOption(DhcpOpt_SubnetMask, DHCPOptionEncoding_Legacy, "0.0.0.0");
+
+    if (SUCCEEDED(hrc))
+        autoInitSpan.setSucceeded();
+    return hrc;
+}
+
+
+HRESULT DHCPGlobalConfig::initWithSettings(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent, const settings::DHCPConfig &rConfig)
+{
+    AutoInitSpan autoInitSpan(this);
+    AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+    HRESULT hrc = DHCPConfig::i_initWithSettings(a_pVirtualBox, a_pParent, rConfig);
+    if (SUCCEEDED(hrc))
+        autoInitSpan.setSucceeded();
+    else
+        autoInitSpan.setFailed(hrc);
+    return hrc;
+}
+
+
+void DHCPGlobalConfig::uninit()
+{
+    AutoUninitSpan autoUninitSpan(this);
+    if (!autoUninitSpan.uninitDone())
+        autoUninitSpan.setSucceeded();
+}
+
+
+HRESULT DHCPGlobalConfig::i_saveSettings(settings::DHCPConfig &a_rDst)
+{
+    return DHCPConfig::i_saveSettings(a_rDst);
+}
+
+
+/**
+ * For getting the network mask option value (IDHCPServer::netmask attrib).
+ *
+ * @returns COM status code.
+ * @param   a_rDst          Where to return it.
+ * @throws  nothing
+ */
+HRESULT DHCPGlobalConfig::i_getNetworkMask(com::Utf8Str &a_rDst)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    settings::DhcpOptionMap::const_iterator it = m_OptionMap.find(DhcpOpt_SubnetMask);
+    if (it != m_OptionMap.end())
+    {
+        if (it->second.enmEncoding == DHCPOptionEncoding_Legacy)
+            return a_rDst.assignEx(it->second.strValue);
+        return setError(VBOX_E_OBJECT_NOT_FOUND, tr("DHCP option DhcpOpt_SubnetMask is not in a legacy encoding"));
+    }
+    return setError(VBOX_E_OBJECT_NOT_FOUND, tr("DHCP option DhcpOpt_SubnetMask was not found"));
+}
+
+
+/**
+ * For setting the network mask option value (IDHCPServer::netmask attrib).
+ *
+ * @returns COM status code.
+ * @param   a_rSrc          The new value.
+ * @throws  nothing
+ */
+HRESULT DHCPGlobalConfig::i_setNetworkMask(const com::Utf8Str &a_rSrc)
+{
+    /* Validate it before setting it: */
+    RTNETADDRIPV4 AddrIgnored;
+    int vrc = RTNetStrToIPv4Addr(a_rSrc.c_str(), &AddrIgnored);
+    if (RT_FAILURE(vrc))
+        return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid IPv4 netmask '%s': %Rrc"), a_rSrc.c_str(), vrc);
+
+    return i_setOption(DhcpOpt_SubnetMask, DHCPOptionEncoding_Legacy, a_rSrc);
+}
+
+
+/**
+ * Overriden to ensure the sanity of the DhcpOpt_SubnetMask option.
+ */
+HRESULT DHCPGlobalConfig::i_setOption(DhcpOpt_T aOption, DHCPOptionEncoding_T aEncoding, const com::Utf8Str &aValue)
+{
+    if (aOption != DhcpOpt_SubnetMask || aEncoding == DHCPOptionEncoding_Legacy)
+        return DHCPConfig::i_setOption(aOption, aEncoding, aValue);
+    return setError(E_FAIL, tr("DhcpOpt_SubnetMask must use DHCPOptionEncoding_Legacy as it is reflected by IDHCPServer::networkMask"));
+}
+
+
+/**
+ * Overriden to ensure the sanity of the DhcpOpt_SubnetMask option.
+ */
+HRESULT DHCPGlobalConfig::i_removeOption(DhcpOpt_T aOption)
+{
+    if (aOption != DhcpOpt_SubnetMask)
+        return DHCPConfig::i_removeOption(aOption);
+    return setError(E_FAIL, tr("DhcpOpt_SubnetMask cannot be removed as it reflects IDHCPServer::networkMask"));
+}
+
+
+/**
+ * Overriden to preserve the DhcpOpt_SubnetMask option.
+ */
+HRESULT DHCPGlobalConfig::i_removeAllOptions()
+{
+    {
+        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+        settings::DhcpOptionMap::iterator it = m_OptionMap.find(DhcpOpt_SubnetMask);
+        m_OptionMap.erase(m_OptionMap.begin(), it);
+        if (it != m_OptionMap.end())
+        {
+            ++it;
+            if (it != m_OptionMap.end())
+                m_OptionMap.erase(it, m_OptionMap.end());
+        }
+    }
+
+    return i_doWriteConfig();
+}
+
+
+
+/*********************************************************************************************************************************
+*   DHCPIndividualConfig Implementation                                                                                          *
+*********************************************************************************************************************************/
+#undef  LOG_GROUP
+#define LOG_GROUP LOG_GROUP_MAIN_DHCPINDIVIDUALCONFIG
+
+HRESULT DHCPIndividualConfig::initWithMachineIdAndSlot(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent,
+                                                       com::Guid const &a_idMachine, ULONG a_uSlot, uint32_t a_uMACAddressVersion)
+{
+    AutoInitSpan autoInitSpan(this);
+    AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+    HRESULT hrc = DHCPConfig::i_initWithDefaults(a_pVirtualBox, a_pParent);
+    if (SUCCEEDED(hrc))
+    {
+        unconst(m_enmScope)          = DHCPConfigScope_MachineNIC;
+        unconst(m_idMachine)         = a_idMachine;
+        unconst(m_uSlot)             = a_uSlot;
+        m_uMACAddressResolvedVersion = a_uMACAddressVersion;
+
+        autoInitSpan.setSucceeded();
+    }
+    return hrc;
+}
+
+
+HRESULT DHCPIndividualConfig::initWithMACAddress(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent, PCRTMAC a_pMACAddress)
+{
+    AutoInitSpan autoInitSpan(this);
+    AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+    HRESULT hrc = DHCPConfig::i_initWithDefaults(a_pVirtualBox, a_pParent);
+    if (SUCCEEDED(hrc))
+    {
+        unconst(m_enmScope)   = DHCPConfigScope_MAC;
+        unconst(m_MACAddress) = *a_pMACAddress;
+
+        autoInitSpan.setSucceeded();
+    }
+    return hrc;
+}
+
+
+HRESULT DHCPIndividualConfig::initWithSettingsAndMachineIdAndSlot(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent,
+                                                                  settings::DHCPIndividualConfig const &rConfig,
+                                                                  com::Guid const &a_idMachine, ULONG a_uSlot,
+                                                                  uint32_t a_uMACAddressVersion)
+{
+    AutoInitSpan autoInitSpan(this);
+    AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+    HRESULT hrc = DHCPConfig::i_initWithSettings(a_pVirtualBox, a_pParent, rConfig);
+    if (SUCCEEDED(hrc))
+    {
+        unconst(m_enmScope)          = DHCPConfigScope_MachineNIC;
+        unconst(m_idMachine)         = a_idMachine;
+        unconst(m_uSlot)             = a_uSlot;
+        m_uMACAddressResolvedVersion = a_uMACAddressVersion;
+        m_strFixedAddress            = rConfig.strFixedAddress;
+
+        autoInitSpan.setSucceeded();
+    }
+    return hrc;
+}
+
+
+HRESULT DHCPIndividualConfig::initWithSettingsAndMACAddress(VirtualBox *a_pVirtualBox, DHCPServer *a_pParent,
+                                                            settings::DHCPIndividualConfig const &rConfig, PCRTMAC a_pMACAddress)
+{
+    AutoInitSpan autoInitSpan(this);
+    AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+    HRESULT hrc = DHCPConfig::i_initWithSettings(a_pVirtualBox, a_pParent, rConfig);
+    if (SUCCEEDED(hrc))
+    {
+        unconst(m_enmScope)   = DHCPConfigScope_MAC;
+        unconst(m_MACAddress) = *a_pMACAddress;
+        m_strFixedAddress     = rConfig.strFixedAddress;
+
+        autoInitSpan.setSucceeded();
+    }
+    return hrc;
+}
+
+
+void    DHCPIndividualConfig::uninit()
+{
+    AutoUninitSpan autoUninitSpan(this);
+    if (!autoUninitSpan.uninitDone())
+        autoUninitSpan.setSucceeded();
+}
+
+
+HRESULT DHCPIndividualConfig::i_saveSettings(settings::DHCPIndividualConfig &a_rDst)
+{
+    a_rDst.uSlot = m_uSlot;
+    int vrc = a_rDst.strMACAddress.printfNoThrow("%RTmac", &m_MACAddress);
+    if (m_idMachine.isValid() && !m_idMachine.isZero() && RT_SUCCESS(vrc))
+        vrc = a_rDst.strVMName.printfNoThrow("%RTuuid", m_idMachine.raw());
+    if (RT_SUCCESS(vrc))
+        vrc = a_rDst.strFixedAddress.assignNoThrow(m_strFixedAddress);
+    if (RT_SUCCESS(vrc))
+        return DHCPConfig::i_saveSettings(a_rDst);
+    return E_OUTOFMEMORY;;
+}
+
+
+HRESULT DHCPIndividualConfig::getMACAddress(com::Utf8Str &aMACAddress)
+{
+    /* No locking needed here (the MAC address, machine UUID and NIC slot number cannot change). */
+    RTMAC MACAddress;
+    if (m_enmScope == DHCPConfigScope_MAC)
+        MACAddress = m_MACAddress;
+    else
+    {
+        HRESULT hrc = i_getMachineMAC(&MACAddress);
+        if (FAILED(hrc))
+            return hrc;
+    }
+
+    /* Format the return string: */
+    int vrc = aMACAddress.printfNoThrow("%RTmac", &m_MACAddress);
+    return RT_SUCCESS(vrc) ? S_OK : E_OUTOFMEMORY;
+}
+
+
+HRESULT DHCPIndividualConfig::getMachineId(com::Guid &aId)
+{
+    AutoReadLock(this COMMA_LOCKVAL_SRC_POS);
+    aId = m_idMachine;
+    return S_OK;
+}
+
+
+HRESULT DHCPIndividualConfig::getSlot(ULONG *aSlot)
+{
+    AutoReadLock(this COMMA_LOCKVAL_SRC_POS);
+    *aSlot = m_uSlot;
+    return S_OK;
+}
+
+HRESULT DHCPIndividualConfig::getFixedAddress(com::Utf8Str &aFixedAddress)
+{
+    AutoReadLock(this COMMA_LOCKVAL_SRC_POS);
+    return aFixedAddress.assignEx(m_strFixedAddress);
+}
+
+
+HRESULT DHCPIndividualConfig::setFixedAddress(const com::Utf8Str &aFixedAddress)
+{
+    if (aFixedAddress.isNotEmpty())
+    {
+        RTNETADDRIPV4 AddrIgnored;
+        int vrc = RTNetStrToIPv4Addr(aFixedAddress.c_str(), &AddrIgnored);
+        if (RT_FAILURE(vrc))
+            return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid IPv4 address '%s': %Rrc"), aFixedAddress.c_str(), vrc);
+    }
+
+    {
+        AutoWriteLock(this COMMA_LOCKVAL_SRC_POS);
+        m_strFixedAddress = aFixedAddress;
+    }
+    return i_doWriteConfig();
+}
+
+
+/**
+ * Gets the MAC address of m_idMachine + m_uSlot.
+ *
+ * @returns COM status code w/ setError.
+ * @param   pMACAddress     Where to return the address.
+ *
+ * @note    Must be called without holding any DHCP related locks as that would
+ *          be lock order violation.  The m_idMachine and m_uSlot values are
+ *          practically const, so we don't need any locks here anyway.
+ */
+HRESULT DHCPIndividualConfig::i_getMachineMAC(PRTMAC pMACAddress)
+{
+    ComObjPtr<Machine> ptrMachine;
+    HRESULT hrc = m_pVirtualBox->i_findMachine(m_idMachine, false /*fPermitInaccessible*/, true /*aSetError*/, &ptrMachine);
+    if (SUCCEEDED(hrc))
+    {
+        ComPtr<INetworkAdapter> ptrNetworkAdapter;
+        hrc = ptrMachine->GetNetworkAdapter(m_uSlot, ptrNetworkAdapter.asOutParam());
+        if (SUCCEEDED(hrc))
+        {
+            com::Bstr bstrMACAddress;
+            hrc = ptrNetworkAdapter->COMGETTER(MACAddress)(bstrMACAddress.asOutParam());
+            if (SUCCEEDED(hrc))
+            {
+                Utf8Str strMACAddress;
+                try
+                {
+                    strMACAddress = bstrMACAddress;
+                }
+                catch (std::bad_alloc &)
+                {
+                    return E_OUTOFMEMORY;
+                }
+
+                int vrc = RTNetStrToMacAddr(strMACAddress.c_str(), pMACAddress);
+                if (RT_SUCCESS(vrc))
+                    hrc = S_OK;
+                else
+                    hrc = setError(hrc, tr("INetworkAdapter returned bogus MAC address '%ls'"), bstrMACAddress.raw());
+            }
+        }
+    }
+    return hrc;
+}
+
+
+HRESULT DHCPIndividualConfig::i_resolveMACAddress(uint32_t uVersion)
+{
+    HRESULT hrc;
+    if (m_enmScope == DHCPConfigScope_MachineNIC)
+    {
+        RTMAC MACAddress;
+        hrc = i_getMachineMAC(&MACAddress);
+        if (SUCCEEDED(hrc))
+        {
+            AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+            if ((int32_t)(m_uMACAddressResolvedVersion - uVersion) >= 0)
+            {
+                m_uMACAddressResolvedVersion = uVersion;
+                m_MACAddress                 = MACAddress;
+            }
+        }
+    }
+    else
+        hrc = S_OK;
+    return hrc;
+}
+
+
+/**
+ * Overridden to write out additional config.
+ */
+void DHCPIndividualConfig::i_writeDhcpdConfig(xml::ElementNode *pElmConfig)
+{
+    char szTmp[RTUUID_STR_LENGTH + 32];
+    RTStrPrintf(szTmp, sizeof(szTmp), "%RTmac", &m_MACAddress);
+    pElmConfig->setAttribute("MACAddress", szTmp);
+
+    if (m_enmScope == DHCPConfigScope_MachineNIC)
+    {
+        RTStrPrintf(szTmp, sizeof(szTmp), "%RTuuid/%u", m_idMachine.raw(), m_uSlot);
+        pElmConfig->setAttribute("name", szTmp);
+    }
+
+    pElmConfig->setAttribute("fixedAddress", m_strFixedAddress);
+
+    DHCPConfig::i_writeDhcpdConfig(pElmConfig);
+}
+
Index: /trunk/src/VBox/Main/src-server/DHCPServerImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/DHCPServerImpl.cpp	(revision 79731)
+++ /trunk/src/VBox/Main/src-server/DHCPServerImpl.cpp	(revision 79732)
@@ -21,7 +21,5 @@
 *********************************************************************************************************************************/
 #define LOG_GROUP LOG_GROUP_MAIN_DHCPSERVER
-#include "NetworkServiceRunner.h"
 #include "DHCPServerImpl.h"
-#include "AutoCaller.h"
 #include "LoggingNew.h"
 
@@ -38,4 +36,8 @@
 #include <VBox/settings.h>
 
+#include "AutoCaller.h"
+#include "DHCPConfigImpl.h"
+#include "MachineImpl.h"
+#include "NetworkServiceRunner.h"
 #include "VirtualBoxImpl.h"
 
@@ -84,9 +86,18 @@
 {
     Data()
-        : enabled(FALSE)
-        , router(false)
+        : pVirtualBox(NULL)
+        , strName()
+        , enabled(FALSE)
+//        , router(false)
+        , uIndividualMACAddressVersion(1)
     {
         szTempConfigFileName[0] = '\0';
     }
+
+    /** weak VirtualBox parent */
+    VirtualBox * const  pVirtualBox;
+    /** The DHCP server name (network).
+     * @todo Kind of duplicated by networkName, if I understand it correctly.  */
+    Utf8Str const       strName;
 
     Utf8Str IPAddress;
@@ -95,9 +106,14 @@
 
     BOOL enabled;
+#if 0
+    /** Don't quit get WTF this is about, but the old addOption method contained the
+     * following hint: "Indirect way to understand that we're on NAT network."
+     *
+     * Apparently this is a busted with the new dhcpd implementation, so we don't
+     * maintain it with the API overhaul in 6.0.12.
+     */
     bool router;
+#endif
     DHCPServerRunner dhcp;
-
-    settings::DhcpOptionMap GlobalDhcpOptions;
-    settings::VmSlot2OptionsMap VmSlot2Options;
 
     char szTempConfigFileName[RTPATH_MAX];
@@ -106,4 +122,21 @@
     com::Utf8Str trunkName;
     com::Utf8Str trunkType;
+
+    /** Global configuration. */
+    ComObjPtr<DHCPGlobalConfig> globalConfig;
+
+    /** Group configuration indexed by name. */
+    std::map<com::Utf8Str, ComObjPtr<DHCPGroupConfig>> groupConfigs;
+    /** Iterator for groupConfigs. */
+    typedef std::map<com::Utf8Str, ComObjPtr<DHCPGroupConfig>>::iterator GroupConfigIterator;
+
+    /** Individual (host) configuration indexed by MAC address or VM UUID. */
+    std::map<com::Utf8Str, ComObjPtr<DHCPIndividualConfig>> individualConfigs;
+    /** Iterator for individualConfigs. */
+    typedef std::map<com::Utf8Str, ComObjPtr<DHCPIndividualConfig>>::iterator IndividualConfigIterator;
+
+    /** Part of a lock-avoidance hack to resolve the VM ID + slot into MAC
+     *  addresses before writing out the Dhcpd configuration file. */
+    uint32_t uIndividualMACAddressVersion;
 };
 
@@ -115,5 +148,4 @@
 DHCPServer::DHCPServer()
     : m(NULL)
-    , mVirtualBox(NULL)
 {
     m = new DHCPServer::Data();
@@ -139,6 +171,5 @@
 void DHCPServer::FinalRelease()
 {
-    uninit ();
-
+    uninit();
     BaseFinalRelease();
 }
@@ -155,5 +186,5 @@
         stop();
 
-    unconst(mVirtualBox) = NULL;
+    unconst(m->pVirtualBox) = NULL;
 }
 
@@ -167,22 +198,27 @@
 
     /* share VirtualBox weakly (parent remains NULL so far) */
-    unconst(mVirtualBox) = aVirtualBox;
-
-    unconst(mName) = aName;
+    unconst(m->pVirtualBox) = aVirtualBox;
+
+    unconst(m->strName) = aName;
     m->IPAddress = "0.0.0.0";
-    m->GlobalDhcpOptions[DhcpOpt_SubnetMask] = settings::DhcpOptValue("0.0.0.0");
-    m->enabled = FALSE;
-
-    m->lowerIP = "0.0.0.0";
-    m->upperIP = "0.0.0.0";
-
-    /* Confirm a successful initialization */
-    autoInitSpan.setSucceeded();
-
-    return S_OK;
-}
-
-
-HRESULT DHCPServer::init(VirtualBox *aVirtualBox, const settings::DHCPServer &data)
+    m->lowerIP   = "0.0.0.0";
+    m->upperIP   = "0.0.0.0";
+    m->enabled   = FALSE;
+
+    /* Global configuration: */
+    HRESULT hrc = m->globalConfig.createObject();
+    if (SUCCEEDED(hrc))
+        hrc = m->globalConfig->initWithDefaults(aVirtualBox, this);
+
+    /* Confirm a successful initialization or not: */
+    if (SUCCEEDED(hrc))
+        autoInitSpan.setSucceeded();
+    else
+        autoInitSpan.setFailed(hrc);
+    return hrc;
+}
+
+
+HRESULT DHCPServer::init(VirtualBox *aVirtualBox, const settings::DHCPServer &rData)
 {
     /* Enclose the state transition NotReady->InInit->Ready */
@@ -191,25 +227,90 @@
 
     /* share VirtualBox weakly (parent remains NULL so far) */
-    unconst(mVirtualBox) = aVirtualBox;
-
-    unconst(mName) = data.strNetworkName;
-    m->IPAddress = data.strIPAddress;
-    m->enabled = data.fEnabled;
-    m->lowerIP = data.strIPLower;
-    m->upperIP = data.strIPUpper;
-
-    m->GlobalDhcpOptions.clear();
-    m->GlobalDhcpOptions.insert(data.GlobalDhcpOptions.begin(), data.GlobalDhcpOptions.end());
-
-    m->VmSlot2Options.clear();
-    m->VmSlot2Options.insert(data.VmSlot2OptionsM.begin(), data.VmSlot2OptionsM.end());
-
-    autoInitSpan.setSucceeded();
-
-    return S_OK;
-}
-
-
-HRESULT DHCPServer::i_saveSettings(settings::DHCPServer &data)
+    unconst(m->pVirtualBox) = aVirtualBox;
+
+    unconst(m->strName) = rData.strNetworkName;
+    m->IPAddress        = rData.strIPAddress;
+    m->enabled          = rData.fEnabled;
+    m->lowerIP          = rData.strIPLower;
+    m->upperIP          = rData.strIPUpper;
+
+    /*
+     * Global configuration:
+     */
+    HRESULT hrc = m->globalConfig.createObject();
+    if (SUCCEEDED(hrc))
+        hrc = m->globalConfig->initWithSettings(aVirtualBox, this, rData.GlobalConfig);
+
+    /*
+     * Group configurations:
+     */
+
+    /*
+     * Individual configuration:
+     */
+    Assert(m->individualConfigs.size() == 0);
+    if (SUCCEEDED(hrc))
+    {
+        for (settings::DHCPIndividualConfigMap::const_iterator it = rData.IndividualConfigs.begin();
+             it != rData.IndividualConfigs.end() && SUCCEEDED(hrc); ++it)
+        {
+            ComObjPtr<DHCPIndividualConfig> ptrIndiCfg;
+            com::Utf8Str                    strKey;
+            if (!it->second.strVMName.isNotEmpty())
+            {
+                RTMAC MACAddress;
+                int vrc = RTNetStrToMacAddr(it->second.strMACAddress.c_str(), &MACAddress);
+                if (RT_FAILURE(vrc))
+                {
+                    LogRel(("Ignoring invalid MAC address for individual DHCP config: '%s' - %Rrc\n", it->second.strMACAddress.c_str(), vrc));
+                    continue;
+                }
+
+                vrc = strKey.printfNoThrow("%RTmac", &MACAddress);
+                AssertRCReturn(vrc, E_OUTOFMEMORY);
+
+                hrc = ptrIndiCfg.createObject();
+                if (SUCCEEDED(hrc))
+                    hrc = ptrIndiCfg->initWithSettingsAndMACAddress(aVirtualBox, this, it->second, &MACAddress);
+            }
+            else
+            {
+                /* This ASSUMES that we're being called after the machines have been
+                   loaded so we can resolve VM names into UUID for old settings. */
+                com::Guid idMachine;
+                hrc = i_vmNameToIdAndValidateSlot(it->second.strVMName, it->second.uSlot, idMachine);
+                if (SUCCEEDED(hrc))
+                {
+                    hrc = ptrIndiCfg.createObject();
+                    if (SUCCEEDED(hrc))
+                        hrc = ptrIndiCfg->initWithSettingsAndMachineIdAndSlot(aVirtualBox, this, it->second,
+                                                                              idMachine, it->second.uSlot,
+                                                                              m->uIndividualMACAddressVersion - UINT32_MAX / 4);
+                }
+            }
+            if (SUCCEEDED(hrc))
+            {
+                try
+                {
+                    m->individualConfigs[strKey] = ptrIndiCfg;
+                }
+                catch (std::bad_alloc &)
+                {
+                    return E_OUTOFMEMORY;
+                }
+            }
+        }
+    }
+
+    /* Confirm a successful initialization or not: */
+    if (SUCCEEDED(hrc))
+        autoInitSpan.setSucceeded();
+    else
+        autoInitSpan.setFailed(hrc);
+    return hrc;
+}
+
+
+HRESULT DHCPServer::i_saveSettings(settings::DHCPServer &rData)
 {
     AutoCaller autoCaller(this);
@@ -218,20 +319,46 @@
     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
-    data.strNetworkName = mName;
-    data.strIPAddress = m->IPAddress;
-
-    data.fEnabled = !!m->enabled;
-    data.strIPLower = m->lowerIP;
-    data.strIPUpper = m->upperIP;
-
-    data.GlobalDhcpOptions.clear();
-    data.GlobalDhcpOptions.insert(m->GlobalDhcpOptions.begin(),
-                                  m->GlobalDhcpOptions.end());
-
-    data.VmSlot2OptionsM.clear();
-    data.VmSlot2OptionsM.insert(m->VmSlot2Options.begin(),
-                                m->VmSlot2Options.end());
-
-    return S_OK;
+    rData.strNetworkName = m->strName;
+    rData.strIPAddress   = m->IPAddress;
+    rData.fEnabled       = m->enabled != FALSE;
+    rData.strIPLower     = m->lowerIP;
+    rData.strIPUpper     = m->upperIP;
+
+    /* Global configuration: */
+    HRESULT hrc = m->globalConfig->i_saveSettings(rData.GlobalConfig);
+
+    /* Group configuration: */
+
+    /* Individual configuration: */
+    for (Data::IndividualConfigIterator it = m->individualConfigs.begin();
+         it != m->individualConfigs.end() && SUCCEEDED(hrc); ++it)
+    {
+        try
+        {
+            rData.IndividualConfigs[it->first] = settings::DHCPIndividualConfig();
+        }
+        catch (std::bad_alloc &)
+        {
+            return E_OUTOFMEMORY;
+        }
+        hrc = it->second->i_saveSettings(rData.IndividualConfigs[it->first]);
+    }
+
+    return hrc;
+}
+
+
+/**
+ * Internal worker that saves the settings after a modification was made.
+ *
+ * @returns COM status code.
+ *
+ * @note    Caller must not hold any locks!
+ */
+HRESULT DHCPServer::i_doSaveSettings()
+{
+    // save the global settings; for that we should hold only the VirtualBox lock
+    AutoWriteLock vboxLock(m->pVirtualBox COMMA_LOCKVAL_SRC_POS);
+    return m->pVirtualBox->i_saveSettings();
 }
 
@@ -239,15 +366,12 @@
 HRESULT DHCPServer::getNetworkName(com::Utf8Str &aName)
 {
+    /* The name is const, so no need to for locking. */
+    return aName.assignEx(m->strName);
+}
+
+
+HRESULT DHCPServer::getEnabled(BOOL *aEnabled)
+{
     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    aName = mName;
-    return S_OK;
-}
-
-
-HRESULT DHCPServer::getEnabled(BOOL *aEnabled)
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
     *aEnabled = m->enabled;
     return S_OK;
@@ -257,13 +381,9 @@
 HRESULT DHCPServer::setEnabled(BOOL aEnabled)
 {
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-    m->enabled = aEnabled;
-
-    // save the global settings; for that we should hold only the VirtualBox lock
-    alock.release();
-    AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
-    HRESULT rc = mVirtualBox->i_saveSettings();
-
-    return rc;
+    {
+        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+        m->enabled = aEnabled;
+    }
+    return i_doSaveSettings();
 }
 
@@ -272,7 +392,5 @@
 {
     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    aIPAddress = Utf8Str(m->IPAddress);
-    return S_OK;
+    return aIPAddress.assignEx(m->IPAddress);
 }
 
@@ -280,26 +398,19 @@
 HRESULT DHCPServer::getNetworkMask(com::Utf8Str &aNetworkMask)
 {
+    return m->globalConfig->i_getNetworkMask(aNetworkMask);
+}
+
+
+HRESULT DHCPServer::getLowerIP(com::Utf8Str &aIPAddress)
+{
     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    aNetworkMask = m->GlobalDhcpOptions[DhcpOpt_SubnetMask].text;
-    return S_OK;
-}
-
-
-HRESULT DHCPServer::getLowerIP(com::Utf8Str &aIPAddress)
+    return aIPAddress.assignEx(m->lowerIP);
+}
+
+
+HRESULT DHCPServer::getUpperIP(com::Utf8Str &aIPAddress)
 {
     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    aIPAddress = Utf8Str(m->lowerIP);
-    return S_OK;
-}
-
-
-HRESULT DHCPServer::getUpperIP(com::Utf8Str &aIPAddress)
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    aIPAddress = Utf8Str(m->upperIP);
-    return S_OK;
+    return aIPAddress.assignEx(m->upperIP);
 }
 
@@ -314,17 +425,17 @@
     int vrc = RTNetStrToIPv4Addr(aIPAddress.c_str(), &IPAddress);
     if (RT_FAILURE(vrc))
-        return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid server address");
+        return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid server address: %s"), aIPAddress.c_str());
 
     vrc = RTNetStrToIPv4Addr(aNetworkMask.c_str(), &NetworkMask);
     if (RT_FAILURE(vrc))
-        return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid netmask");
+        return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid netmask: %s"), aNetworkMask.c_str());
 
     vrc = RTNetStrToIPv4Addr(aLowerIP.c_str(), &LowerIP);
     if (RT_FAILURE(vrc))
-        return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid range lower address");
+        return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid range lower address: %s"), aLowerIP.c_str());
 
     vrc = RTNetStrToIPv4Addr(aUpperIP.c_str(), &UpperIP);
     if (RT_FAILURE(vrc))
-        return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid range upper address");
+        return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid range upper address: %s"), aUpperIP.c_str());
 
     /*
@@ -334,11 +445,11 @@
     vrc = RTNetMaskToPrefixIPv4(&NetworkMask, NULL);
     if (RT_FAILURE(vrc))
-        return mVirtualBox->setErrorBoth(E_INVALIDARG, vrc, "Invalid netmask");
-
-    /* It's more convenient to convert to host order once */
-    IPAddress.u = RT_N2H_U32(IPAddress.u);
+        return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid netmask: %s"), aNetworkMask.c_str());
+
+    /* It's more convenient to convert to host order once: */
+    IPAddress.u   = RT_N2H_U32(IPAddress.u);
     NetworkMask.u = RT_N2H_U32(NetworkMask.u);
-    LowerIP.u = RT_N2H_U32(LowerIP.u);
-    UpperIP.u = RT_N2H_U32(UpperIP.u);
+    LowerIP.u     = RT_N2H_U32(LowerIP.u);
+    UpperIP.u     = RT_N2H_U32(UpperIP.u);
 
     /*
@@ -348,5 +459,5 @@
         || (IPAddress.u & ~NetworkMask.u) == 0
         || ((IPAddress.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
-        return mVirtualBox->setError(E_INVALIDARG, "Invalid server address");
+        return setError(E_INVALIDARG, tr("Invalid server address: %s (mask %s)"), aIPAddress.c_str(), aNetworkMask.c_str());
 
     if (   (LowerIP.u & UINT32_C(0xe0000000)) == UINT32_C(0xe0000000)
@@ -354,5 +465,5 @@
         || (LowerIP.u & ~NetworkMask.u) == 0
         || ((LowerIP.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
-        return mVirtualBox->setError(E_INVALIDARG, "Invalid range lower address");
+        return setError(E_INVALIDARG, tr("Invalid range lower address: %s (mask %s)"), aLowerIP.c_str(), aNetworkMask.c_str());
 
     if (   (UpperIP.u & UINT32_C(0xe0000000)) == UINT32_C(0xe0000000)
@@ -360,35 +471,47 @@
         || (UpperIP.u & ~NetworkMask.u) == 0
         || ((UpperIP.u & ~NetworkMask.u) | NetworkMask.u) == UINT32_C(0xffffffff))
-        return mVirtualBox->setError(E_INVALIDARG, "Invalid range upper address");
+        return setError(E_INVALIDARG, tr("Invalid range upper address"), aUpperIP.c_str(), aNetworkMask.c_str());
 
     /* The range should be valid ... */
     if (LowerIP.u > UpperIP.u)
-        return mVirtualBox->setError(E_INVALIDARG, "Invalid range bounds");
+        return setError(E_INVALIDARG, tr("Lower bound must be less or eqaul than the upper: %s vs %s"),
+                        aLowerIP.c_str(), aUpperIP.c_str());
 
     /* ... and shouldn't contain the server's address */
     if (LowerIP.u <= IPAddress.u && IPAddress.u <= UpperIP.u)
-        return mVirtualBox->setError(E_INVALIDARG, "Server address within range bounds");
-
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-    m->IPAddress = aIPAddress;
-    m->GlobalDhcpOptions[DhcpOpt_SubnetMask] = aNetworkMask;
-
-    m->lowerIP = aLowerIP;
-    m->upperIP = aUpperIP;
-
-    // save the global settings; for that we should hold only the VirtualBox lock
-    alock.release();
-    AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
-    return mVirtualBox->i_saveSettings();
-}
-
-
-HRESULT DHCPServer::encodeOption(com::Utf8Str &aEncoded,
-                                 uint32_t aOptCode,
-                                 const settings::DhcpOptValue &aOptValue)
-{
-    switch (aOptValue.encoding)
-    {
-        case DhcpOptEncoding_Legacy:
+        return setError(E_INVALIDARG, tr("Server address within range bounds: %s in %s - %s"),
+                        aIPAddress.c_str(), aLowerIP.c_str(), aUpperIP.c_str());
+
+    /*
+     * Input is valid, effect the changes.
+     */
+    HRESULT hrc;
+    {
+        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+        m->IPAddress  = aIPAddress;
+        m->lowerIP    = aLowerIP;
+        m->upperIP    = aUpperIP;
+        hrc = m->globalConfig->i_setNetworkMask(aNetworkMask);
+    }
+    if (SUCCEEDED(hrc))
+        hrc = i_doSaveSettings();
+    return hrc;
+}
+
+
+/**
+ * Used by the legacy 6.0 IDHCPServer::GetVmSlotOptions() and
+ * IDHCPServer::GetGlobalOptions() implementations.
+ *
+ * New interfaces have the option number and option encoding separate from the
+ * value.
+ */
+HRESULT DHCPServer::i_encode60Option(com::Utf8Str &strEncoded, DhcpOpt_T enmOption,
+                                     DHCPOptionEncoding_T enmEncoding, const com::Utf8Str &strValue)
+{
+    int vrc;
+    switch (enmEncoding)
+    {
+        case DHCPOptionEncoding_Legacy:
         {
             /*
@@ -400,9 +523,9 @@
              *   "6:1.2.3.4 8.8.8.8" # array of ip-address
              */
-            aEncoded = Utf8StrFmt("%d:%s", aOptCode, aOptValue.text.c_str());
+            vrc = strEncoded.printfNoThrow("%d:%s", (int)enmOption, strValue.c_str());
             break;
         }
 
-        case DhcpOptEncoding_Hex:
+        case DHCPOptionEncoding_Hex:
         {
             /*
@@ -413,5 +536,5 @@
              *   234=68:65:6c:6c:6f:2c:20:77:6f:72:6c:64
              */
-            aEncoded = Utf8StrFmt("%d=%s", aOptCode, aOptValue.text.c_str());
+            vrc = strEncoded.printfNoThrow("%d=%s", (int)enmOption, strValue.c_str());
             break;
         }
@@ -424,171 +547,250 @@
              *   "254@42=i hope you know what this means"
              */
-            aEncoded = Utf8StrFmt("%d@%d=%s", aOptCode, (int)aOptValue.encoding,
-                                  aOptValue.text.c_str());
+            vrc = strEncoded.printfNoThrow("%d@%d=%s", (int)enmOption, (int)enmEncoding, strValue.c_str());
             break;
         }
     }
-
-    return S_OK;
-}
-
-
-int DHCPServer::addOption(settings::DhcpOptionMap &aMap,
-                          DhcpOpt_T aOption, const com::Utf8Str &aValue)
-{
-    settings::DhcpOptValue OptValue;
-
+    return RT_SUCCESS(vrc) ? S_OK : E_OUTOFMEMORY;
+}
+
+
+/**
+ * worker for IDHCPServer::GetGlobalOptions.
+ */
+HRESULT DHCPServer::i_getAllOptions60(DHCPConfig &aSourceConfig, std::vector<com::Utf8Str> &aValues)
+{
+    /* Get the values using the new getter: */
+    std::vector<DhcpOpt_T>              Options;
+    std::vector<DHCPOptionEncoding_T>   Encodings;
+    std::vector<com::Utf8Str>           Values;
+    HRESULT hrc = aSourceConfig.i_getAllOptions(Options, Encodings, Values);
+    if (SUCCEEDED(hrc))
+    {
+        /* Encoding them using in the 6.0 style: */
+        size_t const cValues = Values.size();
+        aValues.resize(cValues);
+        for (size_t i = 0; i < cValues && SUCCEEDED(hrc); i++)
+            hrc = i_encode60Option(aValues[i], Options[i], Encodings[i], Values[i]);
+    }
+    return hrc;
+}
+
+
+/**
+ * Worker for legacy <=6.0 interfaces for adding options.
+ *
+ * @throws std::bad_alloc
+ */
+HRESULT DHCPServer::i_add60Option(DHCPConfig &aTargetConfig, DhcpOpt_T aOption, const com::Utf8Str &aValue)
+{
     if (aOption != 0)
-    {
-        OptValue = settings::DhcpOptValue(aValue, DhcpOptEncoding_Legacy);
-    }
+        return aTargetConfig.i_setOption(aOption, DHCPOptionEncoding_Legacy, aValue);
+
     /*
      * This is a kludge to sneak in option encoding information
      * through existing API.  We use option 0 and supply the real
-     * option/value in the same format that encodeOption() above
+     * option/value in the same format that i_encode60Option() above
      * produces for getter methods.
      */
-    else
-    {
-        uint8_t u8Code;
-        char    *pszNext;
-        int vrc = RTStrToUInt8Ex(aValue.c_str(), &pszNext, 10, &u8Code);
-        if (!RT_SUCCESS(vrc))
+    uint8_t u8Code;
+    char    *pszNext;
+    int vrc = RTStrToUInt8Ex(aValue.c_str(), &pszNext, 10, &u8Code);
+    if (RT_FAILURE(vrc))
+        return setErrorBoth(E_INVALIDARG, VERR_PARSE_ERROR);
+
+    DHCPOptionEncoding_T enmEncoding;
+    switch (*pszNext)
+    {
+        case ':':           /* support legacy format too */
+        {
+            enmEncoding = DHCPOptionEncoding_Legacy;
+            break;
+        }
+
+        case '=':
+        {
+            enmEncoding = DHCPOptionEncoding_Hex;
+            break;
+        }
+
+        case '@':
+        {
+            uint32_t u32Enc = 0;
+            vrc = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &u32Enc);
+            if (RT_FAILURE(vrc))
+                return setErrorBoth(E_INVALIDARG, VERR_PARSE_ERROR);
+            if (*pszNext != '=')
+                return setErrorBoth(E_INVALIDARG, VERR_PARSE_ERROR);
+            enmEncoding = (DHCPOptionEncoding_T)u32Enc;
+            break;
+        }
+
+        default:
             return VERR_PARSE_ERROR;
-
-        uint32_t u32Enc;
-        switch (*pszNext)
-        {
-            case ':':           /* support legacy format too */
+    }
+
+    return aTargetConfig.i_setOption(aOption, enmEncoding, com::Utf8Str(pszNext + 1));
+}
+
+
+HRESULT DHCPServer::addGlobalOption(DhcpOpt_T aOption, const com::Utf8Str &aValue)
+{
+    return i_add60Option(*m->globalConfig, aOption, aValue);
+}
+
+
+HRESULT DHCPServer::removeGlobalOption(DhcpOpt_T aOption)
+{
+    return m->globalConfig->i_removeOption(aOption);
+}
+
+
+HRESULT DHCPServer::removeGlobalOptions()
+{
+    return m->globalConfig->i_removeAllOptions();
+}
+
+
+HRESULT DHCPServer::getGlobalOptions(std::vector<com::Utf8Str> &aValues)
+{
+    return i_getAllOptions60(*m->globalConfig, aValues);
+}
+
+
+HRESULT DHCPServer::getVmConfigs(std::vector<com::Utf8Str> &aValues)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    try
+    {
+        aValues.resize(m->individualConfigs.size());
+        size_t i = 0;
+        for (Data::IndividualConfigIterator it = m->individualConfigs.begin(); it != m->individualConfigs.end(); ++it, i++)
+            if (it->second->i_getScope() != DHCPConfigScope_MAC)
+                aValues[i].printf("[%RTuuid]:%d", it->second->i_getMachineId().raw(), it->second->i_getSlot());
+            else
+                aValues[i].printf("[%RTmac]", it->second->i_getMACAddress());
+    }
+    catch (std::bad_alloc &)
+    {
+        return E_OUTOFMEMORY;
+    }
+
+    return S_OK;
+}
+
+
+HRESULT DHCPServer::i_vmNameToIdAndValidateSlot(const com::Utf8Str &aVmName, LONG aSlot, com::Guid &idMachine)
+{
+    if ((ULONG)aSlot <= 32)
+    {
+        /* Is it a UUID? */
+        idMachine = aVmName;
+        if (idMachine.isValid() && !idMachine.isZero())
+            return S_OK;
+
+        /* No, find the VM and get it's UUID. */
+        ComObjPtr<Machine> ptrMachine;
+        HRESULT hrc = m->pVirtualBox->i_findMachine(aVmName, false /*fPermitInaccessible*/, true /*aSetError*/, &ptrMachine);
+        if (SUCCEEDED(hrc))
+            idMachine = ptrMachine->i_getId();
+        return hrc;
+    }
+    return setError(E_INVALIDARG, tr("NIC slot number (%d) is out of range (0..32)"), aSlot);
+}
+
+
+/**
+ * Translates a VM name/id and slot to an individual configuration object.
+ *
+ * @returns COM status code.
+ * @param   a_strVmName         The VM name or ID.
+ * @param   a_uSlot             The NIC slot.
+ * @param   a_fCreateIfNeeded   Whether to create a new entry if not found.
+ * @param   a_rPtrConfig        Where to return the config object.  It's
+ *                              implicitly referenced, so we don't be returning
+ *                              with any locks held.
+ *
+ * @note    Caller must not be holding any locks!
+ */
+HRESULT DHCPServer::i_vmNameAndSlotToConfig(const com::Utf8Str &a_strVmName, LONG a_uSlot, bool a_fCreateIfNeeded,
+                                            ComObjPtr<DHCPIndividualConfig> &a_rPtrConfig)
+{
+    /*
+     * Validate the slot and normalize the name into a UUID.
+     */
+    com::Guid idMachine;
+    HRESULT hrc = i_vmNameToIdAndValidateSlot(a_strVmName, a_uSlot, idMachine);
+    if (SUCCEEDED(hrc))
+    {
+        Utf8Str strKey;
+        int vrc = strKey.printfNoThrow("%RTuuid/%u", idMachine.raw(), a_uSlot);
+        if (RT_SUCCESS(vrc))
+        {
+            /*
+             * Look it up.
+             */
             {
-                u32Enc = DhcpOptEncoding_Legacy;
-                break;
+                AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+                Data::IndividualConfigIterator it = m->individualConfigs.find(strKey);
+                if (it != m->individualConfigs.end())
+                {
+                    a_rPtrConfig = it->second;
+                    return S_OK;
+                }
             }
-
-            case '=':
+            if (a_fCreateIfNeeded)
             {
-                u32Enc = DhcpOptEncoding_Hex;
-                break;
+                /*
+                 * Create a new slot.
+                 */
+                /* Instantiate the object: */
+                hrc = a_rPtrConfig.createObject();
+                if (SUCCEEDED(hrc))
+                    hrc = a_rPtrConfig->initWithMachineIdAndSlot(m->pVirtualBox, this, idMachine, a_uSlot,
+                                                                 m->uIndividualMACAddressVersion - UINT32_MAX / 4);
+                if (SUCCEEDED(hrc))
+                {
+                    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+                    /* Check for creation race: */
+                    Data::IndividualConfigIterator it = m->individualConfigs.find(strKey);
+                    if (it != m->individualConfigs.end())
+                    {
+                        a_rPtrConfig.setNull();
+                        a_rPtrConfig = it->second;
+                        return S_OK;
+                    }
+
+                    /* Add it. */
+                    try
+                    {
+                        m->individualConfigs[strKey] = a_rPtrConfig;
+                        return S_OK;
+                    }
+                    catch (std::bad_alloc &)
+                    {
+                        hrc = E_OUTOFMEMORY;
+                    }
+                    a_rPtrConfig.setNull();
+                }
             }
-
-            case '@':
-            {
-                vrc = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &u32Enc);
-                if (!RT_SUCCESS(vrc))
-                    return VERR_PARSE_ERROR;
-                if (*pszNext != '=')
-                    return VERR_PARSE_ERROR;
-                break;
-            }
-
-            default:
-                return VERR_PARSE_ERROR;
-        }
-
-        aOption = (DhcpOpt_T)u8Code;
-        OptValue = settings::DhcpOptValue(pszNext + 1, (DhcpOptEncoding_T)u32Enc);
-    }
-
-    aMap[aOption] = OptValue;
-    return VINF_SUCCESS;
-}
-
-
-HRESULT DHCPServer::addGlobalOption(DhcpOpt_T aOption, const com::Utf8Str &aValue)
-{
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    int rc = addOption(m->GlobalDhcpOptions, aOption, aValue);
-    if (!RT_SUCCESS(rc))
-        return E_INVALIDARG;
-
-    /* Indirect way to understand that we're on NAT network */
-    if (aOption == DhcpOpt_Router)
-    {
-        m->router = true;
-    }
-
-    alock.release();
-
-    AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
-    return mVirtualBox->i_saveSettings();
-}
-
-
-HRESULT DHCPServer::removeGlobalOption(DhcpOpt_T aOption)
-{
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    settings::DhcpOptionMap::size_type cErased = m->GlobalDhcpOptions.erase(aOption);
-    if (!cErased)
-        return E_INVALIDARG;
-
-    alock.release();
-
-    AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
-    return mVirtualBox->i_saveSettings();
-}
-
-
-HRESULT DHCPServer::removeGlobalOptions()
-{
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-    m->GlobalDhcpOptions.clear();
-
-    alock.release();
-
-    AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
-    return mVirtualBox->i_saveSettings();
-}
-
-
-HRESULT DHCPServer::getGlobalOptions(std::vector<com::Utf8Str> &aValues)
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-    aValues.resize(m->GlobalDhcpOptions.size());
-    settings::DhcpOptionMap::const_iterator it;
-    size_t i = 0;
-    for (it = m->GlobalDhcpOptions.begin(); it != m->GlobalDhcpOptions.end(); ++it, ++i)
-    {
-        uint32_t OptCode = (*it).first;
-        const settings::DhcpOptValue &OptValue = (*it).second;
-
-        encodeOption(aValues[i], OptCode, OptValue);
-    }
-
-    return S_OK;
-}
-
-HRESULT DHCPServer::getVmConfigs(std::vector<com::Utf8Str> &aValues)
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-    aValues.resize(m->VmSlot2Options.size());
-    settings::VmSlot2OptionsMap::const_iterator it;
-    size_t i = 0;
-    for (it = m->VmSlot2Options.begin(); it != m->VmSlot2Options.end(); ++it, ++i)
-    {
-        aValues[i] = Utf8StrFmt("[%s]:%d", it->first.VmName.c_str(), it->first.Slot);
-    }
-
-    return S_OK;
-}
-
-
-HRESULT DHCPServer::addVmSlotOption(const com::Utf8Str &aVmName,
-                                    LONG aSlot,
-                                    DhcpOpt_T aOption,
-                                    const com::Utf8Str &aValue)
-{
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    settings::DhcpOptionMap &map = m->VmSlot2Options[settings::VmNameSlotKey(aVmName, aSlot)];
-    int rc = addOption(map, aOption, aValue);
-    if (!RT_SUCCESS(rc))
-        return E_INVALIDARG;
-
-    alock.release();
-
-    AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
-    return mVirtualBox->i_saveSettings();
+            else
+                hrc = VBOX_E_OBJECT_NOT_FOUND;
+        }
+        else
+            hrc = E_OUTOFMEMORY;
+    }
+    return hrc;
+}
+
+
+HRESULT DHCPServer::addVmSlotOption(const com::Utf8Str &aVmName, LONG aSlot, DhcpOpt_T aOption, const com::Utf8Str &aValue)
+{
+    ComObjPtr<DHCPIndividualConfig> ptrConfig;
+    HRESULT hrc = i_vmNameAndSlotToConfig(aVmName, aSlot, true, ptrConfig);
+    if (SUCCEEDED(hrc))
+        hrc = i_add60Option(*ptrConfig, aOption, aValue);
+    return hrc;
 }
 
@@ -596,14 +798,9 @@
 HRESULT DHCPServer::removeVmSlotOption(const com::Utf8Str &aVmName, LONG aSlot, DhcpOpt_T aOption)
 {
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-    settings::DhcpOptionMap &map = i_findOptMapByVmNameSlot(aVmName, aSlot);
-    settings::DhcpOptionMap::size_type cErased = map.erase(aOption);
-    if (!cErased)
-        return E_INVALIDARG;
-
-    alock.release();
-
-    AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
-    return mVirtualBox->i_saveSettings();
+    ComObjPtr<DHCPIndividualConfig> ptrConfig;
+    HRESULT hrc = i_vmNameAndSlotToConfig(aVmName, aSlot, false, ptrConfig);
+    if (SUCCEEDED(hrc))
+        hrc = ptrConfig->i_removeOption(aOption);
+    return hrc;
 }
 
@@ -611,36 +808,24 @@
 HRESULT DHCPServer::removeVmSlotOptions(const com::Utf8Str &aVmName, LONG aSlot)
 {
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-    settings::DhcpOptionMap &map = i_findOptMapByVmNameSlot(aVmName, aSlot);
-    map.clear();
-
-    alock.release();
-
-    AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
-    return mVirtualBox->i_saveSettings();
-}
-
-/**
- * this is mapping (vm, slot)
- */
-HRESULT DHCPServer::getVmSlotOptions(const com::Utf8Str &aVmName,
-                                     LONG aSlot,
-                                     std::vector<com::Utf8Str> &aValues)
-{
-
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-    settings::DhcpOptionMap &map = i_findOptMapByVmNameSlot(aVmName, aSlot);
-    aValues.resize(map.size());
-    size_t i = 0;
-    settings::DhcpOptionMap::const_iterator it;
-    for (it = map.begin(); it != map.end(); ++it, ++i)
-    {
-        uint32_t OptCode = (*it).first;
-        const settings::DhcpOptValue &OptValue = (*it).second;
-
-        encodeOption(aValues[i], OptCode, OptValue);
-    }
-
-    return S_OK;
+    ComObjPtr<DHCPIndividualConfig> ptrConfig;
+    HRESULT hrc = i_vmNameAndSlotToConfig(aVmName, aSlot, false, ptrConfig);
+    if (SUCCEEDED(hrc))
+        hrc = ptrConfig->i_removeAllOptions();
+    return hrc;
+}
+
+
+HRESULT DHCPServer::getVmSlotOptions(const com::Utf8Str &aVmName, LONG aSlot, std::vector<com::Utf8Str> &aValues)
+{
+    ComObjPtr<DHCPIndividualConfig> ptrConfig;
+    HRESULT hrc = i_vmNameAndSlotToConfig(aVmName, aSlot, false, ptrConfig);
+    if (SUCCEEDED(hrc))
+        hrc = i_getAllOptions60(*ptrConfig, aValues);
+    else if (hrc == VBOX_E_OBJECT_NOT_FOUND)
+    {
+        aValues.resize(0);
+        hrc = S_OK;
+    }
+    return hrc;
 }
 
@@ -648,41 +833,9 @@
 HRESULT DHCPServer::getMacOptions(const com::Utf8Str &aMAC, std::vector<com::Utf8Str> &aOption)
 {
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-    HRESULT hrc = S_OK;
-    ComPtr<IMachine> machine;
-    ComPtr<INetworkAdapter> nic;
-    settings::VmSlot2OptionsIterator it;
-    for(it = m->VmSlot2Options.begin(); it != m->VmSlot2Options.end(); ++it)
-    {
-        alock.release();
-        hrc = mVirtualBox->FindMachine(Bstr(it->first.VmName).raw(), machine.asOutParam());
-        alock.acquire();
-
-        if (FAILED(hrc))
-            continue;
-
-        alock.release();
-        hrc = machine->GetNetworkAdapter(it->first.Slot, nic.asOutParam());
-        alock.acquire();
-
-        if (FAILED(hrc))
-            continue;
-
-        com::Bstr mac;
-
-        alock.release();
-        hrc = nic->COMGETTER(MACAddress)(mac.asOutParam());
-        alock.acquire();
-
-        if (FAILED(hrc)) /* no MAC address ??? */
-            break;
-        if (!RTStrICmp(com::Utf8Str(mac).c_str(), aMAC.c_str()))
-            return getVmSlotOptions(it->first.VmName,
-                                    it->first.Slot,
-                                    aOption);
-    } /* end of for */
-
-    return hrc;
-}
+    RT_NOREF(aMAC, aOption);
+    AssertFailed();
+    return setError(E_NOTIMPL, tr("The GetMacOptions method has been discontinued as it was only supposed to be used by the DHCP server and it does not need it any more. sorry"));
+}
+
 
 HRESULT DHCPServer::getEventSource(ComPtr<IEventSource> &aEventSource)
@@ -693,10 +846,62 @@
 
 
-DECLINLINE(void) addOptionChild(xml::ElementNode *pParent, uint32_t OptCode, const settings::DhcpOptValue &OptValue)
-{
-    xml::ElementNode *pOption = pParent->createChild("Option");
-    pOption->setAttribute("name", OptCode);
-    pOption->setAttribute("encoding", OptValue.encoding);
-    pOption->setAttribute("value", OptValue.text.c_str());
+HRESULT DHCPServer::getGlobalConfig(ComPtr<IDHCPGlobalConfig> &aGlobalConfig)
+{
+    /* The global configuration is immutable, so no need to lock anything here. */
+    return m->globalConfig.queryInterfaceTo(aGlobalConfig.asOutParam());
+}
+
+
+HRESULT DHCPServer::getGroupConfigs(std::vector<ComPtr<IDHCPGroupConfig> > &aGroupConfigs)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+#if 0 /** @todo implement group configs   */
+
+    try
+    {
+        aGroupConfigs.resize(m->groupConfigs.size());
+    }
+    catch (std::bad_alloc &)
+    {
+        return E_OUTOFMEMORY;
+    }
+
+    size_t i = 0;
+    for (Data::GroupConfigIterator it = m->groupConfigs.begin(); it != m->groupConfigs.end(); ++it)
+    {
+        HRESULT hrc = it->second.queryInterfaceTo(aGroupConfigs[i].asOutParam());
+        if (FAILED(hrc))
+            return hrc;
+    }
+
+#else
+    aGroupConfigs.resize(0);
+#endif
+    return S_OK;
+}
+
+
+HRESULT DHCPServer::getIndividualConfigs(std::vector<ComPtr<IDHCPIndividualConfig> > &aIndividualConfigs)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    try
+    {
+        aIndividualConfigs.resize(m->individualConfigs.size());
+    }
+    catch (std::bad_alloc &)
+    {
+        return E_OUTOFMEMORY;
+    }
+
+    size_t i = 0;
+    for (Data::IndividualConfigIterator it = m->individualConfigs.begin(); it != m->individualConfigs.end(); ++it)
+    {
+        HRESULT hrc = it->second.queryInterfaceTo(aIndividualConfigs[i].asOutParam());
+        if (FAILED(hrc))
+            return hrc;
+    }
+
+    return S_OK;
 }
 
@@ -718,15 +923,127 @@
 
 
-HRESULT DHCPServer::start(const com::Utf8Str &aNetworkName,
-                          const com::Utf8Str &aTrunkName,
-                          const com::Utf8Str &aTrunkType)
-{
+/**
+ * @throws std::bad_alloc
+ */
+HRESULT DHCPServer::i_writeDhcpdConfig(const char *pszFilename, uint32_t uMACAddressVersion)
+{
+    /*
+     * Produce the DHCP server configuration.
+     */
+    xml::Document doc;
+    try
+    {
+        xml::ElementNode *pElmRoot = doc.createRootElement("DHCPServer");
+        pElmRoot->setAttribute("networkName", m->networkName);
+        if (m->trunkName.isNotEmpty())
+            pElmRoot->setAttribute("trunkName", m->trunkName);
+        pElmRoot->setAttribute("trunkType", m->trunkType);
+        pElmRoot->setAttribute("IPAddress",  m->IPAddress);
+        pElmRoot->setAttribute("lowerIP", m->lowerIP);
+        pElmRoot->setAttribute("upperIP", m->upperIP);
+        pElmRoot->setAttribute("leasesFilename", m->strLeasesFilename);
+        Utf8Str strNetworkMask;
+        HRESULT hrc = m->globalConfig->i_getNetworkMask(strNetworkMask);
+        if (FAILED(hrc))
+            return hrc;
+        pElmRoot->setAttribute("networkMask", strNetworkMask);
+
+        /*
+         * Process global options
+         */
+        m->globalConfig->i_writeDhcpdConfig(pElmRoot->createChild("Options"));
+
+        /*
+         * Groups.
+         */
+        //for (Data::GroupConfigIterator it = m->groupConfigs.begin(); it != m->groupConfigs.end(); ++it)
+        //    it->second->i_writeDhcpdConfig(pElmRoot->createChild("Config"));
+
+        /*
+         * Individual NIC configurations.
+         */
+        for (Data::IndividualConfigIterator it = m->individualConfigs.begin(); it != m->individualConfigs.end(); ++it)
+            if (it->second->i_isMACAddressResolved(uMACAddressVersion))
+                it->second->i_writeDhcpdConfig(pElmRoot->createChild("Config"));
+            else
+                LogRelFunc(("Skipping %RTuuid/%u, no MAC address.\n", it->second->i_getMachineId().raw(), it->second->i_getSlot()));
+    }
+    catch (std::bad_alloc &)
+    {
+        return E_OUTOFMEMORY;
+    }
+
+    /*
+     * Write out the document.
+     */
+    try
+    {
+        xml::XmlFileWriter writer(doc);
+        writer.write(pszFilename, false);
+    }
+    catch (...)
+    {
+        return E_FAIL;
+    }
+
+    return S_OK;
+}
+
+
+/** @todo r=bird: why do we get the network name passed in here?  it's the same
+ *        as m->strName, isn't it? */
+HRESULT DHCPServer::start(const com::Utf8Str &aNetworkName, const com::Utf8Str &aTrunkName, const com::Utf8Str &aTrunkType)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
     /* Silently ignore attempts to run disabled servers. */
     if (!m->enabled)
         return S_OK;
 
-    /**
-     * @todo: the existing code cannot handle concurrent attempts to start DHCP server.
-     * Note that technically it may receive different parameters from different callers.
+    /*
+     * Resolve the MAC addresses.  This requires us to leave the lock.
+     */
+    uint32_t uMACAddressVersion = m->uIndividualMACAddressVersion;
+    if (m->individualConfigs.size() > 0)
+    {
+        m->uIndividualMACAddressVersion = uMACAddressVersion + 1;
+
+        /* Retain pointers to all the individual configuration objects so we
+           can safely access these after releaseing the lock: */
+        std::vector< ComObjPtr<DHCPIndividualConfig> > vecIndividualConfigs;
+        try
+        {
+            vecIndividualConfigs.resize(m->individualConfigs.size());
+        }
+        catch (std::bad_alloc &)
+        {
+            return E_OUTOFMEMORY;
+        }
+        size_t i = 0;
+        for (Data::IndividualConfigIterator it = m->individualConfigs.begin(); it != m->individualConfigs.end(); ++it, i++)
+            vecIndividualConfigs[i] = it->second;
+
+        /* Drop the lock and resolve the MAC addresses: */
+        alock.release();
+
+        i = vecIndividualConfigs.size();
+        while (i-- > 0)
+            vecIndividualConfigs[i]->i_resolveMACAddress(uMACAddressVersion);
+
+        /* Reacquire the lock  */
+        alock.acquire();
+        if (!m->enabled)
+            return S_OK;
+    }
+
+    /*
+     * Refuse to start a 2nd DHCP server instance for the same network.
+     */
+    if (m->dhcp.isRunning())
+        return setErrorBoth(VBOX_E_OBJECT_IN_USE, VERR_PROCESS_RUNNING,
+                            tr("Cannot start DHCP server because it is already running"));
+
+    /*
+     * Copy the startup parameters.
      */
     m->networkName = aNetworkName;
@@ -734,143 +1051,62 @@
     m->trunkType   = aTrunkType;
     HRESULT hrc = i_calcLeasesFilename(aNetworkName);
-    if (FAILED(hrc))
-        return hrc;
-
-    m->dhcp.resetArguments();
-
-#ifdef VBOX_WITH_DHCPD
-
-    /*
-     * Create configuration file path.
-     */
-    /** @todo put this next to the leases file.   */
-    int rc = RTPathTemp(m->szTempConfigFileName, sizeof(m->szTempConfigFileName));
-    if (RT_SUCCESS(rc))
-        rc = RTPathAppend(m->szTempConfigFileName, sizeof(m->szTempConfigFileName), "dhcp-config-XXXXX.xml");
-    if (RT_SUCCESS(rc))
-        rc = RTFileCreateTemp(m->szTempConfigFileName, 0600);
-    if (RT_FAILURE(rc))
-    {
+    if (SUCCEEDED(hrc))
+    {
+        /*
+         * Create configuration file path and write out the configuration.
+         */
+        /** @todo put this next to the leases file.   */
+        int vrc = RTPathTemp(m->szTempConfigFileName, sizeof(m->szTempConfigFileName));
+        if (RT_SUCCESS(vrc))
+            vrc = RTPathAppend(m->szTempConfigFileName, sizeof(m->szTempConfigFileName), "dhcp-config-XXXXX.xml");
+        if (RT_SUCCESS(vrc))
+            vrc = RTFileCreateTemp(m->szTempConfigFileName, 0600);
+        if (RT_SUCCESS(vrc))
+        {
+            hrc = i_writeDhcpdConfig(m->szTempConfigFileName, uMACAddressVersion);
+            if (SUCCEEDED(hrc))
+            {
+                /*
+                 * Setup the arguments and start the DHCP server.
+                 */
+                m->dhcp.resetArguments();
+                vrc = m->dhcp.addArgPair(DHCPServerRunner::kDsrKeyConfig, m->szTempConfigFileName);
+                if (RT_SUCCESS(vrc))
+                    vrc = m->dhcp.addArgPair(DHCPServerRunner::kDsrKeyComment, m->networkName.c_str());
+                if (RT_SUCCESS(vrc))
+                    vrc = m->dhcp.start(true /*aKillProcessOnStop*/);
+                if (RT_FAILURE(vrc))
+                    hrc = setErrorVrc(vrc, tr("Failed to start DHCP server for '%s': %Rrc"), m->strName.c_str(), vrc);
+            }
+            if (FAILED(hrc))
+            {
+                RTFileDelete(m->szTempConfigFileName);
+                m->szTempConfigFileName[0] = '\0';
+            }
+        }
+        else
+        {
+            m->szTempConfigFileName[0] = '\0';
+            hrc = setErrorVrc(vrc);
+        }
+    }
+    return hrc;
+}
+
+
+HRESULT DHCPServer::stop(void)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    if (m->szTempConfigFileName[0])
+    {
+        RTFileDelete(m->szTempConfigFileName);
         m->szTempConfigFileName[0] = '\0';
-        return E_FAIL;
-    }
-
-    /*
-     * Produce the DHCP server configuration.
-     */
-    xml::Document doc;
-    xml::ElementNode *pElmRoot = doc.createRootElement("DHCPServer");
-    pElmRoot->setAttribute("networkName", m->networkName);
-    if (m->trunkName.isNotEmpty())
-        pElmRoot->setAttribute("trunkName", m->trunkName);
-    pElmRoot->setAttribute("trunkType", m->trunkType);
-    pElmRoot->setAttribute("IPAddress",  m->IPAddress);
-    pElmRoot->setAttribute("networkMask", m->GlobalDhcpOptions[DhcpOpt_SubnetMask].text);
-    pElmRoot->setAttribute("lowerIP", m->lowerIP);
-    pElmRoot->setAttribute("upperIP", m->upperIP);
-    pElmRoot->setAttribute("leasesFilename", m->strLeasesFilename);
-
-    /* Process global options */
-    xml::ElementNode *pOptions = pElmRoot->createChild("Options");
-    for (settings::DhcpOptionMap::const_iterator it = m->GlobalDhcpOptions.begin(); it != m->GlobalDhcpOptions.end(); ++it)
-        addOptionChild(pOptions, (*it).first, (*it).second);
-
-    /* Process network-adapter-specific options */
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-    hrc = S_OK;
-    ComPtr<IMachine> machine;
-    ComPtr<INetworkAdapter> nic;
-    settings::VmSlot2OptionsIterator it;
-    for (it = m->VmSlot2Options.begin(); it != m->VmSlot2Options.end(); ++it)
-    {
-        alock.release();
-        hrc = mVirtualBox->FindMachine(Bstr(it->first.VmName).raw(), machine.asOutParam());
-        alock.acquire();
-
-        if (FAILED(hrc))
-            continue;
-
-        alock.release();
-        hrc = machine->GetNetworkAdapter(it->first.Slot, nic.asOutParam());
-        alock.acquire();
-
-        if (FAILED(hrc))
-            continue;
-
-        com::Bstr mac;
-
-        alock.release();
-        hrc = nic->COMGETTER(MACAddress)(mac.asOutParam());
-        alock.acquire();
-
-        if (FAILED(hrc)) /* no MAC address ??? */
-            continue;
-
-        /* Convert MAC address from XXXXXXXXXXXX to XX:XX:XX:XX:XX:XX */
-        Utf8Str strMacWithoutColons(mac);
-        const char *pszSrc = strMacWithoutColons.c_str();
-        RTMAC binaryMac;
-        if (RTStrConvertHexBytes(pszSrc, &binaryMac, sizeof(binaryMac), 0) != VINF_SUCCESS)
-            continue;
-        char szMac[18]; /* "XX:XX:XX:XX:XX:XX" */
-        if (RTStrPrintHexBytes(szMac, sizeof(szMac), &binaryMac, sizeof(binaryMac), RTSTRPRINTHEXBYTES_F_SEP_COLON) != VINF_SUCCESS)
-            continue;
-
-        xml::ElementNode *pMacConfig = pElmRoot->createChild("Config");
-        pMacConfig->setAttribute("MACAddress", szMac);
-
-        com::Utf8Str encodedOption;
-        settings::DhcpOptionMap &map = i_findOptMapByVmNameSlot(it->first.VmName, it->first.Slot);
-        settings::DhcpOptionMap::const_iterator itAdapterOption;
-        for (itAdapterOption = map.begin(); itAdapterOption != map.end(); ++itAdapterOption)
-            addOptionChild(pMacConfig, (*itAdapterOption).first, (*itAdapterOption).second);
-    }
-
-    xml::XmlFileWriter writer(doc);
-    writer.write(m->szTempConfigFileName, false);
-
-    m->dhcp.addArgPair(DHCPServerRunner::kDsrKeyConfig, m->szTempConfigFileName);
-    m->dhcp.addArgPair(DHCPServerRunner::kDsrKeyComment, m->networkName.c_str());
-
-#else /* !VBOX_WITH_DHCPD */
-    /* Main is needed for NATNetwork */
-    if (m->router)
-        m->dhcp.addArgPair(NetworkServiceRunner::kpszKeyNeedMain, "on");
-
-    /* Commmon Network Settings */
-    m->dhcp.addArgPair(NetworkServiceRunner::kpszKeyNetwork, aNetworkName.c_str());
-    if (!aTrunkName.isEmpty())
-        m->dhcp.addArgPair(NetworkServiceRunner::kpszTrunkName, aTrunkName.c_str());
-    m->dhcp.addArgPair(NetworkServiceRunner::kpszKeyTrunkType, aTrunkType.c_str());
-
-    /* XXX: should this MAC default initialization moved to NetworkServiceRunner? */
-    char strMAC[32];
-    Guid guid;
-    guid.create();
-    RTStrPrintf (strMAC, sizeof(strMAC), "08:00:27:%02X:%02X:%02X",
-                 guid.raw()->au8[0], guid.raw()->au8[1], guid.raw()->au8[2]);
-    m->dhcp.addArgPair(NetworkServiceRunner::kpszMacAddress, strMAC);
-    m->dhcp.addArgPair(NetworkServiceRunner::kpszIpAddress, m->IPAddress.c_str());
-    m->dhcp.addArgPair(NetworkServiceRunner::kpszIpNetmask, m->GlobalDhcpOptions[DhcpOpt_SubnetMask].text.c_str());
-    m->dhcp.addArgPair(DHCPServerRunner::kDsrKeyLowerIp, m->lowerIP.c_str());
-    m->dhcp.addArgPair(DHCPServerRunner::kDsrKeyUpperIp, m->upperIP.c_str());
-#endif /* !VBOX_WITH_DHCPD */
-
-    /* XXX: This parameters Dhcp Server will fetch via API */
-    return RT_FAILURE(m->dhcp.start(!m->router /* KillProcOnExit */)) ? E_FAIL : S_OK;
-    //m->dhcp.detachFromServer(); /* need to do this to avoid server shutdown on runner destruction */
-}
-
-
-HRESULT DHCPServer::stop(void)
-{
-#ifdef VBOX_WITH_DHCPD
-    if (m->szTempConfigFileName[0])
-    {
-        RTFileDelete(m->szTempConfigFileName);
-        m->szTempConfigFileName[0] = 0;
-    }
-#endif /* VBOX_WITH_DHCPD */
-    return RT_FAILURE(m->dhcp.stop()) ? E_FAIL : S_OK;
+    }
+
+    int vrc = m->dhcp.stop();
+    if (RT_SUCCESS(vrc))
+        return S_OK;
+    return setErrorVrc(vrc);
 }
 
@@ -901,5 +1137,5 @@
     if (m->strLeasesFilename.isEmpty())
     {
-        HRESULT hrc = i_calcLeasesFilename(m->networkName.isEmpty() ? mName : m->networkName);
+        HRESULT hrc = i_calcLeasesFilename(m->networkName.isEmpty() ? m->strName : m->networkName);
         if (FAILED(hrc))
             return hrc;
@@ -1020,4 +1256,90 @@
 
 
+HRESULT DHCPServer::getConfig(DHCPConfigScope_T aScope, const com::Utf8Str &aName, ULONG aSlot, BOOL aMayAdd,
+                              ComPtr<IDHCPConfig> &aConfig)
+{
+    if (aSlot != 0 && aScope != DHCPConfigScope_MachineNIC)
+        return setError(E_INVALIDARG, tr("The 'slot' argument must be zero for all but the MachineNIC scope!"));
+
+    switch (aScope)
+    {
+        case DHCPConfigScope_Global:
+            if (aName.isNotEmpty())
+                return setError(E_INVALIDARG, tr("The name must be empty or NULL for the Global scope!"));
+            /* No locking required here. */
+            return m->globalConfig.queryInterfaceTo(aConfig.asOutParam());
+
+        case DHCPConfigScope_Group:
+            return setError(E_NOTIMPL, tr("Groups are not yet implemented, sorry."));
+
+        case DHCPConfigScope_MachineNIC:
+        {
+            ComObjPtr<DHCPIndividualConfig> ptrIndividualConfig;
+            HRESULT hrc = i_vmNameAndSlotToConfig(aName, aSlot, aMayAdd != FALSE, ptrIndividualConfig);
+            if (SUCCEEDED(hrc))
+                hrc = ptrIndividualConfig.queryInterfaceTo(aConfig.asOutParam());
+            return hrc;
+        }
+
+        case DHCPConfigScope_MAC:
+        {
+            /* Check and Normalize the MAC address into a key: */
+            RTMAC MACAddress;
+            int vrc = RTNetStrToMacAddr(aName.c_str(), &MACAddress);
+            if (RT_SUCCESS(vrc))
+            {
+                Utf8Str strKey;
+                vrc = strKey.printfNoThrow("%RTmac", &MACAddress);
+                if (RT_SUCCESS(vrc))
+                {
+                    /* Look up the MAC address: */
+                    {
+                        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+                        Data::IndividualConfigIterator it = m->individualConfigs.find(strKey);
+                        if (it != m->individualConfigs.end())
+                            return it->second.queryInterfaceTo(aConfig.asOutParam());
+                    }
+                    if (aMayAdd)
+                    {
+                        /* Create a new individiual configuration: */
+                        ComObjPtr<DHCPIndividualConfig> ptrIndividualConfig;
+                        HRESULT hrc = ptrIndividualConfig.createObject();
+                        if (SUCCEEDED(hrc))
+                            hrc = ptrIndividualConfig->initWithMACAddress(m->pVirtualBox, this, &MACAddress);
+                        if (SUCCEEDED(hrc))
+                        {
+                            AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+                            /* Check for insertion race: */
+                            Data::IndividualConfigIterator it = m->individualConfigs.find(strKey);
+                            if (it != m->individualConfigs.end())
+                                return it->second.queryInterfaceTo(aConfig.asOutParam()); /* creation race*/
+
+                            /* Try insert it: */
+                            try
+                            {
+                                m->individualConfigs[strKey] = ptrIndividualConfig;
+                            }
+                            catch (std::bad_alloc &)
+                            {
+                                return E_OUTOFMEMORY;
+                            }
+                            return ptrIndividualConfig.queryInterfaceTo(aConfig.asOutParam());
+                        }
+                    }
+                    else
+                        return setError(VBOX_E_OBJECT_NOT_FOUND, tr("Found no configuration for MAC address %s"), strKey.c_str());
+                }
+                return E_OUTOFMEMORY;
+            }
+            return setErrorBoth(E_INVALIDARG, vrc, tr("Invalid MAC address: %s"), aName.c_str());
+        }
+
+        default:
+            return E_FAIL;
+    }
+}
+
+
 /**
  * Calculates and updates the value of strLeasesFilename given @a aNetwork.
@@ -1028,5 +1350,5 @@
 
     /* The lease file must be the same as we used the last time, so careful when changing this code. */
-    int vrc = m->strLeasesFilename.assignNoThrow(mVirtualBox->i_homeDir());
+    int vrc = m->strLeasesFilename.assignNoThrow(m->pVirtualBox->i_homeDir());
     if (RT_SUCCESS(vrc))
         vrc = RTPathAppendCxx(m->strLeasesFilename, aNetwork);
@@ -1042,8 +1364,2 @@
 }
 
-settings::DhcpOptionMap &DHCPServer::i_findOptMapByVmNameSlot(const com::Utf8Str &aVmName,
-                                                              LONG aSlot)
-{
-    return m->VmSlot2Options[settings::VmNameSlotKey(aVmName, aSlot)];
-}
-
Index: /trunk/src/VBox/Main/xml/Settings.cpp
===================================================================
--- /trunk/src/VBox/Main/xml/Settings.cpp	(revision 79731)
+++ /trunk/src/VBox/Main/xml/Settings.cpp	(revision 79732)
@@ -1614,7 +1614,7 @@
  * Constructor. Needs to set sane defaults which stand the test of time.
  */
-DhcpOptValue::DhcpOptValue() :
-    text(),
-    encoding(DhcpOptEncoding_Legacy)
+DhcpOptValue::DhcpOptValue()
+    : strValue()
+    , enmEncoding(DHCPOptionEncoding_Legacy)
 {
 }
@@ -1623,28 +1623,31 @@
  * Non-standard constructor.
  */
-DhcpOptValue::DhcpOptValue(const com::Utf8Str &aText, DhcpOptEncoding_T aEncoding) :
-    text(aText),
-    encoding(aEncoding)
-{
-}
-
-/**
- * Non-standard constructor.
- */
-VmNameSlotKey::VmNameSlotKey(const com::Utf8Str& aVmName, LONG aSlot) :
-    VmName(aVmName),
-    Slot(aSlot)
-{
-}
-
-/**
- * Non-standard comparison operator.
- */
-bool VmNameSlotKey::operator< (const VmNameSlotKey& that) const
-{
-    if (VmName == that.VmName)
-        return Slot < that.Slot;
-    else
-        return VmName < that.VmName;
+DhcpOptValue::DhcpOptValue(const com::Utf8Str &aText, DHCPOptionEncoding_T aEncoding)
+    : strValue(aText)
+    , enmEncoding(aEncoding)
+{
+}
+
+
+/**
+ * Default constructor.
+ */
+DHCPConfig::DHCPConfig()
+    : OptionMap()
+    , secMinLeaseTime(0)
+    , secDefaultLeaseTime(0)
+    , secMaxLeaseTime(0)
+{
+}
+
+/**
+ * Default constructor.
+ */
+DHCPIndividualConfig::DHCPIndividualConfig()
+    : DHCPConfig()
+    , strMACAddress()
+    , strVMName()
+    , uSlot(0)
+{
 }
 
@@ -1652,6 +1655,6 @@
  * Constructor. Needs to set sane defaults which stand the test of time.
  */
-DHCPServer::DHCPServer() :
-    fEnabled(false)
+DHCPServer::DHCPServer()
+    : fEnabled(false)
 {
 }
@@ -1705,4 +1708,77 @@
 
 /**
+ * Builds the XML tree for the DHCP servers.
+ */
+void MainConfigFile::buildDHCPServers(xml::ElementNode &elmDHCPServers, DHCPServersList const &ll)
+{
+    for (DHCPServersList::const_iterator it = ll.begin(); it != ll.end(); ++it)
+    {
+        const DHCPServer &srv = *it;
+        xml::ElementNode *pElmThis = elmDHCPServers.createChild("DHCPServer");
+
+        pElmThis->setAttribute("networkName", srv.strNetworkName);
+        pElmThis->setAttribute("IPAddress", srv.strIPAddress);
+        DhcpOptConstIterator itOpt = srv.GlobalConfig.OptionMap.find(DhcpOpt_SubnetMask);
+        if (itOpt != srv.GlobalConfig.OptionMap.end())
+            pElmThis->setAttribute("networkMask", itOpt->second.strValue);
+        pElmThis->setAttribute("lowerIP", srv.strIPLower);
+        pElmThis->setAttribute("upperIP", srv.strIPUpper);
+        pElmThis->setAttribute("enabled", (srv.fEnabled) ? 1 : 0);        // too bad we chose 1 vs. 0 here
+
+        /* We don't want duplicate validation check of networkMask here*/
+        if (srv.GlobalConfig.OptionMap.size() > (itOpt != srv.GlobalConfig.OptionMap.end() ? 1 : 0))
+        {
+            xml::ElementNode *pElmOptions = pElmThis->createChild("Options");
+            buildDHCPOptions(*pElmOptions, srv.GlobalConfig, true);
+        }
+
+        if (srv.IndividualConfigs.size() > 0)
+        {
+            for (DHCPIndividualConfigMap::const_iterator itHost = srv.IndividualConfigs.begin();
+                 itHost != srv.IndividualConfigs.end(); ++itHost)
+            {
+                DHCPIndividualConfig const &rIndividualConfig = itHost->second;
+
+                xml::ElementNode *pElmConfig = pElmThis->createChild("Config");
+                if (rIndividualConfig.strMACAddress.isNotEmpty())
+                    pElmConfig->setAttribute("MACAddress", rIndividualConfig.strMACAddress);
+                if (rIndividualConfig.strVMName.isNotEmpty())
+                    pElmConfig->setAttribute("vm-name", rIndividualConfig.strVMName);
+                if (rIndividualConfig.uSlot != 0 || rIndividualConfig.strVMName.isNotEmpty())
+                    pElmConfig->setAttribute("slot", rIndividualConfig.strVMName);
+                if (rIndividualConfig.strFixedAddress.isNotEmpty())
+                    pElmConfig->setAttribute("fixedAddress", rIndividualConfig.strFixedAddress);
+                buildDHCPOptions(*pElmConfig, rIndividualConfig, false);
+            }
+        }
+     }
+}
+
+/**
+ * Worker for buildDHCPServers() that builds Options or Config element trees.
+ */
+void MainConfigFile::buildDHCPOptions(xml::ElementNode &elmOptions, DHCPConfig const &rConfig, bool fSkipSubnetMask)
+{
+    /* Generic (and optional) attributes on the Options or Config element: */
+    if (rConfig.secMinLeaseTime > 0)
+        elmOptions.setAttribute("secMinLeaseTime", rConfig.secMinLeaseTime);
+    if (rConfig.secDefaultLeaseTime > 0)
+        elmOptions.setAttribute("secDefaultLeaseTime", rConfig.secDefaultLeaseTime);
+    if (rConfig.secMaxLeaseTime > 0)
+        elmOptions.setAttribute("secMaxLeaseTime", rConfig.secMaxLeaseTime);
+
+    /* The DHCP options are <Option> child elements: */
+    for (DhcpOptConstIterator it = rConfig.OptionMap.begin(); it != rConfig.OptionMap.end(); ++it)
+        if (it->first != DhcpOpt_SubnetMask || !fSkipSubnetMask)
+        {
+            xml::ElementNode *pElmOption = elmOptions.createChild("Option");
+            pElmOption->setAttribute("name", it->first);
+            pElmOption->setAttribute("value", it->second.strValue);
+            if (it->second.enmEncoding != DHCPOptionEncoding_Legacy)
+                pElmOption->setAttribute("encoding", (int32_t)it->second.enmEncoding);
+        }
+}
+
+/**
  * Reads in the \<DHCPServers\> chunk.
  * @param elmDHCPServers
@@ -1719,26 +1795,45 @@
             if (   pelmServer->getAttributeValue("networkName", srv.strNetworkName)
                 && pelmServer->getAttributeValue("IPAddress", srv.strIPAddress)
-                && pelmServer->getAttributeValue("networkMask", srv.GlobalDhcpOptions[DhcpOpt_SubnetMask].text)
+                && pelmServer->getAttributeValue("networkMask", srv.GlobalConfig.OptionMap[DhcpOpt_SubnetMask].strValue)
                 && pelmServer->getAttributeValue("lowerIP", srv.strIPLower)
                 && pelmServer->getAttributeValue("upperIP", srv.strIPUpper)
                 && pelmServer->getAttributeValue("enabled", srv.fEnabled) )
             {
-                xml::NodesLoop nlOptions(*pelmServer, "Options");
-                const xml::ElementNode *options;
-                /* XXX: Options are in 1:1 relation to DHCPServer */
-
-                while ((options = nlOptions.forAllNodes()))
+                /* Global options: */
+                const xml::ElementNode *pElmOptions;
+                xml::NodesLoop          nlOptions(*pelmServer, "Options");
+                while ((pElmOptions = nlOptions.forAllNodes()) != NULL) /** @todo this loop makes no sense, there can only be one <Options> child. */
+                    readDHCPOptions(srv.GlobalConfig, *pElmOptions, true /*fIgnoreSubnetMask*/);
+
+                /* host specific configuration: */
+                xml::NodesLoop nlConfig(*pelmServer, "Config");
+                const xml::ElementNode *pElmConfig;
+                while ((pElmConfig = nlConfig.forAllNodes()) != NULL)
                 {
-                    readDhcpOptions(srv.GlobalDhcpOptions, *options);
-                } /* end of forall("Options") */
-                xml::NodesLoop nlConfig(*pelmServer, "Config");
-                const xml::ElementNode *cfg;
-                while ((cfg = nlConfig.forAllNodes()))
-                {
-                    com::Utf8Str strVmName;
-                    uint32_t u32Slot;
-                    cfg->getAttributeValue("vm-name", strVmName);
-                    cfg->getAttributeValue("slot", u32Slot);
-                    readDhcpOptions(srv.VmSlot2OptionsM[VmNameSlotKey(strVmName, u32Slot)], *cfg);
+                    com::Utf8Str strMACAddress;
+                    if (!pElmConfig->getAttributeValue("MACAddress", strMACAddress))
+                        strMACAddress.setNull();
+
+                    com::Utf8Str strVMName;
+                    if (!pElmConfig->getAttributeValue("vm-name", strVMName))
+                        strVMName.setNull();
+
+                    uint32_t uSlot;
+                    if (!pElmConfig->getAttributeValue("slot", uSlot))
+                        uSlot = 0;
+
+                    com::Utf8Str strKey;
+                    if (strVMName.isNotEmpty())
+                        strKey.printf("%s/%u", strVMName.c_str(), uSlot);
+                    else
+                        strKey.printf("%s/%u", strMACAddress.c_str(), uSlot);
+
+                    DHCPIndividualConfig &rIndividualConfig = srv.IndividualConfigs[strKey];
+                    rIndividualConfig.strMACAddress = strMACAddress;
+                    rIndividualConfig.strVMName     = strVMName;
+                    rIndividualConfig.uSlot         = uSlot;
+                    pElmConfig->getAttributeValue("fixedAddress", rIndividualConfig.strFixedAddress);
+
+                    readDHCPOptions(rIndividualConfig, *pElmConfig, false /*fIgnoreSubnetMask*/);
                 }
                 llDhcpServers.push_back(srv);
@@ -1750,24 +1845,36 @@
 }
 
-void MainConfigFile::readDhcpOptions(DhcpOptionMap& map,
-                                     const xml::ElementNode& options)
-{
-    xml::NodesLoop nl2(options, "Option");
-    const xml::ElementNode *opt;
-    while ((opt = nl2.forAllNodes()))
+/**
+ * Worker for readDHCPServers that reads a configuration, either global,
+ * group or host (VM+NIC) specific.
+ */
+void MainConfigFile::readDHCPOptions(DHCPConfig &rConfig, const xml::ElementNode &elmConfig, bool fIgnoreSubnetMask)
+{
+    /* Generic (and optional) attributes on the Options or Config element: */
+    if (!elmConfig.getAttributeValue("secMinLeaseTime", rConfig.secMinLeaseTime))
+        rConfig.secMinLeaseTime = 0;
+    if (!elmConfig.getAttributeValue("secDefaultLeaseTime", rConfig.secDefaultLeaseTime))
+        rConfig.secDefaultLeaseTime = 0;
+    if (!elmConfig.getAttributeValue("secMaxLeaseTime", rConfig.secMaxLeaseTime))
+        rConfig.secMaxLeaseTime = 0;
+
+    /* The DHCP options are <Option> child elements: */
+    xml::NodesLoop          nl2(elmConfig, "Option");
+    const xml::ElementNode *pElmOption;
+    while ((pElmOption = nl2.forAllNodes()) != NULL)
     {
         DhcpOpt_T OptName;
-        com::Utf8Str OptText;
-        int32_t OptEnc = DhcpOptEncoding_Legacy;
-
-        opt->getAttributeValue("name", (uint32_t&)OptName);
-
-        if (OptName == DhcpOpt_SubnetMask)
+        pElmOption->getAttributeValue("name", (uint32_t&)OptName);
+        if (OptName == DhcpOpt_SubnetMask && fIgnoreSubnetMask)
             continue;
 
-        opt->getAttributeValue("value", OptText);
-        opt->getAttributeValue("encoding", OptEnc);
-
-        map[OptName] = DhcpOptValue(OptText, (DhcpOptEncoding_T)OptEnc);
+        com::Utf8Str strValue;
+        pElmOption->getAttributeValue("value", strValue);
+
+        int32_t iOptEnc;
+        if (!pElmOption->getAttributeValue("encoding", iOptEnc))
+            iOptEnc = DHCPOptionEncoding_Legacy;
+
+        rConfig.OptionMap[OptName] = DhcpOptValue(strValue, (DHCPOptionEncoding_T)iOptEnc);
     } /* end of forall("Option") */
 
@@ -2074,12 +2181,11 @@
     {
         DHCPServer srv;
-        srv.strNetworkName =
 #ifdef RT_OS_WINDOWS
-            "HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter";
+        srv.strNetworkName = "HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter";
 #else
-            "HostInterfaceNetworking-vboxnet0";
+        srv.strNetworkName = "HostInterfaceNetworking-vboxnet0";
 #endif
         srv.strIPAddress = "192.168.56.100";
-        srv.GlobalDhcpOptions[DhcpOpt_SubnetMask] = DhcpOptValue("255.255.255.0");
+        srv.GlobalConfig.OptionMap[DhcpOpt_SubnetMask] = DhcpOptValue("255.255.255.0");
         srv.strIPLower = "192.168.56.101";
         srv.strIPUpper = "192.168.56.254";
@@ -2137,75 +2243,6 @@
     buildMediaRegistry(*pelmGlobal, mediaRegistry);
 
-    xml::ElementNode *pelmNetserviceRegistry = pelmGlobal->createChild("NetserviceRegistry");
-    xml::ElementNode *pelmDHCPServers = pelmNetserviceRegistry->createChild("DHCPServers");
-    for (DHCPServersList::const_iterator it = llDhcpServers.begin();
-         it != llDhcpServers.end();
-         ++it)
-    {
-        const DHCPServer &d = *it;
-        xml::ElementNode *pelmThis = pelmDHCPServers->createChild("DHCPServer");
-        DhcpOptConstIterator itOpt;
-        itOpt = d.GlobalDhcpOptions.find(DhcpOpt_SubnetMask);
-
-        pelmThis->setAttribute("networkName", d.strNetworkName);
-        pelmThis->setAttribute("IPAddress", d.strIPAddress);
-        if (itOpt != d.GlobalDhcpOptions.end())
-            pelmThis->setAttribute("networkMask", itOpt->second.text);
-        pelmThis->setAttribute("lowerIP", d.strIPLower);
-        pelmThis->setAttribute("upperIP", d.strIPUpper);
-        pelmThis->setAttribute("enabled", (d.fEnabled) ? 1 : 0);        // too bad we chose 1 vs. 0 here
-        /* We assume that if there're only 1 element it means that */
-        size_t cOpt = d.GlobalDhcpOptions.size();
-        /* We don't want duplicate validation check of networkMask here*/
-        if (   (   itOpt == d.GlobalDhcpOptions.end()
-                && cOpt > 0)
-            || cOpt > 1)
-        {
-            xml::ElementNode *pelmOptions = pelmThis->createChild("Options");
-            for (itOpt = d.GlobalDhcpOptions.begin();
-                 itOpt != d.GlobalDhcpOptions.end();
-                 ++itOpt)
-            {
-                if (itOpt->first == DhcpOpt_SubnetMask)
-                    continue;
-
-                xml::ElementNode *pelmOpt = pelmOptions->createChild("Option");
-
-                if (!pelmOpt)
-                    break;
-
-                pelmOpt->setAttribute("name", itOpt->first);
-                pelmOpt->setAttribute("value", itOpt->second.text);
-                if (itOpt->second.encoding != DhcpOptEncoding_Legacy)
-                    pelmOpt->setAttribute("encoding", (int)itOpt->second.encoding);
-            }
-        } /* end of if */
-
-        if (d.VmSlot2OptionsM.size() > 0)
-        {
-            VmSlot2OptionsConstIterator itVmSlot;
-            DhcpOptConstIterator itOpt1;
-            for(itVmSlot = d.VmSlot2OptionsM.begin();
-                itVmSlot != d.VmSlot2OptionsM.end();
-                ++itVmSlot)
-            {
-                xml::ElementNode *pelmCfg = pelmThis->createChild("Config");
-                pelmCfg->setAttribute("vm-name", itVmSlot->first.VmName);
-                pelmCfg->setAttribute("slot", (int32_t)itVmSlot->first.Slot);
-
-                for (itOpt1 = itVmSlot->second.begin();
-                     itOpt1 != itVmSlot->second.end();
-                     ++itOpt1)
-                {
-                    xml::ElementNode *pelmOpt = pelmCfg->createChild("Option");
-                    pelmOpt->setAttribute("name", itOpt1->first);
-                    pelmOpt->setAttribute("value", itOpt1->second.text);
-                    if (itOpt1->second.encoding != DhcpOptEncoding_Legacy)
-                        pelmOpt->setAttribute("encoding", (int)itOpt1->second.encoding);
-                }
-            }
-        } /* and of if */
-
-     }
+    xml::ElementNode *pelmNetServiceRegistry = pelmGlobal->createChild("NetserviceRegistry"); /** @todo r=bird: wrong capitalization of NetServiceRegistry. sigh. */
+    buildDHCPServers(*pelmNetServiceRegistry->createChild("DHCPServers"), llDhcpServers);
 
     xml::ElementNode *pelmNATNetworks;
@@ -2213,5 +2250,5 @@
     if (!llNATNetworks.empty())
     {
-        pelmNATNetworks = pelmNetserviceRegistry->createChild("NATNetworks");
+        pelmNATNetworks = pelmNetServiceRegistry->createChild("NATNetworks");
         for (NATNetworksList::const_iterator it = llNATNetworks.begin();
              it != llNATNetworks.end();
