1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
// Copyright (C) 2014-2023 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <exceptions/exceptions.h>
#include <dhcp/opaque_data_tuple.h>
#include <dhcp/option_vendor_class.h>
#include <sstream>
namespace isc {
namespace dhcp {
OptionVendorClass::OptionVendorClass(Option::Universe u,
const uint32_t vendor_id)
: Option(u, getOptionCode(u)), vendor_id_(vendor_id) {
if (u == Option::V4) {
addTuple(OpaqueDataTuple(OpaqueDataTuple::LENGTH_1_BYTE));
}
}
OptionVendorClass::OptionVendorClass(Option::Universe u,
OptionBufferConstIter begin,
OptionBufferConstIter end)
: Option(u, getOptionCode(u)) {
unpack(begin, end);
}
OptionPtr
OptionVendorClass::clone() const {
return (cloneInternal<OptionVendorClass>());
}
void
OptionVendorClass::pack(isc::util::OutputBuffer& buf, bool check) const {
packHeader(buf, check);
buf.writeUint32(getVendorId());
for (TuplesCollection::const_iterator it = tuples_.begin();
it != tuples_.end(); ++it) {
// For DHCPv4 V-I Vendor Class option, there is enterprise id before
// every tuple.
if ((getUniverse() == V4) && (it != tuples_.begin())) {
buf.writeUint32(getVendorId());
}
it->pack(buf);
}
// That's it. We don't pack any sub-options here, because this option
// must not contain sub-options.
}
void
OptionVendorClass::unpack(OptionBufferConstIter begin,
OptionBufferConstIter end) {
if (std::distance(begin, end) < getMinimalLength() - getHeaderLen()) {
isc_throw(OutOfRange, "parsed Vendor Class option data truncated to"
" size " << std::distance(begin, end));
}
// Option must contain at least one enterprise id. It is ok to read 4-byte
// value here because we have checked that the buffer he minimal length.
vendor_id_ = isc::util::readUint32(&(*begin), distance(begin, end));
begin += sizeof(vendor_id_);
// Start reading opaque data.
size_t offset = 0;
while (offset < std::distance(begin, end)) {
// Parse a tuple.
OpaqueDataTuple tuple(OptionDataTypeUtil::getTupleLenFieldType(getUniverse()),
begin + offset, end);
addTuple(tuple);
// The tuple has been parsed correctly which implies that it is safe to
// advance the offset by its total length.
offset += tuple.getTotalLength();
// For DHCPv4 option, there is enterprise id before every opaque data
// tuple. Let's read it, unless we have already reached the end of
// buffer.
if ((getUniverse() == V4) && (begin + offset != end)) {
// Get the other enterprise id.
uint32_t other_id = isc::util::readUint32(&(*(begin + offset)),
distance(begin + offset,
end));
// Throw if there are two different enterprise ids.
if (other_id != vendor_id_) {
isc_throw(isc::BadValue, "V-I Vendor Class option with two "
"different enterprise ids: " << vendor_id_
<< " and " << other_id);
}
// Advance the offset by the size of enterprise id.
offset += sizeof(vendor_id_);
// If the offset already ran over the buffer length or there is
// no space left for the empty tuple (thus we add 1), we have
// to signal the option truncation.
if (offset + 1 >= std::distance(begin, end)) {
isc_throw(isc::OutOfRange, "truncated DHCPv4 V-I Vendor Class"
" option - it should contain enterprise id followed"
" by opaque data field tuple");
}
}
}
}
void
OptionVendorClass::addTuple(const OpaqueDataTuple& tuple) {
if (tuple.getLengthFieldType() != OptionDataTypeUtil::getTupleLenFieldType(getUniverse())) {
isc_throw(isc::BadValue, "attempted to add opaque data tuple having"
" invalid size of the length field "
<< tuple.getDataFieldSize() << " to Vendor Class option");
}
tuples_.push_back(tuple);
}
void
OptionVendorClass::setTuple(const size_t at, const OpaqueDataTuple& tuple) {
if (at >= getTuplesNum()) {
isc_throw(isc::OutOfRange, "attempted to set an opaque data for the"
" vendor option at position " << at << " which is out of"
" range");
} else if (tuple.getLengthFieldType() != OptionDataTypeUtil::getTupleLenFieldType(getUniverse())) {
isc_throw(isc::BadValue, "attempted to set opaque data tuple having"
" invalid size of the length field "
<< tuple.getDataFieldSize() << " to Vendor Class option");
}
tuples_[at] = tuple;
}
OpaqueDataTuple
OptionVendorClass::getTuple(const size_t at) const {
if (at >= getTuplesNum()) {
isc_throw(isc::OutOfRange, "attempted to get an opaque data for the"
" vendor option at position " << at << " which is out of"
" range. There are only " << getTuplesNum() << " tuples");
}
return (tuples_[at]);
}
bool
OptionVendorClass::hasTuple(const std::string& tuple_str) const {
// Iterate over existing tuples (there shouldn't be many of them),
// and try to match the searched one.
for (TuplesCollection::const_iterator it = tuples_.begin();
it != tuples_.end(); ++it) {
if (*it == tuple_str) {
return (true);
}
}
return (false);
}
uint16_t
OptionVendorClass::len() const {
// The option starts with the header and enterprise id.
uint16_t length = getHeaderLen() + sizeof(uint32_t);
// Now iterate over existing tuples and add their size.
for (TuplesCollection::const_iterator it = tuples_.begin();
it != tuples_.end(); ++it) {
// For DHCPv4 V-I Vendor Class option, there is enterprise id before
// every tuple.
if ((getUniverse() == V4) && (it != tuples_.begin())) {
length += sizeof(uint32_t);
}
length += it->getTotalLength();
}
return (length);
}
std::string
OptionVendorClass::toText(int indent) const {
std::ostringstream s;
// Apply indentation
s << std::string(indent, ' ');
// Print type, length and first occurrence of enterprise id.
s << "type=" << getType() << ", len=" << len() - getHeaderLen() << ", "
" enterprise id=0x" << std::hex << getVendorId() << std::dec;
// Iterate over all tuples and print their size and contents.
for (unsigned i = 0; i < getTuplesNum(); ++i) {
// The DHCPv4 V-I Vendor Class has enterprise id before every tuple.
if ((getUniverse() == V4) && (i > 0)) {
s << ", enterprise id=0x" << std::hex << getVendorId() << std::dec;
}
// Print the tuple.
s << ", data-len" << i << "=" << getTuple(i).getLength();
s << ", vendor-class-data" << i << "='" << getTuple(i) << "'";
}
return (s.str());
}
} // namespace isc::dhcp
} // namespace isc
|