diff options
Diffstat (limited to 'src/bin/dhcp4/dhcp4to6_ipc.cc')
-rw-r--r-- | src/bin/dhcp4/dhcp4to6_ipc.cc | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/src/bin/dhcp4/dhcp4to6_ipc.cc b/src/bin/dhcp4/dhcp4to6_ipc.cc new file mode 100644 index 0000000..bf34028 --- /dev/null +++ b/src/bin/dhcp4/dhcp4to6_ipc.cc @@ -0,0 +1,191 @@ +// Copyright (C) 2015-2022 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 <util/buffer.h> +#include <dhcp/iface_mgr.h> +#include <dhcp/pkt4.h> +#include <dhcp/pkt4o6.h> +#include <dhcp/pkt6.h> +#include <dhcpsrv/callout_handle_store.h> +#include <dhcpsrv/cfgmgr.h> +#include <dhcp4/ctrl_dhcp4_srv.h> +#include <dhcp4/dhcp4to6_ipc.h> +#include <dhcp4/dhcp4_log.h> +#include <hooks/callout_handle.h> +#include <hooks/hooks_log.h> +#include <hooks/hooks_manager.h> + +using namespace std; +using namespace isc::dhcp; +using namespace isc::hooks; + +namespace isc { +namespace dhcp { + +Dhcp4to6Ipc::Dhcp4to6Ipc() : Dhcp4o6IpcBase() {} + +Dhcp4to6Ipc& Dhcp4to6Ipc::instance() { + static Dhcp4to6Ipc dhcp4to6_ipc; + return (dhcp4to6_ipc); +} + +void Dhcp4to6Ipc::open() { + uint16_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port(); + if (port == 0) { + Dhcp4o6IpcBase::close(); + return; + } + if (port > 65534) { + isc_throw(OutOfRange, "DHCP4o6 port " << port); + } + + int old_fd = socket_fd_; + socket_fd_ = Dhcp4o6IpcBase::open(port, ENDPOINT_TYPE_V4); + if ((old_fd == -1) && (socket_fd_ != old_fd)) { + IfaceMgr::instance().addExternalSocket(socket_fd_, + Dhcp4to6Ipc::handler); + } +} + +void Dhcp4to6Ipc::handler(int /* fd */) { + Dhcp4to6Ipc& ipc = Dhcp4to6Ipc::instance(); + Pkt6Ptr pkt; + + try { + LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_DHCP4O6_RECEIVING); + // Receive message from the IPC socket. + pkt = ipc.receive(); + + // from Dhcpv4Srv::run_one() after receivePacket() + if (pkt) { + LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC, DHCP4_DHCP4O6_PACKET_RECEIVED) + .arg(static_cast<int>(pkt->getType())) + .arg(pkt->getRemoteAddr().toText()) + .arg(pkt->getRemotePort()) + .arg(pkt->getIface()); + } + } catch (const std::exception& e) { + LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_DHCP4O6_RECEIVE_FAIL) + .arg(e.what()); + } + + if (!pkt) { + return; + } + + // Each message must contain option holding DHCPv4 message. + OptionCollection msgs = pkt->getOptions(D6O_DHCPV4_MSG); + if (msgs.empty()) { + LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_DHCP4O6_BAD_PACKET) + .arg("DHCPv4 message option not present"); + return; + } else if (msgs.size() > 1) { + LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_DHCP4O6_BAD_PACKET) + .arg("more than one DHCPv4 message option"); + return; + } + + // Get the DHCPv4 message. + OptionPtr msg = msgs.begin()->second; + if (!msg) { + LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_DHCP4O6_BAD_PACKET) + .arg("null DHCPv4 message option"); + return; + } + + // Extract the DHCPv4 packet with DHCPv6 packet attached + Pkt4Ptr query(new Pkt4o6(msg->getData(), pkt)); + + // From Dhcpv4Srv::run_one() processing and after + Pkt4Ptr rsp; + + ControlledDhcpv4Srv::getInstance()->processPacket(query, rsp, false); + + if (!rsp) { + return; + } + + try { + // Now all fields and options are constructed into output wire buffer. + // Option objects modification does not make sense anymore. Hooks + // can only manipulate wire buffer at this stage. + // Let's execute all callouts registered for buffer4_send + if (HooksManager::calloutsPresent(Dhcpv4Srv::getHookIndexBuffer4Send())) { + CalloutHandlePtr callout_handle = getCalloutHandle(query); + + // Delete previously set arguments + callout_handle->deleteAllArguments(); + + // Use the RAII wrapper to make sure that the callout handle state is + // reset when this object goes out of scope. All hook points must do + // it to prevent possible circular dependency between the callout + // handle and its arguments. + ScopedCalloutHandleState callout_handle_state(callout_handle); + + // Enable copying options from the packet within hook library. + ScopedEnableOptionsCopy<Pkt4> response4_options_copy(rsp); + + // Pass incoming packet as argument + callout_handle->setArgument("response4", rsp); + + // Call callouts + HooksManager::callCallouts(Dhcpv4Srv::getHookIndexBuffer4Send(), + *callout_handle); + + // Callouts decided to skip the next processing step. The next + // processing step would be to parse the packet, so skip at this + // stage means drop. + if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) || + (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) { + LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS, + DHCP4_HOOK_BUFFER_SEND_SKIP) + .arg(rsp->getLabel()); + return; + } + + callout_handle->getArgument("response4", rsp); + } + + Pkt4o6Ptr rsp6 = boost::dynamic_pointer_cast<Pkt4o6>(rsp); + // Should not happen + if (!rsp6) { + isc_throw(Unexpected, "Dhcp4o6 packet cast fail"); + } + + LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC, DHCP4_DHCP4O6_PACKET_SEND) + .arg(rsp6->getLabel()) + .arg(rsp6->getName()) + .arg(static_cast<int>(rsp6->getType())) + .arg(rsp6->getRemoteAddr()) + .arg(rsp6->getRemotePort()) + .arg(rsp6->getIface()) + .arg(rsp->getLabel()) + .arg(rsp->getName()) + .arg(static_cast<int>(rsp->getType())); + + LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL_DATA, + DHCP4_DHCP4O6_RESPONSE_DATA) + .arg(rsp6->getLabel()) + .arg(rsp6->getName()) + .arg(static_cast<int>(rsp6->getType())) + .arg(rsp6->toText()); + + ipc.send(rsp6->getPkt6()); + + // Update statistics accordingly for sent packet. + Dhcpv4Srv::processStatsSent(rsp); + + } catch (const std::exception& e) { + LOG_ERROR(packet4_logger, DHCP4_DHCP4O6_PACKET_SEND_FAIL) + .arg(rsp->getLabel()) + .arg(e.what()); + } +} + +} // namespace dhcp +} // namespace isc |