Kea  2.1.7-git
pkt_transform.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-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 
11 #include <perfdhcp/stats_mgr.h>
12 
13 #include <exceptions/exceptions.h>
14 #include <dhcp/option.h>
15 #include <dhcp/libdhcp++.h>
16 #include <dhcp/dhcp6.h>
17 
18 #include <iostream>
19 
20 
21 using namespace std;
22 using namespace isc;
23 using namespace dhcp;
24 
25 namespace isc {
26 namespace perfdhcp {
27 
28 bool
29 PktTransform::pack(const Option::Universe universe,
30  const OptionBuffer& in_buffer,
31  const OptionCollection& options,
32  const size_t transid_offset,
33  const uint32_t transid,
34  util::OutputBuffer& out_buffer) {
35 
36  // Always override the packet if function is called.
37  out_buffer.clear();
38  // Write whole buffer to output buffer.
39  out_buffer.writeData(&in_buffer[0], in_buffer.size());
40 
41  uint8_t transid_len = (universe == Option::V6) ? 3 : 4;
42 
43  if ((transid_offset + transid_len >= in_buffer.size()) ||
44  (transid_offset == 0)) {
45  cout << "Failed to build packet: provided transaction id offset: "
46  << transid_offset << " is out of bounds (expected 1.."
47  << in_buffer.size()-1 << ")." << endl;
48  return (false);
49  }
50 
51  try {
52  size_t offset_ptr = transid_offset;
53  if (universe == Option::V4) {
54  out_buffer.writeUint8At(transid >> 24 & 0xFF, offset_ptr++);
55  }
56  out_buffer.writeUint8At(transid >> 16 & 0xFF, offset_ptr++);
57  out_buffer.writeUint8At(transid >> 8 & 0xFF, offset_ptr++);
58  out_buffer.writeUint8At(transid & 0xFF, offset_ptr++);
59 
60  // We already have packet template stored in output buffer
61  // but still some options have to be updated if client
62  // specified them along with their offsets in the buffer.
63  PktTransform::packOptions(in_buffer, options, out_buffer);
64  } catch (const isc::BadValue& e) {
65  cout << "Building packet failed: " << e.what() << endl;
66  return (false);
67  }
68  return (true);
69 }
70 
71 bool
72 PktTransform::unpack(const Option::Universe universe,
73  const OptionBuffer& in_buffer,
74  const OptionCollection& options,
75  const size_t transid_offset,
76  uint32_t& transid) {
77 
78  uint8_t transid_len = (universe == Option::V6) ? 3 : 4;
79 
80  // Validate transaction id offset.
81  if ((transid_offset + transid_len + 1 > in_buffer.size()) ||
82  (transid_offset == 0)) {
83  cout << "Failed to parse packet: provided transaction id offset: "
84  << transid_offset << " is out of bounds (expected 1.."
85  << in_buffer.size()-1 << ")." << endl;
86  return (false);
87  }
88 
89  // Read transaction id from the buffer.
90  // For DHCPv6 we transaction id is 3 bytes long so the high byte
91  // of transid will be zero.
92  OptionBufferConstIter it = in_buffer.begin() + transid_offset;
93  transid = 0;
94  for (int i = 0; i < transid_len; ++i, ++it) {
95  // Read next byte and shift it left to its position in
96  // transid (shift by the number of bytes read so far.
97  transid += *it << (transid_len - i - 1) * 8;
98  }
99 
100  try {
101  PktTransform::unpackOptions(in_buffer, options);
102  } catch (const isc::BadValue& e) {
103  ExchangeStats::malformed_pkts_++;
104  cout << "Packet parsing failed: " << e.what() << endl;
105  return (false);
106  }
107 
108  return (true);
109 }
110 
111 void
112 PktTransform::packOptions(const OptionBuffer& in_buffer,
113  const OptionCollection& options,
114  util::OutputBuffer& out_buffer) {
115  try {
116  // If there are any options on the list, we will use provided
117  // options offsets to override them in the output buffer
118  // with new contents.
119  for (OptionCollection::const_iterator it = options.begin();
120  it != options.end(); ++it) {
121  // Get options with their position (offset).
122  boost::shared_ptr<LocalizedOption> option =
123  boost::dynamic_pointer_cast<LocalizedOption>(it->second);
124  if (option == NULL) {
125  isc_throw(isc::BadValue, "option is null");
126  }
127  uint32_t offset = option->getOffset();
128  if ((offset == 0) ||
129  (offset + option->len() > in_buffer.size())) {
131  "option offset for option: " << option->getType()
132  << " is out of bounds (expected 1.."
133  << in_buffer.size() - option->len() << ")");
134  }
135 
136  // Create temporary buffer to store option contents.
137  util::OutputBuffer buf(option->len());
138  // Pack option contents into temporary buffer.
139  option->pack(buf);
140  // OutputBuffer class has nice functions that write
141  // data at the specified position so we can use it to
142  // inject contents of temporary buffer to output buffer.
143  const uint8_t *buf_data =
144  static_cast<const uint8_t*>(buf.getData());
145  for (size_t i = 0; i < buf.getLength(); ++i) {
146  out_buffer.writeUint8At(buf_data[i], offset + i);
147  }
148  }
149  }
150  catch (const Exception&) {
151  isc_throw(isc::BadValue, "failed to pack options into buffer.");
152  }
153 }
154 
155 void
156 PktTransform::unpackOptions(const OptionBuffer& in_buffer,
157  const OptionCollection& options) {
158  for (OptionCollection::const_iterator it = options.begin();
159  it != options.end(); ++it) {
160 
161  boost::shared_ptr<LocalizedOption> option =
162  boost::dynamic_pointer_cast<LocalizedOption>(it->second);
163  if (option == NULL) {
164  isc_throw(isc::BadValue, "option is null");
165  }
166  size_t opt_pos = option->getOffset();
167  if (opt_pos == 0) {
168  isc_throw(isc::BadValue, "failed to unpack packet from raw buffer "
169  "(Option position not specified)");
170  } else if (opt_pos + option->getHeaderLen() > in_buffer.size()) {
172  "failed to unpack options from from raw buffer "
173  "(Option position out of bounds)");
174  }
175 
176  size_t offset = opt_pos;
177  size_t offset_step = 1;
178  uint16_t opt_type = 0;
179  if (option->getUniverse() == Option::V6) {
180  offset_step = 2;
181  // For DHCPv6 option type is in first two octets.
182  opt_type = in_buffer[offset] * 256 + in_buffer[offset + 1];
183  } else {
184  // For DHCPv4 option type is in first octet.
185  opt_type = in_buffer[offset];
186  }
187  // Check if we got expected option type.
188  if (opt_type != option->getType()) {
190  "failed to unpack option from raw buffer "
191  "(option type mismatch)");
192  }
193 
194  // Get option length which is supposed to be after option type.
195  offset += offset_step;
196  const uint16_t opt_len =
197  (option->getUniverse() == Option::V6) ?
198  in_buffer[offset] * 256 + in_buffer[offset + 1] :
199  in_buffer[offset];
200 
201  // Check if packet is not truncated.
202  if (offset + option->getHeaderLen() + opt_len > in_buffer.size()) {
204  "failed to unpack option from raw buffer "
205  "(option truncated)");
206  }
207 
208  // Seek to actual option data and replace it.
209  offset += offset_step;
210  option->setData(in_buffer.begin() + offset,
211  in_buffer.begin() + offset + opt_len);
212  }
213 }
214 
215 void
216 PktTransform::writeAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos,
217  dhcp::OptionBuffer::iterator first,
218  dhcp::OptionBuffer::iterator last) {
219  memcpy(&in_buffer[dest_pos], &(*first), std::distance(first, last));
220 }
221 
222 } // namespace perfdhcp
223 } // namespace isc
DHCP option at specific offset.
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:83
STL namespace.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
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.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
void clear()
Clear buffer content.
Definition: buffer.h:451
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition: option.h:40
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
OptionBuffer::const_iterator OptionBufferConstIter
const_iterator for walking over OptionBuffer
Definition: option.h:30
This is a base class for exceptions thrown from the DNS library module.
Defines the logger used by the top-level component of kea-lfc.
void writeUint8At(uint8_t data, size_t pos)
Write an unsigned 8-bit integer into the buffer.
Definition: buffer.h:479