summaryrefslogtreecommitdiffstats
path: root/src/VBox/NetworkServices/Dhcpd/DHCPD.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/NetworkServices/Dhcpd/DHCPD.cpp286
1 files changed, 286 insertions, 0 deletions
diff --git a/src/VBox/NetworkServices/Dhcpd/DHCPD.cpp b/src/VBox/NetworkServices/Dhcpd/DHCPD.cpp
new file mode 100644
index 00000000..5ded7b38
--- /dev/null
+++ b/src/VBox/NetworkServices/Dhcpd/DHCPD.cpp
@@ -0,0 +1,286 @@
+/* $Id: DHCPD.cpp $ */
+/** @file
+ * DHCP server - protocol logic
+ */
+
+/*
+ * Copyright (C) 2017-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.
+ */
+
+#include "DHCPD.h"
+#include "DhcpOptions.h"
+
+#include <iprt/path.h>
+
+
+DHCPD::DHCPD()
+ : m_pConfig(NULL), m_db()
+{
+}
+
+
+int DHCPD::init(const Config *pConfig)
+{
+ int rc;
+
+ if (m_pConfig != NULL)
+ return VERR_INVALID_STATE;
+
+ /* leases file name */
+ m_strLeasesFileName = pConfig->getHome();
+ m_strLeasesFileName += RTPATH_DELIMITER;
+ m_strLeasesFileName += pConfig->getBaseName();
+ m_strLeasesFileName += "-Dhcpd.leases";
+
+ rc = m_db.init(pConfig);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ loadLeases();
+
+ m_pConfig = pConfig;
+ return VINF_SUCCESS;
+}
+
+
+void DHCPD::loadLeases()
+{
+ m_db.loadLeases(m_strLeasesFileName);
+}
+
+
+void DHCPD::saveLeases()
+{
+ m_db.expire();
+ m_db.writeLeases(m_strLeasesFileName);
+}
+
+
+DhcpServerMessage *DHCPD::process(DhcpClientMessage &req)
+{
+ DhcpServerMessage *reply = NULL;
+
+ req.dump();
+
+ OptServerId sid(req);
+ if (sid.present() && sid.value().u != m_pConfig->getIPv4Address().u)
+ {
+ if (req.broadcasted() && req.messageType() == RTNET_DHCP_MT_REQUEST)
+ m_db.cancelOffer(req);
+
+ return NULL;
+ }
+
+ switch (req.messageType())
+ {
+ /*
+ * Requests that require server's reply.
+ */
+ case RTNET_DHCP_MT_DISCOVER:
+ reply = doDiscover(req);
+ break;
+
+ case RTNET_DHCP_MT_REQUEST:
+ reply = doRequest(req);
+ break;
+
+ case RTNET_DHCP_MT_INFORM:
+ reply = doInform(req);
+ break;
+
+ /*
+ * Requests that don't have a reply.
+ */
+ case RTNET_DHCP_MT_DECLINE:
+ doDecline(req);
+ break;
+
+ case RTNET_DHCP_MT_RELEASE:
+ doRelease(req);
+ break;
+
+ /*
+ * Unexpected or unknown message types.
+ */
+ case RTNET_DHCP_MT_OFFER: /* FALLTHROUGH */
+ case RTNET_DHCP_MT_ACK: /* FALLTHROUGH */
+ case RTNET_DHCP_MT_NAC: /* FALLTHROUGH */
+ default:
+ break;
+ }
+
+ return reply;
+}
+
+
+DhcpServerMessage *DHCPD::createMessage(int type, DhcpClientMessage &req)
+{
+ return new DhcpServerMessage(req, type, m_pConfig->getIPv4Address());
+}
+
+
+DhcpServerMessage *DHCPD::doDiscover(DhcpClientMessage &req)
+{
+ /*
+ * XXX: TODO: Windows iSCSI initiator sends DHCPDISCOVER first and
+ * it has ciaddr filled. Shouldn't let it screw up the normal
+ * lease we already have for that client, but we should probably
+ * reply with a pro-forma offer.
+ */
+ if (req.ciaddr().u != 0)
+ return NULL;
+
+ Binding *b = m_db.allocateBinding(req);
+ if (b == NULL)
+ return NULL;
+
+
+ std::unique_ptr<DhcpServerMessage> reply;
+
+ bool fRapidCommit = OptRapidCommit(req).present();
+ if (!fRapidCommit)
+ {
+ reply.reset(createMessage(RTNET_DHCP_MT_OFFER, req));
+
+ if (b->state() < Binding::OFFERED)
+ b->setState(Binding::OFFERED);
+
+ /* use small lease time internally to quickly free unclaimed offers? */
+ }
+ else
+ {
+ reply.reset(createMessage(RTNET_DHCP_MT_ACK, req));
+ reply->addOption(OptRapidCommit(true));
+
+ b->setState(Binding::ACKED);
+ saveLeases();
+ }
+
+ reply->setYiaddr(b->addr());
+ reply->addOption(OptLeaseTime(b->leaseTime()));
+
+
+ OptParameterRequest optlist(req);
+ reply->addOptions(m_pConfig->getOptions(optlist, req.clientId()));
+
+ // reply->maybeUnicast(req); /* XXX: we reject ciaddr != 0 above */
+ return reply.release();
+}
+
+
+DhcpServerMessage *DHCPD::doRequest(DhcpClientMessage &req)
+{
+ OptRequestedAddress reqAddr(req);
+ if (req.ciaddr().u != 0 && reqAddr.present() && reqAddr.value().u != req.ciaddr().u)
+ {
+ std::unique_ptr<DhcpServerMessage> nak (
+ createMessage(RTNET_DHCP_MT_NAC, req)
+ );
+ nak->addOption(OptMessage("Requested address does not match ciaddr"));
+ return nak.release();
+ }
+
+
+ Binding *b = m_db.allocateBinding(req);
+ if (b == NULL)
+ {
+ return createMessage(RTNET_DHCP_MT_NAC, req);
+ }
+
+
+ std::unique_ptr<DhcpServerMessage> ack (
+ createMessage(RTNET_DHCP_MT_ACK, req)
+ );
+
+ b->setState(Binding::ACKED);
+ saveLeases();
+
+ ack->setYiaddr(b->addr());
+ ack->addOption(OptLeaseTime(b->leaseTime()));
+
+ OptParameterRequest optlist(req);
+ ack->addOptions(m_pConfig->getOptions(optlist, req.clientId()));
+
+ ack->addOption(OptMessage("Ok, ok, here it is"));
+
+ ack->maybeUnicast(req);
+ return ack.release();
+}
+
+
+/*
+ * 4.3.5 DHCPINFORM message
+ *
+ * The server responds to a DHCPINFORM message by sending a DHCPACK
+ * message directly to the address given in the 'ciaddr' field of the
+ * DHCPINFORM message. The server MUST NOT send a lease expiration time
+ * to the client and SHOULD NOT fill in 'yiaddr'. The server includes
+ * other parameters in the DHCPACK message as defined in section 4.3.1.
+ */
+DhcpServerMessage *DHCPD::doInform(DhcpClientMessage &req)
+{
+ if (req.ciaddr().u == 0)
+ return NULL;
+
+ const OptParameterRequest params(req);
+ if (!params.present())
+ return NULL;
+
+ optmap_t info(m_pConfig->getOptions(params, req.clientId()));
+ if (info.empty())
+ return NULL;
+
+ std::unique_ptr<DhcpServerMessage> ack (
+ createMessage(RTNET_DHCP_MT_ACK, req)
+ );
+
+ ack->addOptions(info);
+
+ ack->maybeUnicast(req);
+ return ack.release();
+}
+
+
+/*
+ * 4.3.3 DHCPDECLINE message
+ *
+ * If the server receives a DHCPDECLINE message, the client has
+ * discovered through some other means that the suggested network
+ * address is already in use. The server MUST mark the network address
+ * as not available and SHOULD notify the local system administrator of
+ * a possible configuration problem.
+ */
+DhcpServerMessage *DHCPD::doDecline(DhcpClientMessage &req)
+{
+ RT_NOREF(req);
+ return NULL;
+}
+
+
+/*
+ * 4.3.4 DHCPRELEASE message
+ *
+ * Upon receipt of a DHCPRELEASE message, the server marks the network
+ * address as not allocated. The server SHOULD retain a record of the
+ * client's initialization parameters for possible reuse in response to
+ * subsequent requests from the client.
+ */
+DhcpServerMessage *DHCPD::doRelease(DhcpClientMessage &req)
+{
+ if (req.ciaddr().u == 0)
+ return NULL;
+
+ bool released = m_db.releaseBinding(req);
+ if (released)
+ saveLeases();
+
+ return NULL;
+}