Kea 2.5.9
option_classless_static_route.cc
Go to the documentation of this file.
1// Copyright (C) 2023-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
9#include <asiolink/io_error.h>
10#include <util/io.h>
11#include <util/str.h>
12
14
15using namespace isc::asiolink;
16using namespace isc::util;
17
18namespace isc {
19namespace dhcp {
20
23 bool convenient_notation)
24 : Option(V4, DHO_CLASSLESS_STATIC_ROUTE), static_routes_(), data_len_(0),
25 convenient_notation_(convenient_notation) {
26 unpack(begin, end);
27}
28
31 return (cloneInternal<OptionClasslessStaticRoute>());
32}
33
34void
36 // Header = option code and length.
37 packHeader(buf, check);
38 for (auto const& route : static_routes_) {
39 // 1-5 octets of destination descriptor
40 auto dest = encodeDestinationDescriptor(route);
41 buf.writeData(&dest[0], dest.size());
42 // IP address of the router
43 buf.writeUint32(std::get<2>(route).toUint32());
44 }
45}
46
47void
49 // Classless Static route option data must contain at least 5 octets.
50 // 1 octet - shortest possible destination descriptor (0x00) + 4 octets router IPv4 addr.
51 if (distance(begin, end) < 5) {
52 isc_throw(OutOfRange, "DHCPv4 OptionClasslessStaticRoute "
53 << type_ << " has invalid length=" << distance(begin, end)
54 << ", must be at least 5.");
55 }
56
57 if (convenient_notation_) {
58 // As an alternative to the binary format,
59 // we provide convenience option definition as a string in format:
60 // subnet1 - router1 IP addr, subnet2 - router2 IP addr, ...
61 // e.g.:
62 // 10.0.0.0/8 - 10.2.3.1, 10.229.0.128/25 - 10.1.0.3, ...
63 // where destination descriptors will be encoded as per RFC3442.
64 std::string config_txt = std::string(begin, end);
65 parseConfigData(config_txt);
66 } else {
67 parseWireData(begin, end);
68 }
69
70 calcDataLen();
71}
72
73std::string
75 std::ostringstream stream;
76 std::string in(indent, ' '); // base indentation
77 stream << in << "type=" << type_ << "(CLASSLESS_STATIC_ROUTE), "
78 << "len=" << (len() - getHeaderLen());
79 int i = 0;
80 for (auto const& route : static_routes_) {
81 stream << ", Route " << ++i << " (subnet " << std::get<0>(route).toText() << "/"
82 << static_cast<int>(std::get<1>(route)) << ", router IP "
83 << std::get<2>(route).toText() << ")";
84 }
85
86 return (stream.str());
87}
88
89uint16_t
91 uint16_t len = getHeaderLen();
92 len += data_len_;
93 return (len);
94}
95
96std::vector<uint8_t>
97OptionClasslessStaticRoute::encodeDestinationDescriptor(const StaticRouteTuple& route) {
98 // Encoding as per RFC3442
99 const std::vector<uint8_t>& subnet = std::get<0>(route).toBytes();
100 const uint8_t& mask_width = std::get<1>(route);
101
102 std::vector<uint8_t> res;
103 res.push_back(mask_width);
104 if (mask_width == 0) {
105 // there are no significant octets, destination descriptor is 0 value - one octet long
106 return (res);
107 }
108
109 uint8_t significant_octets = calcSignificantOctets(mask_width);
110 res.insert(res.end(), subnet.begin(), subnet.begin() + significant_octets);
111
112 return (res);
113}
114
115uint8_t
116OptionClasslessStaticRoute::calcSignificantOctets(const uint8_t& mask_width) {
117 return ((mask_width + 7) / 8);
118}
119
120void
121OptionClasslessStaticRoute::calcDataLen() {
122 uint16_t len = 0;
123 for (auto const& route : static_routes_) {
124 // 1-5 octets of destination descriptor
125 len += calcSignificantOctets(std::get<1>(route)) + 1;
126 // IP address of the router
127 len += V4ADDRESS_LEN;
128 }
129
130 data_len_ = len;
131}
132
133void
134OptionClasslessStaticRoute::parseWireData(OptionBufferConstIter begin, OptionBufferConstIter end) {
135 while (begin != end) {
136 // check for truncated data for each static route
137 if (distance(begin, end) < 5) {
138 isc_throw(OutOfRange, "DHCPv4 OptionClasslessStaticRoute "
139 << type_ << " has invalid length=" << distance(begin, end)
140 << ", must be at least 5.");
141 }
142
143 // 1st octet is a width of subnet mask
144 uint8_t mask_width = *begin;
145 if (mask_width > 32) {
146 isc_throw(BadValue, "DHCPv4 OptionClasslessStaticRoute "
147 << type_ << " has invalid value, provided width of subnet mask "
148 << static_cast<int>(mask_width) << " is not valid.");
149 }
150
151 uint8_t significant_octets = calcSignificantOctets(mask_width);
152 ++begin;
153
154 // once we know haw many significant octets there are, check for truncated data again
155 if (distance(begin, end) < (significant_octets + V4ADDRESS_LEN)) {
157 "DHCPv4 OptionClasslessStaticRoute " << type_ << " is truncated.");
158 }
159
160 // following octets are significant octets of the subnet nr
161 uint32_t subnet_octets;
163
164 switch (significant_octets) {
165 case 0:
166 // no-op - this is 0.0.0.0/0 subnet
167 break;
168 case 1:
169 subnet_octets = *begin;
170 subnet_nr = IOAddress(subnet_octets << 24);
171 break;
172 case 2:
173 subnet_octets = readUint16(&(*begin), distance(begin, end));
174 subnet_nr = IOAddress(subnet_octets << 16);
175 break;
176 case 3:
177 // we are reading one octet too much in this case,
178 // but since we did check for truncated data before,
179 // we are safe do so and mask 4th octet with zeros
180 subnet_octets = readUint32(&(*begin), distance(begin, end));
181 subnet_nr = IOAddress(subnet_octets & 0xFFFFFF00);
182 break;
183 case 4:
184 subnet_octets = readUint32(&(*begin), distance(begin, end));
185 subnet_nr = IOAddress(subnet_octets);
186 break;
187 }
188
189 begin += significant_octets;
190
191 // last comes router IPv4 address
192 IOAddress router_addr = IOAddress(readUint32(&(*begin), distance(begin, end)));
193 begin += V4ADDRESS_LEN;
194
195 StaticRouteTuple route = std::make_tuple(subnet_nr, mask_width, router_addr);
196 static_routes_.push_back(route);
197 }
198}
199
200void
201OptionClasslessStaticRoute::parseConfigData(const std::string& config_txt) {
202 // this option allows more than one static route, so let's separate them using comma
203 std::vector<std::string> tokens = str::tokens(config_txt, std::string(","));
204 for (auto const& route_str : tokens) {
205 std::vector<std::string> parts = str::tokens(str::trim(route_str), std::string("-"));
206 if (parts.size() != 2) {
207 isc_throw(BadValue, "DHCPv4 OptionClasslessStaticRoute "
208 << type_
209 << " has invalid value, option definition must"
210 " have comma separated routes formatted as in "
211 "example: 10.229.0.128/25 - 10.229.0.1");
212 }
213
214 std::string txt_subnet_prefix = str::trim(parts[0]);
215
216 // Is this prefix/len notation?
217 size_t pos = txt_subnet_prefix.find('/');
218 if (pos == std::string::npos) {
219 isc_throw(BadValue, "DHCPv4 OptionClasslessStaticRoute "
220 << type_ << " has invalid value, provided IPv4 prefix "
221 << txt_subnet_prefix << " is not valid.");
222 }
223
224 std::string txt_subnet_addr = txt_subnet_prefix.substr(0, pos);
225 IOAddress subnet_addr = IOAddress("::");
226 try {
227 subnet_addr = IOAddress(txt_subnet_addr);
228 if (!subnet_addr.isV4()) {
229 isc_throw(BadValue, "This is not IPv4 address.");
230 }
231 } catch (const std::exception& e) {
232 isc_throw(BadValue, "DHCPv4 OptionClasslessStaticRoute "
233 << type_ << " has invalid value, provided subnet_addr "
234 << txt_subnet_addr << " is not a valid IPv4 address. "
235 << "Error: " << e.what());
236 }
237
238 std::string txt_prefix_len = txt_subnet_prefix.substr(pos + 1);
239 int16_t prefix_len = 0;
240 try {
241 // We should be able to lexically cast IPv4 prefix len to short int.
242 // After that len<=32 check is also required.
243 prefix_len = boost::lexical_cast<int16_t>(txt_prefix_len);
244 if (prefix_len > 32) {
245 isc_throw(BadValue, "Provided IPv4 prefix len is out of 0-32 range.");
246 }
247 } catch (const std::exception& e) {
248 isc_throw(BadValue, "DHCPv4 OptionClasslessStaticRoute "
249 << type_ << " has invalid value, provided prefix len "
250 << txt_prefix_len << " is not valid. "
251 << "Error: " << e.what());
252 }
253
254 IOAddress router_addr = IOAddress("::");
255 std::string txt_router = str::trim(parts[1]);
256 try {
257 router_addr = IOAddress(txt_router);
258 if (!router_addr.isV4()) {
259 isc_throw(BadValue, "This is not IPv4 address.");
260 }
261 } catch (const std::exception& e) {
262 isc_throw(BadValue, "DHCPv4 OptionClasslessStaticRoute "
263 << type_ << " has invalid value, provided router address "
264 << txt_router << " is not a valid IPv4 address. "
265 << "Error: " << e.what());
266 }
267
268 StaticRouteTuple route = std::make_tuple(subnet_addr, prefix_len, router_addr);
269 static_routes_.push_back(route);
270 }
271}
272
273} // namespace dhcp
274} // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
std::string toText(int indent=0) const override
Returns string representation of the option.
OptionPtr clone() const override
Copies this option and returns a pointer to the copy.
void pack(util::OutputBuffer &buf, bool check=true) const override
Writes option in wire-format to a buffer.
void unpack(OptionBufferConstIter begin, OptionBufferConstIter end) override
Parses option from the received buffer.
OptionClasslessStaticRoute(OptionBufferConstIter begin, OptionBufferConstIter end, bool convenient_notation=false)
Constructor of the Option from data in the buffer.
uint16_t len() const override
Returns length of the complete option (data length + DHCPv4 option header)
uint16_t type_
option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
Definition: option.h:590
virtual uint16_t getHeaderLen() const
Returns length of header (2 for v4, 4 for v6)
Definition: option.cc:321
void packHeader(isc::util::OutputBuffer &buf, bool check=true) const
Store option's header in a buffer.
Definition: option.cc:119
void check() const
A protected method used for option correctness.
Definition: option.cc:90
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 writeUint32(uint32_t data)
Write an unsigned 32-bit integer in host byte order into the buffer in network byte order.
Definition: buffer.h:528
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
std::tuple< asiolink::IOAddress, uint8_t, asiolink::IOAddress > StaticRouteTuple
Defines a tuple of Subnet number, Subnet mask width and IPv4 router address.
@ DHO_CLASSLESS_STATIC_ROUTE
Definition: dhcp4.h:186
OptionBuffer::const_iterator OptionBufferConstIter
const_iterator for walking over OptionBuffer
Definition: option.h:30
boost::shared_ptr< Option > OptionPtr
Definition: option.h:37
vector< string > tokens(const string &text, const string &delim, bool escape)
Split string into tokens.
Definition: str.cc:52
string trim(const string &input)
Trim leading and trailing spaces.
Definition: str.cc:32
uint16_t readUint16(void const *const buffer, size_t const length)
uint16_t wrapper over readUint.
Definition: io.h:76
uint32_t readUint32(void const *const buffer, size_t const length)
uint32_t wrapper over readUint.
Definition: io.h:82
Defines the logger used by the top-level component of kea-lfc.