Kea 3.2.1-git
pkt_transform.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2026 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 if (in_buffer.empty()) {
39 isc_throw(Unexpected, "no packet data");
40 }
41 // Write whole buffer to output buffer.
42 out_buffer.writeData(&in_buffer[0], in_buffer.size());
43
44 uint8_t transid_len = (universe == Option::V6) ? 3 : 4;
45
46 if ((transid_offset + transid_len >= in_buffer.size()) ||
47 (transid_offset == 0)) {
48 cout << "Failed to build packet: provided transaction id offset: "
49 << transid_offset << " is out of bounds (expected 1.."
50 << in_buffer.size()-1 << ")." << endl;
51 return (false);
52 }
53
54 try {
55 size_t offset_ptr = transid_offset;
56 if (universe == Option::V4) {
57 out_buffer.writeUint8At(transid >> 24 & 0xFF, offset_ptr++);
58 }
59 out_buffer.writeUint8At(transid >> 16 & 0xFF, offset_ptr++);
60 out_buffer.writeUint8At(transid >> 8 & 0xFF, offset_ptr++);
61 out_buffer.writeUint8At(transid & 0xFF, offset_ptr++);
62
63 // We already have packet template stored in output buffer
64 // but still some options have to be updated if client
65 // specified them along with their offsets in the buffer.
66 PktTransform::packOptions(in_buffer, options, out_buffer);
67 } catch (const isc::BadValue& e) {
68 cout << "Building packet failed: " << e.what() << endl;
69 return (false);
70 }
71 return (true);
72}
73
74bool
76 const OptionBuffer& in_buffer,
77 const OptionCollection& options,
78 const size_t transid_offset,
79 uint32_t& transid) {
80
81 uint8_t transid_len = (universe == Option::V6) ? 3 : 4;
82
83 // Validate transaction id offset.
84 if ((transid_offset + transid_len + 1 > in_buffer.size()) ||
85 (transid_offset == 0)) {
86 cout << "Failed to parse packet: provided transaction id offset: "
87 << transid_offset << " is out of bounds (expected 1.."
88 << in_buffer.size()-1 << ")." << endl;
89 return (false);
90 }
91
92 // Read transaction id from the buffer.
93 // For DHCPv6 we transaction id is 3 bytes long so the high byte
94 // of transid will be zero.
95 OptionBufferConstIter it = in_buffer.begin() + transid_offset;
96 transid = 0;
97 for (int i = 0; i < transid_len; ++i, ++it) {
98 // Read next byte and shift it left to its position in
99 // transid (shift by the number of bytes read so far.
100 transid += *it << (transid_len - i - 1) * 8;
101 }
102
103 try {
104 PktTransform::unpackOptions(in_buffer, options);
105 } catch (const isc::BadValue& e) {
107 cout << "Packet parsing failed: " << e.what() << endl;
108 return (false);
109 }
110
111 return (true);
112}
113
114void
115PktTransform::packOptions(const OptionBuffer& in_buffer,
116 const OptionCollection& options,
117 util::OutputBuffer& out_buffer) {
118 try {
119 // If there are any options on the list, we will use provided
120 // options offsets to override them in the output buffer
121 // with new contents.
122 for (auto const& it : options) {
123 // Get options with their position (offset).
124 boost::shared_ptr<LocalizedOption> option =
125 boost::dynamic_pointer_cast<LocalizedOption>(it.second);
126 if (option == NULL) {
127 isc_throw(isc::BadValue, "option is null");
128 }
129 uint32_t offset = option->getOffset();
130 if ((offset == 0) ||
131 (offset + option->len() > in_buffer.size())) {
133 "option offset for option: " << option->getType()
134 << " is out of bounds (expected 1.."
135 << in_buffer.size() - option->len() << ")");
136 }
137
138 // Create temporary buffer to store option contents.
139 util::OutputBuffer buf(option->len());
140 // Pack option contents into temporary buffer.
141 option->pack(buf);
142 // OutputBuffer class has nice functions that write
143 // data at the specified position so we can use it to
144 // inject contents of temporary buffer to output buffer.
145 const uint8_t *buf_data = buf.getData();
146 for (size_t i = 0; i < buf.getLength(); ++i) {
147 out_buffer.writeUint8At(buf_data[i], offset + i);
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 (auto const& it : options) {
159
160 boost::shared_ptr<LocalizedOption> option =
161 boost::dynamic_pointer_cast<LocalizedOption>(it.second);
162 if (option == NULL) {
163 isc_throw(isc::BadValue, "option is null");
164 }
165 size_t opt_pos = option->getOffset();
166 if (opt_pos == 0) {
167 isc_throw(isc::BadValue, "failed to unpack packet from raw buffer "
168 "(Option position not specified)");
169 } else if (opt_pos + option->getHeaderLen() > in_buffer.size()) {
170 isc_throw(isc::BadValue,
171 "failed to unpack options from from raw buffer "
172 "(Option position out of bounds)");
173 }
174
175 size_t offset = opt_pos;
176 size_t offset_step = 1;
177 uint16_t opt_type = 0;
178 if (option->getUniverse() == Option::V6) {
179 offset_step = 2;
180 // For DHCPv6 option type is in first two octets.
181 opt_type = in_buffer[offset] * 256 + in_buffer[offset + 1];
182 } else {
183 // For DHCPv4 option type is in first octet.
184 opt_type = in_buffer[offset];
185 }
186 // Check if we got expected option type.
187 if (opt_type != option->getType()) {
188 isc_throw(isc::BadValue,
189 "failed to unpack option from raw buffer "
190 "(option type mismatch)");
191 }
192
193 // Get option length which is supposed to be after option type.
194 offset += offset_step;
195 const uint16_t opt_len =
196 (option->getUniverse() == Option::V6) ?
197 in_buffer[offset] * 256 + in_buffer[offset + 1] :
198 in_buffer[offset];
199
200 // Check if packet is not truncated.
201 if (offset + option->getHeaderLen() + opt_len > in_buffer.size()) {
202 isc_throw(isc::BadValue,
203 "failed to unpack option from raw buffer "
204 "(option truncated)");
205 }
206
207 // Seek to actual option data and replace it.
208 offset += offset_step;
209 option->setData(in_buffer.begin() + offset,
210 in_buffer.begin() + offset + opt_len);
211 }
212}
213
214void
215PktTransform::writeAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos,
216 dhcp::OptionBuffer::iterator first,
217 dhcp::OptionBuffer::iterator last) {
218 auto length = std::distance(first, last);
219 if (length <= 0) {
220 return;
221 }
222 if (in_buffer.size() < dest_pos + length) {
223 isc_throw(OutOfRange, "writeAt: pos=" << dest_pos << ", length="
224 << length << ", size=" << in_buffer.size());
225 }
226 memcpy(&in_buffer[dest_pos], &(*first), length);
227}
228
229} // namespace perfdhcp
230} // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
A generic exception that is thrown when an unexpected error condition occurs.
Universe
defines option universe DHCPv4 or DHCPv6
Definition option.h:90
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:346
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition buffer.h:559
void clear()
Clear buffer content.
Definition buffer.h:469
void writeUint8At(uint8_t data, size_t position)
Write an unsigned 8-bit integer into the buffer.
Definition buffer.h:488
#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.