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