Kea  2.3.6
bootp_callouts.cc
Go to the documentation of this file.
1 // Copyright (C) 2019-2022 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <bootp_log.h>
10 #include <hooks/hooks.h>
11 #include <dhcp/pkt4.h>
12 #include <process/daemon.h>
13 #include <stats/stats_mgr.h>
14 
15 #include <vector>
16 
17 using namespace isc;
18 using namespace isc::bootp;
19 using namespace isc::dhcp;
20 using namespace isc::hooks;
21 using namespace isc::log;
22 using namespace isc::process;
23 using namespace isc::stats;
24 
25 namespace {
26 
27 // DHCP Specific options listed in RFC 1533 section 9 and with a code name
28 // beginning by DHO_DHCP_.
29 const std::vector<uint16_t> DHCP_SPECIFIC_OPTIONS = {
41 };
42 
43 // Size of the BOOTP space for vendor extensions.
44 const size_t BOOT_VENDOR_SPACE_SIZE = 64;
45 
46 // Minimum size of a BOOTP message.
47 const size_t BOOT_MIN_SIZE = Pkt4::DHCPV4_PKT_HDR_LEN + BOOT_VENDOR_SPACE_SIZE;
48 
49 // Check as compile time it is really 300!
50 static_assert(BOOT_MIN_SIZE == 300, "BOOT_MIN_SIZE is not 300");
51 
52 } // end of anonymous namespace.
53 
54 // Functions accessed by the hooks framework use C linkage to avoid the name
55 // mangling that accompanies use of the C++ compiler as well as to avoid
56 // issues related to namespaces.
57 extern "C" {
58 
69  CalloutHandle::CalloutNextStep status = handle.getStatus();
70  if (status == CalloutHandle::NEXT_STEP_DROP) {
71  return (0);
72  }
73 
74  // Get the received unpacked message.
75  Pkt4Ptr query;
76  handle.getArgument("query4", query);
77 
78  try {
79  if (handle.getStatus() != CalloutHandle::NEXT_STEP_SKIP) {
80  query->unpack();
81  }
82 
83  // Not DHCP query nor BOOTP response?
84  if ((query->getType() == DHCP_NOTYPE) &&
85  (query->getOp() == BOOTREQUEST)) {
86 
87  query->addClass("BOOTP");
88  query->setType(DHCPREQUEST);
89 
91  .arg(query->getLabel());
92  }
93  } catch (const SkipRemainingOptionsError& ex) {
94  // An option failed to unpack but we are to attempt to process it
95  // anyway. Log it and let's hope for the best.
98  .arg(ex.what());
99  } catch (const std::exception& ex) {
100  // Failed to parse the packet.
103  .arg(query->getRemoteAddr().toText())
104  .arg(query->getLocalAddr().toText())
105  .arg(query->getIface())
106  .arg(ex.what());
107 
108  // Increase the statistics of parse failures and dropped packets.
109  StatsMgr::instance().addValue("pkt4-parse-failed",
110  static_cast<int64_t>(1));
111  StatsMgr::instance().addValue("pkt4-receive-drop",
112  static_cast<int64_t>(1));
113 
114  handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
115 
116  return (0);
117  }
118 
119  // Avoid to unpack it a second time!
120  handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
121 
122  return (0);
123 }
124 
132 int pkt4_send(CalloutHandle& handle) {
133  CalloutHandle::CalloutNextStep status = handle.getStatus();
134  if (status == CalloutHandle::NEXT_STEP_DROP) {
135  return (0);
136  }
137 
138  // Get the query message.
139  Pkt4Ptr query;
140  handle.getArgument("query4", query);
141 
142  // Check if it is a BOOTP query.
143  if (!query->inClass("BOOTP")) {
144  return (0);
145  }
146 
147  // Get the response message.
148  Pkt4Ptr response;
149  handle.getArgument("response4", response);
150 
151  if (status == CalloutHandle::NEXT_STEP_SKIP) {
152  isc_throw(InvalidOperation, "packet pack already handled");
153  }
154 
155  for (uint16_t code : DHCP_SPECIFIC_OPTIONS) {
156  while (response->delOption(code))
157  ;
158  }
159 
160  // Pack the response.
161  try {
163  .arg(response->getLabel());
164  response->pack();
165 
166  // The pack method adds a DHO_END option at the end.
167  isc::util::OutputBuffer& buffer = response->getBuffer();
168  size_t size = buffer.getLength();
169  if (size < BOOT_MIN_SIZE) {
170  size_t delta = BOOT_MIN_SIZE - size;
171  std::vector<uint8_t> zeros(delta, 0);
172  buffer.writeData(&zeros[0], delta);
173  }
174  } catch (const std::exception& ex) {
176  .arg(response->getLabel())
177  .arg(ex.what());
178  }
179 
180  // Avoid to pack it a second time!
181  handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
182 
183  return (0);
184 }
185 
189 int load(LibraryHandle& /* handle */) {
190  const std::string& proc_name = Daemon::getProcName();
191  if (proc_name != "kea-dhcp4") {
192  isc_throw(isc::Unexpected, "Bad process name: " << proc_name
193  << ", expected kea-dhcp4");
194  }
196  return (0);
197 }
198 
202 int unload() {
204  return (0);
205 }
206 
211  return (1);
212 }
213 
214 } // end extern "C"
int load(LibraryHandle &)
This function is called when the library is loaded.
int multi_threading_compatible()
This function is called to retrieve the multi-threading compatibility.
int pkt4_send(CalloutHandle &handle)
This callout is called at the "pkt4_send" hook.
int unload()
This function is called when the library is unloaded.
int buffer4_receive(CalloutHandle &handle)
This callout is called at the "buffer4_receive" hook.
const isc::log::MessageID BOOTP_LOAD
const isc::log::MessageID BOOTP_PACKET_UNPACK_FAILED
const isc::log::MessageID BOOTP_PACKET_PACK
const isc::log::MessageID BOOTP_UNLOAD
const isc::log::MessageID BOOTP_PACKET_OPTIONS_SKIPPED
const isc::log::MessageID BOOTP_BOOTP_QUERY
const isc::log::MessageID BOOTP_PACKET_PACK_FAIL
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown if a function is called in a prohibited way.
A generic exception that is thrown when an unexpected error condition occurs.
Exception thrown during option unpacking This exception is thrown when an error has occurred,...
Definition: option.h:52
Per-packet callout handle.
CalloutNextStep
Specifies allowed next steps.
CalloutNextStep getStatus() const
Returns the next processing step.
void setStatus(const CalloutNextStep next)
Sets the next processing step.
void getArgument(const std::string &name, T &value) const
Get argument.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition: buffer.h:550
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
isc::log::Logger bootp_logger("bootp-hooks")
Definition: bootp_log.h:18
@ DHO_DHCP_MAX_MESSAGE_SIZE
Definition: dhcp4.h:126
@ DHO_DHCP_MESSAGE
Definition: dhcp4.h:125
@ DHO_DHCP_REBINDING_TIME
Definition: dhcp4.h:128
@ DHO_DHCP_MESSAGE_TYPE
Definition: dhcp4.h:122
@ DHO_DHCP_SERVER_IDENTIFIER
Definition: dhcp4.h:123
@ DHO_DHCP_CLIENT_IDENTIFIER
Definition: dhcp4.h:130
@ DHO_DHCP_REQUESTED_ADDRESS
Definition: dhcp4.h:119
@ DHO_DHCP_OPTION_OVERLOAD
Definition: dhcp4.h:121
@ DHO_DHCP_PARAMETER_REQUEST_LIST
Definition: dhcp4.h:124
@ DHO_DHCP_RENEWAL_TIME
Definition: dhcp4.h:127
@ DHO_DHCP_LEASE_TIME
Definition: dhcp4.h:120
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:547
@ BOOTREQUEST
Definition: dhcp4.h:46
@ DHCPREQUEST
Definition: dhcp4.h:236
@ DHCP_NOTYPE
Message Type option missing.
Definition: dhcp4.h:233
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Definition: log_dbglevels.h:69
Defines the logger used by the top-level component of kea-lfc.