Kea  1.9.9-git
bootp_callouts.cc
Go to the documentation of this file.
1 // Copyright (C) 2019-2020 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 <stats/stats_mgr.h>
13 
14 #include <vector>
15 
16 using namespace isc;
17 using namespace isc::bootp;
18 using namespace isc::dhcp;
19 using namespace isc::hooks;
20 using namespace isc::log;
21 using namespace isc::stats;
22 
23 namespace {
24 
25 // DHCP Specific options listed in RFC 1533 section 9 and with a code name
26 // beginning by DHO_DHCP_.
27 const std::vector<uint16_t> DHCP_SPECIFIC_OPTIONS = {
39 };
40 
41 // Size of the BOOTP space for vendor extensions.
42 const size_t BOOT_VENDOR_SPACE_SIZE = 64;
43 
44 // Minimum size of a BOOTP message.
45 const size_t BOOT_MIN_SIZE = Pkt4::DHCPV4_PKT_HDR_LEN + BOOT_VENDOR_SPACE_SIZE;
46 
47 // Check as compile time it is really 300!
48 static_assert(BOOT_MIN_SIZE == 300, "BOOT_MIN_SIZE is not 300");
49 
50 } // end of anonymous namespace.
51 
52 // Functions accessed by the hooks framework use C linkage to avoid the name
53 // mangling that accompanies use of the C++ compiler as well as to avoid
54 // issues related to namespaces.
55 extern "C" {
56 
67  CalloutHandle::CalloutNextStep status = handle.getStatus();
68  if (status == CalloutHandle::NEXT_STEP_DROP) {
69  return (0);
70  }
71 
72  // Get the received unpacked message.
73  Pkt4Ptr query;
74  handle.getArgument("query4", query);
75 
76  try {
77  if (handle.getStatus() != CalloutHandle::NEXT_STEP_SKIP) {
78  query->unpack();
79  }
80 
81  // Not DHCP query nor BOOTP response?
82  if ((query->getType() == DHCP_NOTYPE) &&
83  (query->getOp() == BOOTREQUEST)) {
84 
85  query->addClass("BOOTP");
86  query->setType(DHCPREQUEST);
87 
89  .arg(query->getLabel());
90  }
91  } catch (const SkipRemainingOptionsError& ex) {
92  // An option failed to unpack but we are to attempt to process it
93  // anyway. Log it and let's hope for the best.
96  .arg(ex.what());
97  } catch (const std::exception& ex) {
98  // Failed to parse the packet.
101  .arg(query->getRemoteAddr().toText())
102  .arg(query->getLocalAddr().toText())
103  .arg(query->getIface())
104  .arg(ex.what());
105 
106  // Increase the statistics of parse failures and dropped packets.
107  StatsMgr::instance().addValue("pkt4-parse-failed",
108  static_cast<int64_t>(1));
109  StatsMgr::instance().addValue("pkt4-receive-drop",
110  static_cast<int64_t>(1));
111 
112  handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
113 
114  return (0);
115  }
116 
117  // Avoid to unpack it a second time!
118  handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
119 
120  return (0);
121 }
122 
130 int pkt4_send(CalloutHandle& handle) {
131  CalloutHandle::CalloutNextStep status = handle.getStatus();
132  if (status == CalloutHandle::NEXT_STEP_DROP) {
133  return (0);
134  }
135 
136  // Get the query message.
137  Pkt4Ptr query;
138  handle.getArgument("query4", query);
139 
140  // Check if it is a BOOTP query.
141  if (!query->inClass("BOOTP")) {
142  return (0);
143  }
144 
145  // Get the response message.
146  Pkt4Ptr response;
147  handle.getArgument("response4", response);
148 
149  if (status == CalloutHandle::NEXT_STEP_SKIP) {
150  isc_throw(InvalidOperation, "packet pack already handled");
151  }
152 
153  for (uint16_t code : DHCP_SPECIFIC_OPTIONS) {
154  while (response->delOption(code))
155  ;
156  }
157 
158  // Pack the response.
159  try {
161  .arg(response->getLabel());
162  response->pack();
163 
164  // The pack method adds a DHO_END option at the end.
165  isc::util::OutputBuffer& buffer = response->getBuffer();
166  size_t size = buffer.getLength();
167  if (size < BOOT_MIN_SIZE) {
168  size_t delta = BOOT_MIN_SIZE - size;
169  std::vector<uint8_t> zeros(delta, 0);
170  buffer.writeData(&zeros[0], delta);
171  }
172  } catch (const std::exception& ex) {
174  .arg(response->getLabel())
175  .arg(ex.what());
176  }
177 
178  // Avoid to pack it a second time!
179  handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
180 
181  return (0);
182 }
183 
187 int load(LibraryHandle& /* handle */) {
189  return (0);
190 }
191 
195 int unload() {
197  return (0);
198 }
199 
204  return (1);
205 }
206 
207 } // end extern "C"
int pkt4_send(CalloutHandle &handle)
This callout is called at the "pkt4_send" hook.
void setStatus(const CalloutNextStep next)
Sets the next processing step.
int load(LibraryHandle &)
This function is called when the library is loaded.
Message Type option missing.
Definition: dhcp4.h:229
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Definition: log_dbglevels.h:65
const isc::log::MessageID BOOTP_BOOTP_QUERY
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
Exception thrown during option unpacking This exception is thrown when an error has occurred...
Definition: option.h:51
const isc::log::MessageID BOOTP_PACKET_PACK
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
const isc::log::MessageID BOOTP_LOAD
const isc::log::MessageID BOOTP_PACKET_PACK_FAIL
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition: buffer.h:550
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
int unload()
This function is called when the library is unloaded.
Per-packet callout handle.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:544
CalloutNextStep
Specifies allowed next steps.
const isc::log::MessageID BOOTP_UNLOAD
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
int buffer4_receive(CalloutHandle &handle)
This callout is called at the "buffer4_receive" hook.
Defines the logger used by the top-level component of kea-dhcp-ddns.
A generic exception that is thrown if a function is called in a prohibited way.
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
void getArgument(const std::string &name, T &value) const
Get argument.
isc::log::Logger bootp_logger("bootp-hooks")
Definition: bootp_log.h:18
const isc::log::MessageID BOOTP_PACKET_OPTIONS_SKIPPED
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
int multi_threading_compatible()
This function is called to retrieve the multi-threading compatibility.
const isc::log::MessageID BOOTP_PACKET_UNPACK_FAILED
CalloutNextStep getStatus() const
Returns the next processing step.