Index: /trunk/src/VBox/NetworkServices/Dhcpd/Config.cpp
===================================================================
--- /trunk/src/VBox/NetworkServices/Dhcpd/Config.cpp	(revision 79817)
+++ /trunk/src/VBox/NetworkServices/Dhcpd/Config.cpp	(revision 79818)
@@ -264,4 +264,5 @@
     i_logInit();
 
+    /** @todo the MAC address is always generated, no XML config option for it ... */
     bool fMACGenerated = false;
     if (   m_MacAddress.au16[0] == 0
@@ -291,57 +292,6 @@
     }
 
-    /* unicast IP address */
-    if ((m_IPv4Address.au8[0] & 0xe0) == 0xe0)
-    {
-        LogRel(("IP address is not unicast: %RTnaipv4\n", m_IPv4Address.u));
-        return VERR_GENERAL_FAILURE;
-    }
-
-    /* valid netmask */
-    int cPrefixBits;
-    int rc = RTNetMaskToPrefixIPv4(&m_IPv4Netmask, &cPrefixBits);
-    if (RT_FAILURE(rc) || cPrefixBits == 0)
-    {
-        LogRel(("IP mask is not valid: %RTnaipv4\n", m_IPv4Netmask.u));
-        return VERR_GENERAL_FAILURE;
-    }
-
-    /* first IP is from the same network */
-    if ((m_IPv4PoolFirst.u & m_IPv4Netmask.u) != (m_IPv4Address.u & m_IPv4Netmask.u))
-    {
-        LogRel(("first pool address is outside the network %RTnaipv4/%d: %RTnaipv4\n",
-                 (m_IPv4Address.u & m_IPv4Netmask.u), cPrefixBits, m_IPv4PoolFirst.u));
-        return VERR_GENERAL_FAILURE;
-    }
-
-    /* last IP is from the same network */
-    if ((m_IPv4PoolLast.u & m_IPv4Netmask.u) != (m_IPv4Address.u & m_IPv4Netmask.u))
-    {
-        LogRel(("last pool address is outside the network %RTnaipv4/%d: %RTnaipv4\n",
-                 (m_IPv4Address.u & m_IPv4Netmask.u), cPrefixBits, m_IPv4PoolLast.u));
-        return VERR_GENERAL_FAILURE;
-    }
-
-    /* the pool is valid */
-    if (RT_N2H_U32(m_IPv4PoolLast.u) < RT_N2H_U32(m_IPv4PoolFirst.u))
-    {
-        LogRel(("pool range is invalid: %RTnaipv4 - %RTnaipv4\n",
-                 m_IPv4PoolFirst.u, m_IPv4PoolLast.u));
-        return VERR_GENERAL_FAILURE;
-    }
-
-    /* our own address is not inside the pool */
-    if (   RT_N2H_U32(m_IPv4PoolFirst.u) <= RT_N2H_U32(m_IPv4Address.u)
-        && RT_N2H_U32(m_IPv4Address.u)   <= RT_N2H_U32(m_IPv4PoolLast.u))
-    {
-        LogRel(("server address inside the pool range %RTnaipv4 - %RTnaipv4: %RTnaipv4\n",
-                 m_IPv4PoolFirst.u, m_IPv4PoolLast.u, m_IPv4Address.u));
-        return VERR_GENERAL_FAILURE;
-    }
-
     if (!fMACGenerated)
         LogRel(("MAC address %RTmac\n", &m_MacAddress));
-    LogRel(("IP address %RTnaipv4/%d\n", m_IPv4Address.u, cPrefixBits));
-    LogRel(("address pool %RTnaipv4 - %RTnaipv4\n", m_IPv4PoolFirst.u, m_IPv4PoolLast.u));
 
     return VINF_SUCCESS;
@@ -685,4 +635,7 @@
     }
 
+    /*
+     * Addresses and mask.
+     */
     ::getIPv4AddrAttribute(pElmServer, "IPAddress", &m_IPv4Address);
     ::getIPv4AddrAttribute(pElmServer, "networkMask", &m_IPv4Netmask);
@@ -690,4 +643,30 @@
     ::getIPv4AddrAttribute(pElmServer, "upperIP", &m_IPv4PoolLast);
 
+    /* unicast IP address */
+    if ((m_IPv4Address.au8[0] & 0xe0) == 0xe0)
+        throw ConfigFileError("DHCP server IP address is not unicast: %RTnaipv4", m_IPv4Address.u);
+
+    /* valid netmask */
+    int cPrefixBits;
+    int rc = RTNetMaskToPrefixIPv4(&m_IPv4Netmask, &cPrefixBits);
+    if (RT_FAILURE(rc) || cPrefixBits == 0)
+        throw ConfigFileError("IP mask is not valid: %RTnaipv4", m_IPv4Netmask.u);
+
+    /* first IP is from the same network */
+    if ((m_IPv4PoolFirst.u & m_IPv4Netmask.u) != (m_IPv4Address.u & m_IPv4Netmask.u))
+        throw ConfigFileError("first pool address is outside the network %RTnaipv4/%d: %RTnaipv4",
+                              (m_IPv4Address.u & m_IPv4Netmask.u), cPrefixBits, m_IPv4PoolFirst.u);
+
+    /* last IP is from the same network */
+    if ((m_IPv4PoolLast.u & m_IPv4Netmask.u) != (m_IPv4Address.u & m_IPv4Netmask.u))
+        throw ConfigFileError("last pool address is outside the network %RTnaipv4/%d: %RTnaipv4\n",
+                              (m_IPv4Address.u & m_IPv4Netmask.u), cPrefixBits, m_IPv4PoolLast.u);
+
+    /* the pool is valid */
+    if (RT_N2H_U32(m_IPv4PoolLast.u) < RT_N2H_U32(m_IPv4PoolFirst.u))
+        throw ConfigFileError("pool range is invalid: %RTnaipv4 - %RTnaipv4", m_IPv4PoolFirst.u, m_IPv4PoolLast.u);
+    LogRel(("IP address:   %RTnaipv4/%d\n", m_IPv4Address.u, cPrefixBits));
+    LogRel(("Address pool: %RTnaipv4 - %RTnaipv4\n", m_IPv4PoolFirst.u, m_IPv4PoolLast.u));
+
     /*
      * <DHCPServer> children
@@ -699,10 +678,10 @@
         /* Global options: */
         if (pElmChild->nameEquals("Options"))
-            m_GlobalConfig.initFromXml(pElmChild, fStrict);
+            m_GlobalConfig.initFromXml(pElmChild, fStrict, this);
         /* Group w/ options: */
         else if (pElmChild->nameEquals("Group"))
         {
             std::unique_ptr<GroupConfig> ptrGroup(new GroupConfig());
-            ptrGroup->initFromXml(pElmChild, fStrict);
+            ptrGroup->initFromXml(pElmChild, fStrict, this);
             if (m_GroupConfigs.find(ptrGroup->getGroupName()) == m_GroupConfigs.end())
             {
@@ -721,5 +700,5 @@
         {
             std::unique_ptr<HostConfig> ptrHost(new HostConfig());
-            ptrHost->initFromXml(pElmChild, fStrict);
+            ptrHost->initFromXml(pElmChild, fStrict, this);
             if (m_HostConfigs.find(ptrHost->getMACAddress()) == m_HostConfigs.end())
             {
@@ -799,7 +778,8 @@
  * @param   fStrict             Set if we're in strict mode, clear if we just
  *                              want to get on with it if we can.
+ * @param   pConfig             The configuration object.
  * @throws  std::bad_alloc, ConfigFileError
  */
-void ConfigLevelBase::i_parseChild(const xml::ElementNode *pElmChild, bool fStrict)
+void ConfigLevelBase::i_parseChild(const xml::ElementNode *pElmChild, bool fStrict, Config const *pConfig)
 {
     if (pElmChild->nameEquals("Option"))
@@ -823,4 +803,5 @@
     else
         throw ConfigFileError(pElmChild->getParent(), "Unexpected child '%s'", pElmChild->getName());
+    RT_NOREF(pConfig);
 }
 
@@ -834,7 +815,8 @@
  * @param   fStrict             Set if we're in strict mode, clear if we just
  *                              want to get on with it if we can.
+ * @param   pConfig             The configuration object.
  * @throws  std::bad_alloc, ConfigFileError
  */
-void ConfigLevelBase::initFromXml(const xml::ElementNode *pElmConfig, bool fStrict)
+void ConfigLevelBase::initFromXml(const xml::ElementNode *pElmConfig, bool fStrict, Config const *pConfig)
 {
     /*
@@ -854,5 +836,5 @@
     const xml::ElementNode *pElmChild;
     while ((pElmChild = it.forAllNodes()) != NULL)
-        i_parseChild(pElmChild, fStrict);
+        i_parseChild(pElmChild, fStrict, pConfig);
 }
 
@@ -864,9 +846,10 @@
  * @param   fStrict             Set if we're in strict mode, clear if we just
  *                              want to get on with it if we can.
+ * @param   pConfig             The configuration object.
  * @throws  std::bad_alloc, ConfigFileError
  */
-void GlobalConfig::initFromXml(const xml::ElementNode *pElmOptions, bool fStrict)
-{
-    ConfigLevelBase::initFromXml(pElmOptions, fStrict);
+void GlobalConfig::initFromXml(const xml::ElementNode *pElmOptions, bool fStrict, Config const *pConfig)
+{
+    ConfigLevelBase::initFromXml(pElmOptions, fStrict, pConfig);
 }
 
@@ -878,7 +861,8 @@
  * @param   fStrict             Set if we're in strict mode, clear if we just
  *                              want to get on with it if we can.
+ * @param   pConfig             The configuration object.
  * @throws  std::bad_alloc, ConfigFileError
  */
-void GroupConfig::i_parseChild(const xml::ElementNode *pElmChild, bool fStrict)
+void GroupConfig::i_parseChild(const xml::ElementNode *pElmChild, bool fStrict, Config const *pConfig)
 {
     /*
@@ -903,5 +887,5 @@
          * Not a condition, pass it on to the base class.
          */
-        ConfigLevelBase::i_parseChild(pElmChild, fStrict);
+        ConfigLevelBase::i_parseChild(pElmChild, fStrict, pConfig);
         return;
     }
@@ -939,8 +923,7 @@
     {
         ConfigFileError Xcpt(pElmChild, "condition value is empty or missing (inclusive=%RTbool)", fInclusive);
-        if (!fStrict)
-            LogRelFunc(("%s, ignoring condition\n", Xcpt.what()));
-        else
+        if (fStrict)
             throw Xcpt;
+        LogRelFunc(("%s, ignoring condition\n", Xcpt.what()));
     }
 }
@@ -953,7 +936,8 @@
  * @param   fStrict             Set if we're in strict mode, clear if we just
  *                              want to get on with it if we can.
+ * @param   pConfig             The configuration object.
  * @throws  std::bad_alloc, ConfigFileError
  */
-void GroupConfig::initFromXml(const xml::ElementNode *pElmGroup, bool fStrict)
+void GroupConfig::initFromXml(const xml::ElementNode *pElmGroup, bool fStrict, Config const *pConfig)
 {
     /*
@@ -970,5 +954,5 @@
      * Do common initialization (including children).
      */
-    ConfigLevelBase::initFromXml(pElmGroup, fStrict);
+    ConfigLevelBase::initFromXml(pElmGroup, fStrict, pConfig);
 }
 
@@ -985,7 +969,8 @@
  * @param   fStrict             Set if we're in strict mode, clear if we just
  *                              want to get on with it if we can.
+ * @param   pConfig             The configuration object (for netmask).
  * @throws  std::bad_alloc, ConfigFileError
  */
-void HostConfig::initFromXml(const xml::ElementNode *pElmConfig, bool fStrict)
+void HostConfig::initFromXml(const xml::ElementNode *pElmConfig, bool fStrict, Config const *pConfig)
 {
     /*
@@ -1000,11 +985,20 @@
 
     /* Fixed IP address assignment - optional: */
-    const char *pszFixedAddress = pElmConfig->findAttributeValue("FixedIPAddress");
+    const char *pszFixedAddress = pElmConfig->findAttributeValue("fixedAddress");
     if (!pszFixedAddress || *RTStrStripL(pszFixedAddress) == '\0')
         m_fHaveFixedAddress = false;
     else
     {
-        m_fHaveFixedAddress = false;
-        ::getIPv4AddrAttribute(pElmConfig, "FixedIPAddress", &m_FixedAddress);
+        ::getIPv4AddrAttribute(pElmConfig, "fixedAddress", &m_FixedAddress);
+        if (pConfig->isInIPv4Network(m_FixedAddress))
+            m_fHaveFixedAddress = true;
+        else
+        {
+            ConfigFileError Xcpt(pElmConfig, "fixedAddress '%s' is not the DHCP network", pszFixedAddress);
+            if (fStrict)
+                throw Xcpt;
+            LogRelFunc(("%s - ignoring the fixed address assignment\n", Xcpt.what()));
+            m_fHaveFixedAddress = false;
+        }
     }
 
@@ -1012,5 +1006,30 @@
      * Do common initialization.
      */
-    ConfigLevelBase::initFromXml(pElmConfig, fStrict);
+    ConfigLevelBase::initFromXml(pElmConfig, fStrict, pConfig);
+}
+
+
+/**
+ * Assembles a list of hosts with fixed address assignments.
+ *
+ * @returns IPRT status code.
+ * @param   a_rRetConfigs       Where to return the configurations.
+ */
+int Config::getFixedAddressConfigs(HostConfigVec &a_rRetConfigs) const
+{
+    for (HostConfigMap::const_iterator it = m_HostConfigs.begin(); it != m_HostConfigs.end(); ++it)
+    {
+        HostConfig const *pHostConfig = it->second;
+        if (pHostConfig->haveFixedAddress())
+            try
+            {
+                a_rRetConfigs.push_back(pHostConfig);
+            }
+            catch (std::bad_alloc &)
+            {
+                return VERR_NO_MEMORY;
+            }
+    }
+    return VINF_SUCCESS;
 }
 
Index: /trunk/src/VBox/NetworkServices/Dhcpd/Config.h
===================================================================
--- /trunk/src/VBox/NetworkServices/Dhcpd/Config.h	(revision 79817)
+++ /trunk/src/VBox/NetworkServices/Dhcpd/Config.h	(revision 79818)
@@ -34,4 +34,6 @@
 
 
+class Config;
+
 /**
  * Base configuration
@@ -62,5 +64,5 @@
     { }
 
-    virtual void        initFromXml(xml::ElementNode const *pElm, bool fStrict);
+    virtual void        initFromXml(xml::ElementNode const *pElm, bool fStrict, Config const *pConfig);
     virtual const char *getType() const RT_NOEXCEPT = 0;
     virtual const char *getName() const RT_NOEXCEPT = 0;
@@ -78,5 +80,5 @@
 protected:
     void            i_parseOption(const xml::ElementNode *pElmOption);
-    virtual void    i_parseChild(const xml::ElementNode *pElmChild, bool fStrict);
+    virtual void    i_parseChild(const xml::ElementNode *pElmChild, bool fStrict, Config const *pConfig);
 };
 
@@ -91,5 +93,5 @@
         : ConfigLevelBase()
     { }
-    void initFromXml(xml::ElementNode const *pElm, bool fStrict) RT_OVERRIDE;
+    void initFromXml(xml::ElementNode const *pElm, bool fStrict, Config const *pConfig) RT_OVERRIDE;
     const char *getType() const RT_NOEXCEPT RT_OVERRIDE { return "global"; }
     const char *getName() const RT_NOEXCEPT RT_OVERRIDE { return "GlobalConfig"; }
@@ -199,5 +201,5 @@
     }
 
-    void initFromXml(xml::ElementNode const *pElm, bool fStrict) RT_OVERRIDE;
+    void initFromXml(xml::ElementNode const *pElm, bool fStrict, Config const *pConfig) RT_OVERRIDE;
     bool match(const ClientId &a_ridClient, const OptVendorClassId &a_ridVendorClass, const OptUserClassId &a_ridUserClass) const;
 
@@ -210,5 +212,5 @@
 
 protected:
-    void                i_parseChild(const xml::ElementNode *pElmChild, bool fStrict) RT_OVERRIDE;
+    void                i_parseChild(const xml::ElementNode *pElmChild, bool fStrict, Config const *pConfig) RT_OVERRIDE;
     /** Used to name unnamed groups. */
     static uint32_t     s_uGroupNo;
@@ -240,5 +242,5 @@
     }
 
-    void initFromXml(xml::ElementNode const *pElm, bool fStrict) RT_OVERRIDE;
+    void initFromXml(xml::ElementNode const *pElm, bool fStrict, Config const *pConfig) RT_OVERRIDE;
     const char *getType() const RT_NOEXCEPT RT_OVERRIDE { return "host"; }
     const char *getName() const RT_NOEXCEPT RT_OVERRIDE { return m_strName.c_str(); }
@@ -246,5 +248,7 @@
     /** @name Accessors
      * @{ */
-    RTMAC const        &getMACAddress() const RT_NOEXCEPT       { return m_MACAddress; }
+    RTMAC const            &getMACAddress() const RT_NOEXCEPT       { return m_MACAddress; }
+    bool                    haveFixedAddress() const RT_NOEXCEPT    { return m_fHaveFixedAddress; }
+    RTNETADDRIPV4 const &   getFixedAddress() const RT_NOEXCEPT     { return m_FixedAddress; }
     /** @} */
 };
@@ -257,7 +261,7 @@
 {
     /** Group configuration map. */
-    typedef std::map<RTCString, GroupConfig * > GroupConfigMap;
+    typedef std::map<RTCString, GroupConfig const * > GroupConfigMap;
     /** Host configuration map. */
-    typedef std::map<RTMAC,     HostConfig *  > HostConfigMap;
+    typedef std::map<RTMAC,     HostConfig const * > HostConfigMap;
 
 
@@ -321,8 +325,24 @@
     RTNETADDRIPV4       getIPv4Address() const RT_NOEXCEPT      { return m_IPv4Address; }
     RTNETADDRIPV4       getIPv4Netmask() const RT_NOEXCEPT      { return m_IPv4Netmask; }
-
     RTNETADDRIPV4       getIPv4PoolFirst() const RT_NOEXCEPT    { return m_IPv4PoolFirst; }
     RTNETADDRIPV4       getIPv4PoolLast() const RT_NOEXCEPT     { return m_IPv4PoolLast; }
     /** @} */
+
+    /** Gets the network (IP masked by network mask). */
+    RTNETADDRIPV4       getIPv4Network() const RT_NOEXCEPT
+    {
+        RTNETADDRIPV4 Network;
+        Network.u = m_IPv4Netmask.u & m_IPv4Address.u;
+        return Network;
+    }
+    /** Checks if the given IPv4 address is in the DHCP server network. */
+    bool                isInIPv4Network(RTNETADDRIPV4 a_rAddress) const RT_NOEXCEPT
+    {
+        return (a_rAddress.u & getIPv4Netmask().u) == getIPv4Network().u;
+    }
+
+    /** Host configuration vector. */
+    typedef std::vector<HostConfig const *> HostConfigVec;
+    int                 getFixedAddressConfigs(HostConfigVec &a_rRetConfigs) const;
 
     /** Configuration vector. */
Index: /trunk/src/VBox/NetworkServices/Dhcpd/DHCPD.cpp
===================================================================
--- /trunk/src/VBox/NetworkServices/Dhcpd/DHCPD.cpp	(revision 79817)
+++ /trunk/src/VBox/NetworkServices/Dhcpd/DHCPD.cpp	(revision 79818)
@@ -267,5 +267,6 @@
 
         b->setState(Binding::ACKED);
-        i_saveLeases();
+        if (!b->isFixed())
+            i_saveLeases();
     }
 
@@ -321,5 +322,6 @@
 
     b->setState(Binding::ACKED);
-    i_saveLeases();
+    if (!b->isFixed())
+        i_saveLeases();
 
     ack->setYiaddr(b->addr());
Index: /trunk/src/VBox/NetworkServices/Dhcpd/Db.cpp
===================================================================
--- /trunk/src/VBox/NetworkServices/Dhcpd/Db.cpp	(revision 79817)
+++ /trunk/src/VBox/NetworkServices/Dhcpd/Db.cpp	(revision 79818)
@@ -70,4 +70,6 @@
     if (b->m_state == Binding::FREE)
         cb += pfnOutput(pvArgOutput, RT_STR_TUPLE(" free"));
+    else if (b->m_fFixed)
+        cb += pfnOutput(pvArgOutput, RT_STR_TUPLE(" fixed"));
     else
     {
@@ -87,4 +89,24 @@
 }
 
+
+/**
+ * Used to update the client ID of a fixed address assignment.
+ *
+ * We only have the MAC address when prepraring the binding, so the full client
+ * ID must be supplied when the client requests it.
+ *
+ * @param   a_ridClient         The client ID.
+ * @throws  std::bad_alloc
+ */
+void Binding::idUpdate(const ClientId &a_ridClient)
+{
+    AssertReturnVoid(isFixed());
+    m_id = a_ridClient;
+}
+
+
+/**
+ * Get the state as a string for the XML lease database.
+ */
 const char *Binding::stateName() const RT_NOEXCEPT
 {
@@ -108,4 +130,7 @@
 
 
+/**
+ * Sets the state by name (reverse of Binding::stateName()).
+ */
 Binding &Binding::setState(const char *pszStateName) RT_NOEXCEPT
 {
@@ -139,5 +164,5 @@
 bool Binding::expire(Timestamp tsDeadline) RT_NOEXCEPT
 {
-    if (m_state <= Binding::EXPIRED)
+    if (m_state <= Binding::EXPIRED || m_fFixed)
         return false;
 
@@ -338,5 +363,69 @@
     m_pConfig = pConfig;
 
-    return m_pool.init(pConfig->getIPv4PoolFirst(), pConfig->getIPv4PoolLast());
+    int rc = m_pool.init(pConfig->getIPv4PoolFirst(), pConfig->getIPv4PoolLast());
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * If the server IP is in the dynamic range, preallocate it like a fixed assignment.
+         */
+        rc = i_enterFixedAddressAssignment(pConfig->getIPv4Address(), pConfig->getMacAddress());
+        if (RT_SUCCESS(rc))
+        {
+            /*
+             * Preallocate any fixed address assignments:
+             */
+            Config::HostConfigVec vecHostConfigs;
+            rc = pConfig->getFixedAddressConfigs(vecHostConfigs);
+            for (Config::HostConfigVec::const_iterator it = vecHostConfigs.begin();
+                 it != vecHostConfigs.end() && RT_SUCCESS(rc); ++it)
+                rc = i_enterFixedAddressAssignment((*it)->getFixedAddress(), (*it)->getMACAddress());
+        }
+    }
+
+    return rc;
+}
+
+
+/**
+ * Used by Db::init() to register a fixed address assignment.
+ *
+ * @returns IPRT status code.
+ * @param   a_rAddress      The IPv4 address assignment.
+ * @param   a_rMACAddress   The MAC address.
+ */
+int Db::i_enterFixedAddressAssignment(RTNETADDRIPV4 const &a_rAddress, RTMAC const &a_rMACAddress) RT_NOEXCEPT
+{
+    LogRelFunc(("%RTmac: %RTnaipv4\n", &a_rMACAddress, a_rAddress));
+    Assert(m_pConfig->isInIPv4Network(a_rAddress)); /* should've been checked elsewhere already */
+
+    /*
+     * If the address is part of the pool, we have to allocate it to
+     * prevent it from being used again.
+     */
+    if (m_pool.contains(a_rAddress))
+    {
+        if (!m_pool.allocate(a_rAddress))
+        {
+            LogRelFunc(("%RTnaipv4 already allocated?\n", a_rAddress));
+            return VERR_ADDRESS_CONFLICT;
+        }
+    }
+
+    /*
+     * Create the binding.
+     */
+    Binding *pBinding = NULL;
+    try
+    {
+        pBinding = new Binding(a_rAddress, a_rMACAddress, true /*fFixed*/);
+        m_bindings.push_front(pBinding);
+    }
+    catch (std::bad_alloc &)
+    {
+        if (pBinding)
+            delete pBinding;
+        return VERR_NO_MEMORY;
+    }
+    return VINF_SUCCESS;
 }
 
@@ -442,8 +531,19 @@
         /*
          * We've already seen this client, give it its old binding.
+         *
+         * If the client's MAC address is configured with a fixed
+         * address, give its preconfigured binding.  Fixed bindings
+         * are always at the head of the m_bindings list, so we
+         * won't be confused by any old leases of the client.
          */
         if (b->m_id == id)
         {
             LogRel(("> ... found existing binding %R[binding]\n", b));
+            return b;
+        }
+        if (b->isFixed() && b->id().mac() == id.mac())
+        {
+            b->idUpdate(id);
+            LogRel(("> ... found fixed binding %R[binding]\n", b));
             return b;
         }
@@ -552,13 +652,30 @@
 Binding *Db::allocateBinding(const DhcpClientMessage &req)
 {
-    /** @todo XXX: handle fixed address assignments */
+    const ClientId &id(req.clientId());
 
     /*
      * Get and validate the requested address (if present).
+     *
+     * Fixed assignments are often outside the dynamic range, so we much detect
+     * those to make sure they aren't rejected based on IP range.  ASSUMES fixed
+     * assignments are at the head of the binding list.
      */
     OptRequestedAddress reqAddr(req);
     if (reqAddr.present() && !addressBelongs(reqAddr.value()))
     {
-        if (req.messageType() == RTNET_DHCP_MT_DISCOVER)
+        bool fIsFixed = false;
+        for (bindings_t::iterator it = m_bindings.begin(); it != m_bindings.end() && (*it)->isFixed(); ++it)
+            if (reqAddr.value().u == (*it)->addr().u)
+            {
+                if (   (*it)->id() == id
+                    || (*it)->id().mac() == id.mac())
+                {
+                    fIsFixed = true;
+                    break;
+                }
+            }
+        if (fIsFixed)
+            reqAddr = OptRequestedAddress();
+        else if (req.messageType() == RTNET_DHCP_MT_DISCOVER)
         {
             LogRel(("DISCOVER: ignoring invalid requested address\n"));
@@ -572,6 +689,4 @@
      * Allocate the address.
      */
-    const ClientId &id(req.clientId());
-
     Binding *b = i_allocateAddress(id, reqAddr.value());
     if (b != NULL)
@@ -668,6 +783,11 @@
             {
                 LogRel2(("Db::cancelOffer: cancelling %R[binding]\n", b));
-                b->setLeaseTime(0);
-                b->setState(Binding::RELEASED);
+                if (!b->isFixed())
+                {
+                    b->setLeaseTime(0);
+                    b->setState(Binding::RELEASED);
+                }
+                else
+                    b->setState(Binding::ACKED);
             }
             else
@@ -699,6 +819,11 @@
         {
             LogRel2(("Db::releaseBinding: releasing %R[binding]\n", b));
-            b->setState(Binding::RELEASED);
-            return true;
+            if (!b->isFixed())
+            {
+                b->setState(Binding::RELEASED);
+                return true;
+            }
+            b->setState(Binding::ACKED);
+            return false;
         }
     }
@@ -741,5 +866,6 @@
         {
             const Binding *b = *it;
-            b->toXML(pElmRoot);
+            if (!b->isFixed())
+                b->toXML(pElmRoot);
         }
     }
Index: /trunk/src/VBox/NetworkServices/Dhcpd/Db.h
===================================================================
--- /trunk/src/VBox/NetworkServices/Dhcpd/Db.h	(revision 79817)
+++ /trunk/src/VBox/NetworkServices/Dhcpd/Db.h	(revision 79818)
@@ -55,4 +55,6 @@
     Timestamp           m_issued;
     uint32_t            m_secLease;
+    /** Set if this is a fixed assignment. */
+    bool                m_fFixed;
 
 public:
@@ -61,11 +63,20 @@
 
     explicit Binding(RTNETADDRIPV4 a_Addr)
-        : m_addr(a_Addr), m_state(FREE), m_issued(), m_secLease()
+        : m_addr(a_Addr), m_state(FREE), m_issued(), m_secLease(0), m_fFixed(false)
     {}
 
     Binding(RTNETADDRIPV4 a_Addr, const ClientId &a_id)
-        : m_addr(a_Addr), m_state(FREE), m_id(a_id), m_issued(), m_secLease()
+        : m_addr(a_Addr), m_state(FREE), m_id(a_id), m_issued(), m_secLease(0), m_fFixed(false)
     {}
 
+    Binding(RTNETADDRIPV4 a_Addr, const RTMAC &a_MACAddress, bool a_fFixed)
+        : m_addr(a_Addr)
+        , m_state(ACKED)
+        , m_id(ClientId(a_MACAddress, OptClientId()))
+        , m_issued(Timestamp::now())
+        , m_secLease(UINT32_MAX - 1)
+        , m_fFixed(a_fFixed)
+    {}
+
 
     /** @name Attribute accessors
@@ -74,4 +85,5 @@
 
     const ClientId &id() const RT_NOEXCEPT          { return m_id; }
+    void            idUpdate(const ClientId &a_ridClient);
 
     uint32_t        leaseTime() const RT_NOEXCEPT   { return m_secLease; }
@@ -86,4 +98,6 @@
         return *this;
     }
+
+    bool            isFixed() const RT_NOEXCEPT     { return m_fFixed; }
     /** @} */
 
@@ -152,5 +166,7 @@
      * @note Currently not used.  */
     const Config   *m_pConfig;
-    /** The lease database. */
+    /** The lease database.
+     * @note Since fixed assignments are added during initialization, they will
+     *       always be first.  The allocateBinding() code depends on this.  */
     bindings_t      m_bindings;
     /** Address allocation pool. */
@@ -183,4 +199,5 @@
 
 private:
+    int      i_enterFixedAddressAssignment(RTNETADDRIPV4 const &a_rAddress, RTMAC const &a_rMACAddress) RT_NOEXCEPT;
     Binding *i_createBinding(const ClientId &id = ClientId());
     Binding *i_createBinding(RTNETADDRIPV4 addr, const ClientId &id = ClientId());
Index: /trunk/src/VBox/NetworkServices/Dhcpd/DhcpMessage.cpp
===================================================================
--- /trunk/src/VBox/NetworkServices/Dhcpd/DhcpMessage.cpp	(revision 79817)
+++ /trunk/src/VBox/NetworkServices/Dhcpd/DhcpMessage.cpp	(revision 79818)
@@ -359,5 +359,5 @@
 void DhcpServerMessage::addOptions(const optmap_t &optmap)
 {
-    for (optmap_t::const_iterator it( optmap.begin() ); it != optmap.end(); ++it)
+    for (optmap_t::const_iterator it = optmap.begin(); it != optmap.end(); ++it)
         m_optmap << it->second;
 }
@@ -398,6 +398,5 @@
          << m_optMessageType;
 
-    for (optmap_t::const_iterator it ( m_optmap.begin() );
-         it != m_optmap.end(); ++it)
+    for (optmap_t::const_iterator it = m_optmap.begin(); it != m_optmap.end(); ++it)
     {
         LogRel3(("encoding option %d\n", it->first));
@@ -409,5 +408,5 @@
 
     AssertCompile(RTNET_DHCP_NORMAL_SIZE == 548);
-    if (data.size() < RTNET_DHCP_NORMAL_SIZE)      /* XXX */
+    if (data.size() < RTNET_DHCP_NORMAL_SIZE)
         data.resize(RTNET_DHCP_NORMAL_SIZE);
 
