Kea: option_classless_static_route.cc Source File

| Kea 3.0.0

option_classless_static_route.cc
Go to the documentation of this file.
1// Copyright (C) 2023-2025 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
13#include <option_classless_static_route.h>
14
15using namespace isc::asiolink;
16using namespace isc::util;
17
18namespace isc {
19namespace dhcp {
20
21OptionClasslessStaticRoute::OptionClasslessStaticRoute(OptionBufferConstIter begin,
22 OptionBufferConstIter end,
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
29 OptionPtr
30OptionClasslessStaticRoute::clone() const {
31 return (cloneInternal<OptionClasslessStaticRoute>());
32}
33
34void
35OptionClasslessStaticRoute::pack(isc::util::OutputBuffer& buf, bool check) const {
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
48OptionClasslessStaticRoute::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
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
74OptionClasslessStaticRoute::toText(int indent) const {
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
90OptionClasslessStaticRoute::len() const {
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 (static_cast<size_t>(distance(begin, end)) <
156 (significant_octets + V4ADDRESS_LEN)) {
157 isc_throw(OutOfRange,
158 "DHCPv4 OptionClasslessStaticRoute " << type_ << " is truncated.");
159 }
160
161 // following octets are significant octets of the subnet nr
162 uint32_t subnet_octets;
163 IOAddress subnet_nr = asiolink::IOAddress::IPV4_ZERO_ADDRESS();
164
165 switch (significant_octets) {
166 case 0:
167 // no-op - this is 0.0.0.0/0 subnet
168 break;
169 case 1:
170 subnet_octets = *begin;
171 subnet_nr = IOAddress(subnet_octets << 24);
172 break;
173 case 2:
174 subnet_octets = readUint16(&(*begin), distance(begin, end));
175 subnet_nr = IOAddress(subnet_octets << 16);
176 break;
177 case 3:
178 // we are reading one octet too much in this case,
179 // but since we did check for truncated data before,
180 // we are safe do so and mask 4th octet with zeros
181 subnet_octets = readUint32(&(*begin), distance(begin, end));
182 subnet_nr = IOAddress(subnet_octets & 0xFFFFFF00);
183 break;
184 case 4:
185 subnet_octets = readUint32(&(*begin), distance(begin, end));
186 subnet_nr = IOAddress(subnet_octets);
187 break;
188 }
189
190 begin += significant_octets;
191
192 // last comes router IPv4 address
193 IOAddress router_addr = IOAddress(readUint32(&(*begin), distance(begin, end)));
194 begin += V4ADDRESS_LEN;
195
196 StaticRouteTuple route = std::make_tuple(subnet_nr, mask_width, router_addr);
197 static_routes_.push_back(route);
198 }
199}
200
201void
202OptionClasslessStaticRoute::parseConfigData(const std::string& config_txt) {
203 // this option allows more than one static route, so let's separate them using comma
204 std::vector<std::string> tokens = str::tokens(config_txt, std::string(","));
205 for (auto const& route_str : tokens) {
206 std::vector<std::string> parts = str::tokens(str::trim(route_str), std::string("-"));
207 if (parts.size() != 2) {
208 isc_throw(BadValue, "DHCPv4 OptionClasslessStaticRoute "
209 << type_
210 << " has invalid value, option definition must"
211 " have comma separated routes formatted as in "
212 "example: 10.229.0.128/25 - 10.229.0.1");
213 }
214
215 std::string txt_subnet_prefix = str::trim(parts[0]);
216
217 // Is this prefix/len notation?
218 size_t pos = txt_subnet_prefix.find('/');
219 if (pos == std::string::npos) {
220 isc_throw(BadValue, "DHCPv4 OptionClasslessStaticRoute "
221 << type_ << " has invalid value, provided IPv4 prefix "
222 << txt_subnet_prefix << " is not valid.");
223 }
224
225 std::string txt_subnet_addr = txt_subnet_prefix.substr(0, pos);
226 IOAddress subnet_addr = IOAddress("::");
227 try {
228 subnet_addr = IOAddress(txt_subnet_addr);
229 if (!subnet_addr.isV4()) {
230 isc_throw(BadValue, "This is not IPv4 address.");
231 }
232 } catch (const std::exception& e) {
233 isc_throw(BadValue, "DHCPv4 OptionClasslessStaticRoute "
234 << type_ << " has invalid value, provided subnet_addr "
235 << txt_subnet_addr << " is not a valid IPv4 address. "
236 << "Error: " << e.what());
237 }
238
239 std::string txt_prefix_len = txt_subnet_prefix.substr(pos + 1);
240 int16_t prefix_len = 0;
241 try {
242 // We should be able to lexically cast IPv4 prefix len to short int.
243 // After that len<=32 check is also required.
244 prefix_len = boost::lexical_cast<int16_t>(txt_prefix_len);
245 if (prefix_len > 32) {
246 isc_throw(BadValue, "Provided IPv4 prefix len is out of 0-32 range.");
247 }
248 } catch (const std::exception& e) {
249 isc_throw(BadValue, "DHCPv4 OptionClasslessStaticRoute "
250 << type_ << " has invalid value, provided prefix len "
251 << txt_prefix_len << " is not valid. "
252 << "Error: " << e.what());
253 }
254
255 IOAddress router_addr = IOAddress("::");
256 std::string txt_router = str::trim(parts[1]);
257 try {
258 router_addr = IOAddress(txt_router);
259 if (!router_addr.isV4()) {
260 isc_throw(BadValue, "This is not IPv4 address.");
261 }
262 } catch (const std::exception& e) {
263 isc_throw(BadValue, "DHCPv4 OptionClasslessStaticRoute "
264 << type_ << " has invalid value, provided router address "
265 << txt_router << " is not a valid IPv4 address. "
266 << "Error: " << e.what());
267 }
268
269 StaticRouteTuple route = std::make_tuple(subnet_addr, prefix_len, router_addr);
270 static_routes_.push_back(route);
271 }
272}
273
274} // namespace dhcp
275} // namespace isc
isc::OutOfRange
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
Definition exceptions/exceptions.h:115
isc::asiolink::IOAddress::IPV4_ZERO_ADDRESS
static const IOAddress & IPV4_ZERO_ADDRESS()
Returns an address set to all zeros.
Definition io_address.h:277
isc::asiolink::IOAddress::isV4
bool isV4() const
Convenience function to check for an IPv4 address.
Definition io_address.h:114
isc::dhcp::OptionClasslessStaticRoute::toText
std::string toText(int indent=0) const override
Returns string representation of the option.
Definition option_classless_static_route.cc:74
isc::dhcp::OptionClasslessStaticRoute::clone
OptionPtr clone() const override
Copies this option and returns a pointer to the copy.
Definition option_classless_static_route.cc:30
isc::dhcp::OptionClasslessStaticRoute::pack
void pack(util::OutputBuffer &buf, bool check=true) const override
Writes option in wire-format to a buffer.
Definition option_classless_static_route.cc:35
isc::dhcp::OptionClasslessStaticRoute::unpack
void unpack(OptionBufferConstIter begin, OptionBufferConstIter end) override
Parses option from the received buffer.
Definition option_classless_static_route.cc:48
isc::dhcp::OptionClasslessStaticRoute::OptionClasslessStaticRoute
OptionClasslessStaticRoute(OptionBufferConstIter begin, OptionBufferConstIter end, bool convenient_notation=false)
Constructor of the Option from data in the buffer.
Definition option_classless_static_route.cc:21
isc::dhcp::OptionClasslessStaticRoute::len
uint16_t len() const override
Returns length of the complete option (data length + DHCPv4 option header)
Definition option_classless_static_route.cc:90
isc::dhcp::Option::type_
uint16_t type_
option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
Definition option.h:597
isc::dhcp::Option::getHeaderLen
virtual uint16_t getHeaderLen() const
Returns length of header (2 for v4, 4 for v6)
Definition option.cc:327
isc::dhcp::Option::V4
@ V4
Definition option.h:90
isc::dhcp::Option::cloneInternal
OptionPtr cloneInternal() const
Copies this option and returns a pointer to the copy.
Definition option.h:504
isc::dhcp::Option::packHeader
void packHeader(isc::util::OutputBuffer &buf, bool check=true) const
Store option's header in a buffer.
Definition option.cc:119
isc::dhcp::Option::Option
Option(Universe u, uint16_t type)
ctor, used for options constructed, usually during transmission
Definition option.cc:39
isc::dhcp::Option::check
void check() const
A protected method used for option correctness.
Definition option.cc:90
isc::util::OutputBuffer
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition buffer.h:346
isc::util::OutputBuffer::writeData
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition buffer.h:559
isc::util::OutputBuffer::writeUint32
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:531
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition exceptions/exceptions.h:210
io.h
io_error.h
isc::asiolink
Definition addr_utilities.cc:170
isc::dhcp
Definition dhcp4/client_handler.cc:20
isc::dhcp::StaticRouteTuple
std::tuple< asiolink::IOAddress, uint8_t, asiolink::IOAddress > StaticRouteTuple
Defines a tuple of Subnet number, Subnet mask width and IPv4 router address.
Definition option_classless_static_route.h:18
isc::dhcp::DHO_CLASSLESS_STATIC_ROUTE
@ DHO_CLASSLESS_STATIC_ROUTE
Definition dhcp4.h:185
isc::dhcp::OptionBufferConstIter
OptionBuffer::const_iterator OptionBufferConstIter
const_iterator for walking over OptionBuffer
Definition option.h:30
isc::dhcp::OptionPtr
boost::shared_ptr< Option > OptionPtr
Definition option.h:37
isc::util::str::tokens
vector< string > tokens(const string &text, const string &delim, bool escape)
Split string into tokens.
Definition str.cc:52
isc::util::str::trim
string trim(const string &input)
Trim leading and trailing spaces.
Definition str.cc:32
isc::util
Definition time_utils.cc:43
isc::util::readUint16
uint16_t readUint16(void const *const buffer, size_t const length)
uint16_t wrapper over readUint.
Definition io.h:76
isc::util::readUint32
uint32_t readUint32(void const *const buffer, size_t const length)
uint32_t wrapper over readUint.
Definition io.h:82
isc
Defines the logger used by the top-level component of kea-lfc.
Definition agent_parser.cc:148
option_classless_static_route.h
str.h