Kea 2.5.5
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
14#include <dhcp/option.h>
15#include <dhcp/libdhcp++.h>
16#include <dhcp/dhcp6.h>
17
18#include <iostream>
19
20
21using namespace std;
22using namespace isc;
23using namespace dhcp;
24
25namespace isc {
26namespace perfdhcp {
27
28bool
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
71bool
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) {
104 cout << "Packet parsing failed: " << e.what() << endl;
105 return (false);
106 }
107
108 return (true);
109}
110
111void
112PktTransform::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
155void
156PktTransform::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
215void
216PktTransform::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
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:83
static bool unpack(const dhcp::Option::Universe universe, const dhcp::OptionBuffer &in_buffer, const dhcp::OptionCollection &options, const size_t transid_offset, uint32_t &transid)
Handles selective binary packet parsing.
static void writeAt(dhcp::OptionBuffer &in_buffer, size_t dest_pos, std::vector< uint8_t >::iterator first, std::vector< uint8_t >::iterator last)
Replace contents of buffer with vector.
static bool pack(const dhcp::Option::Universe universe, const dhcp::OptionBuffer &in_buffer, const dhcp::OptionCollection &options, const size_t transid_offset, const uint32_t transid, util::OutputBuffer &out_buffer)
Prepares on-wire format from raw buffer.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
void writeUint8At(uint8_t data, size_t pos)
Write an unsigned 8-bit integer into the buffer.
Definition: buffer.h:479
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition: buffer.h:550
void clear()
Clear buffer content.
Definition: buffer.h:451
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
OptionBuffer::const_iterator OptionBufferConstIter
const_iterator for walking over OptionBuffer
Definition: option.h:30
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition: option.h:40
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
Defines the logger used by the top-level component of kea-lfc.