Kea 2.5.8
pkt_transform.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2024 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 (auto const& it : options) {
120 // Get options with their position (offset).
121 boost::shared_ptr<LocalizedOption> option =
122 boost::dynamic_pointer_cast<LocalizedOption>(it.second);
123 if (option == NULL) {
124 isc_throw(isc::BadValue, "option is null");
125 }
126 uint32_t offset = option->getOffset();
127 if ((offset == 0) ||
128 (offset + option->len() > in_buffer.size())) {
130 "option offset for option: " << option->getType()
131 << " is out of bounds (expected 1.."
132 << in_buffer.size() - option->len() << ")");
133 }
134
135 // Create temporary buffer to store option contents.
136 util::OutputBuffer buf(option->len());
137 // Pack option contents into temporary buffer.
138 option->pack(buf);
139 // OutputBuffer class has nice functions that write
140 // data at the specified position so we can use it to
141 // inject contents of temporary buffer to output buffer.
142 const uint8_t *buf_data = buf.getData();
143 for (size_t i = 0; i < buf.getLength(); ++i) {
144 out_buffer.writeUint8At(buf_data[i], offset + i);
145 }
146 }
147 } catch (const Exception&) {
148 isc_throw(isc::BadValue, "failed to pack options into buffer.");
149 }
150}
151
152void
153PktTransform::unpackOptions(const OptionBuffer& in_buffer,
154 const OptionCollection& options) {
155 for (auto const& it : options) {
156
157 boost::shared_ptr<LocalizedOption> option =
158 boost::dynamic_pointer_cast<LocalizedOption>(it.second);
159 if (option == NULL) {
160 isc_throw(isc::BadValue, "option is null");
161 }
162 size_t opt_pos = option->getOffset();
163 if (opt_pos == 0) {
164 isc_throw(isc::BadValue, "failed to unpack packet from raw buffer "
165 "(Option position not specified)");
166 } else if (opt_pos + option->getHeaderLen() > in_buffer.size()) {
168 "failed to unpack options from from raw buffer "
169 "(Option position out of bounds)");
170 }
171
172 size_t offset = opt_pos;
173 size_t offset_step = 1;
174 uint16_t opt_type = 0;
175 if (option->getUniverse() == Option::V6) {
176 offset_step = 2;
177 // For DHCPv6 option type is in first two octets.
178 opt_type = in_buffer[offset] * 256 + in_buffer[offset + 1];
179 } else {
180 // For DHCPv4 option type is in first octet.
181 opt_type = in_buffer[offset];
182 }
183 // Check if we got expected option type.
184 if (opt_type != option->getType()) {
186 "failed to unpack option from raw buffer "
187 "(option type mismatch)");
188 }
189
190 // Get option length which is supposed to be after option type.
191 offset += offset_step;
192 const uint16_t opt_len =
193 (option->getUniverse() == Option::V6) ?
194 in_buffer[offset] * 256 + in_buffer[offset + 1] :
195 in_buffer[offset];
196
197 // Check if packet is not truncated.
198 if (offset + option->getHeaderLen() + opt_len > in_buffer.size()) {
200 "failed to unpack option from raw buffer "
201 "(option truncated)");
202 }
203
204 // Seek to actual option data and replace it.
205 offset += offset_step;
206 option->setData(in_buffer.begin() + offset,
207 in_buffer.begin() + offset + opt_len);
208 }
209}
210
211void
212PktTransform::writeAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos,
213 dhcp::OptionBuffer::iterator first,
214 dhcp::OptionBuffer::iterator last) {
215 memcpy(&in_buffer[dest_pos], &(*first), std::distance(first, last));
216}
217
218} // namespace perfdhcp
219} // 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:343
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition: buffer.h:556
void clear()
Clear buffer content.
Definition: buffer.h:466
void writeUint8At(uint8_t data, size_t position)
Write an unsigned 8-bit integer into the buffer.
Definition: buffer.h:485
#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.