summaryrefslogtreecommitdiffstats
path: root/src/hooks/dhcp/bootp/bootp_callouts.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/hooks/dhcp/bootp/bootp_callouts.cc')
-rw-r--r--src/hooks/dhcp/bootp/bootp_callouts.cc207
1 files changed, 207 insertions, 0 deletions
diff --git a/src/hooks/dhcp/bootp/bootp_callouts.cc b/src/hooks/dhcp/bootp/bootp_callouts.cc
new file mode 100644
index 0000000..688db70
--- /dev/null
+++ b/src/hooks/dhcp/bootp/bootp_callouts.cc
@@ -0,0 +1,207 @@
+// Copyright (C) 2019-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <bootp_log.h>
+#include <hooks/hooks.h>
+#include <dhcp/pkt4.h>
+#include <stats/stats_mgr.h>
+
+#include <vector>
+
+using namespace isc;
+using namespace isc::bootp;
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::log;
+using namespace isc::stats;
+
+namespace {
+
+// DHCP Specific options listed in RFC 1533 section 9 and with a code name
+// beginning by DHO_DHCP_.
+const std::vector<uint16_t> DHCP_SPECIFIC_OPTIONS = {
+ DHO_DHCP_REQUESTED_ADDRESS,
+ DHO_DHCP_LEASE_TIME,
+ DHO_DHCP_OPTION_OVERLOAD,
+ DHO_DHCP_MESSAGE_TYPE,
+ DHO_DHCP_SERVER_IDENTIFIER,
+ DHO_DHCP_PARAMETER_REQUEST_LIST,
+ DHO_DHCP_MESSAGE,
+ DHO_DHCP_MAX_MESSAGE_SIZE,
+ DHO_DHCP_RENEWAL_TIME,
+ DHO_DHCP_REBINDING_TIME,
+ DHO_DHCP_CLIENT_IDENTIFIER
+};
+
+// Size of the BOOTP space for vendor extensions.
+const size_t BOOT_VENDOR_SPACE_SIZE = 64;
+
+// Minimum size of a BOOTP message.
+const size_t BOOT_MIN_SIZE = Pkt4::DHCPV4_PKT_HDR_LEN + BOOT_VENDOR_SPACE_SIZE;
+
+// Check as compile time it is really 300!
+static_assert(BOOT_MIN_SIZE == 300, "BOOT_MIN_SIZE is not 300");
+
+} // end of anonymous namespace.
+
+// Functions accessed by the hooks framework use C linkage to avoid the name
+// mangling that accompanies use of the C++ compiler as well as to avoid
+// issues related to namespaces.
+extern "C" {
+
+/// @brief This callout is called at the "buffer4_receive" hook.
+///
+/// Ignore DHCP and BOOTREPLY messages.
+/// Remaining packets should be BOOTP requests so add the BOOTP client class
+/// and set the message type to DHCPREQUEST.
+///
+/// @param handle CalloutHandle.
+///
+/// @return 0 upon success, non-zero otherwise.
+int buffer4_receive(CalloutHandle& handle) {
+ CalloutHandle::CalloutNextStep status = handle.getStatus();
+ if (status == CalloutHandle::NEXT_STEP_DROP) {
+ return (0);
+ }
+
+ // Get the received unpacked message.
+ Pkt4Ptr query;
+ handle.getArgument("query4", query);
+
+ try {
+ if (handle.getStatus() != CalloutHandle::NEXT_STEP_SKIP) {
+ query->unpack();
+ }
+
+ // Not DHCP query nor BOOTP response?
+ if ((query->getType() == DHCP_NOTYPE) &&
+ (query->getOp() == BOOTREQUEST)) {
+
+ query->addClass("BOOTP");
+ query->setType(DHCPREQUEST);
+
+ LOG_DEBUG(bootp_logger, DBGLVL_TRACE_BASIC, BOOTP_BOOTP_QUERY)
+ .arg(query->getLabel());
+ }
+ } catch (const SkipRemainingOptionsError& ex) {
+ // An option failed to unpack but we are to attempt to process it
+ // anyway. Log it and let's hope for the best.
+ LOG_DEBUG(bootp_logger, DBGLVL_TRACE_BASIC,
+ BOOTP_PACKET_OPTIONS_SKIPPED)
+ .arg(ex.what());
+ } catch (const std::exception& ex) {
+ // Failed to parse the packet.
+ LOG_DEBUG(bootp_logger, DBGLVL_TRACE_BASIC,
+ BOOTP_PACKET_UNPACK_FAILED)
+ .arg(query->getRemoteAddr().toText())
+ .arg(query->getLocalAddr().toText())
+ .arg(query->getIface())
+ .arg(ex.what());
+
+ // Increase the statistics of parse failures and dropped packets.
+ StatsMgr::instance().addValue("pkt4-parse-failed",
+ static_cast<int64_t>(1));
+ StatsMgr::instance().addValue("pkt4-receive-drop",
+ static_cast<int64_t>(1));
+
+ handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
+
+ return (0);
+ }
+
+ // Avoid to unpack it a second time!
+ handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
+
+ return (0);
+}
+
+/// @brief This callout is called at the "pkt4_send" hook.
+///
+/// Remove DHCP specific options and pad the buffer to 300 octets.
+///
+/// @param handle CalloutHandle.
+///
+/// @return 0 upon success, non-zero otherwise.
+int pkt4_send(CalloutHandle& handle) {
+ CalloutHandle::CalloutNextStep status = handle.getStatus();
+ if (status == CalloutHandle::NEXT_STEP_DROP) {
+ return (0);
+ }
+
+ // Get the query message.
+ Pkt4Ptr query;
+ handle.getArgument("query4", query);
+
+ // Check if it is a BOOTP query.
+ if (!query->inClass("BOOTP")) {
+ return (0);
+ }
+
+ // Get the response message.
+ Pkt4Ptr response;
+ handle.getArgument("response4", response);
+
+ if (status == CalloutHandle::NEXT_STEP_SKIP) {
+ isc_throw(InvalidOperation, "packet pack already handled");
+ }
+
+ for (uint16_t code : DHCP_SPECIFIC_OPTIONS) {
+ while (response->delOption(code))
+ ;
+ }
+
+ // Pack the response.
+ try {
+ LOG_DEBUG(bootp_logger, DBGLVL_TRACE_BASIC, BOOTP_PACKET_PACK)
+ .arg(response->getLabel());
+ response->pack();
+
+ // The pack method adds a DHO_END option at the end.
+ isc::util::OutputBuffer& buffer = response->getBuffer();
+ size_t size = buffer.getLength();
+ if (size < BOOT_MIN_SIZE) {
+ size_t delta = BOOT_MIN_SIZE - size;
+ std::vector<uint8_t> zeros(delta, 0);
+ buffer.writeData(&zeros[0], delta);
+ }
+ } catch (const std::exception& ex) {
+ LOG_ERROR(bootp_logger, BOOTP_PACKET_PACK_FAIL)
+ .arg(response->getLabel())
+ .arg(ex.what());
+ }
+
+ // Avoid to pack it a second time!
+ handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
+
+ return (0);
+}
+
+/// @brief This function is called when the library is loaded.
+///
+/// @return always 0.
+int load(LibraryHandle& /* handle */) {
+ LOG_INFO(bootp_logger, BOOTP_LOAD);
+ return (0);
+}
+
+/// @brief This function is called when the library is unloaded.
+///
+/// @return always 0.
+int unload() {
+ LOG_INFO(bootp_logger, BOOTP_UNLOAD);
+ return (0);
+}
+
+/// @brief This function is called to retrieve the multi-threading compatibility.
+///
+/// @return 1 which means compatible with multi-threading.
+int multi_threading_compatible() {
+ return (1);
+}
+
+} // end extern "C"