OptionClasslessStaticRoute::OptionClasslessStaticRoute(OptionBufferConstIter begin,
OptionBufferConstIter end)
- : Option(V4, DHO_CLASSLESS_STATIC_ROUTE) {
+ : Option(V4, DHO_CLASSLESS_STATIC_ROUTE), static_routes_(), data_len_(0) {
unpack(begin, end);
}
void
OptionClasslessStaticRoute::pack(isc::util::OutputBuffer& buf, bool check) const {
- Option::pack(buf, check);
+ // Header = option code and length.
+ packHeader(buf, check);
+ for (const auto& route : static_routes_) {
+ // 1-5 octets of destination descriptor
+ auto dest = encodeDestinationDescriptor(route);
+ buf.writeData(&dest[0], dest.size());
+ // IP address of the router
+ buf.writeUint32(get<2>(route).toUint32());
+ }
}
void
OptionClasslessStaticRoute::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
- if (distance(begin, end) % (V4ADDRESS_LEN * 3)) {
+ // Static route definition requires n * 3 IPv4 addresses (n>0):
+ // Subnet number, subnet mask, router IP
+ if (!distance(begin, end) || distance(begin, end) % (V4ADDRESS_LEN * 3)) {
isc_throw(OutOfRange, "DHCPv4 OptionClasslessStaticRoute "
<< type_ << " has invalid length=" << distance(begin, end)
- << ", must be divisible by 12.");
+ << ", must be not 0 and divisible by 12.");
}
while (begin != end) {
+ // Subnet number IP address e.g. 10.229.0.128
const uint8_t* ptr = &(*begin);
auto subnet_nr = IOAddress(readUint32(ptr, distance(begin, end)));
begin += V4ADDRESS_LEN;
+ // Subnet mask e.g. 255.255.255.128
uint32_t subnet_mask = readUint32(ptr, distance(begin, end));
uint8_t mask_width = calcMaskWidth(subnet_mask);
begin += V4ADDRESS_LEN;
+ // Router IP address
auto router_addr = IOAddress(readUint32(ptr, distance(begin, end)));
begin += V4ADDRESS_LEN;
- StaticRouteTuple route = std::make_tuple(subnet_nr, PrefixLen(mask_width), router_addr);
- addRoute(route);
+ StaticRouteTuple route = std::make_tuple(subnet_nr, mask_width, router_addr);
+ static_routes_.push_back(route);
}
+
+ calcDataLen();
}
std::string
OptionClasslessStaticRoute::toText(int indent) const {
- return Option::toText(indent);
+ std::ostringstream stream;
+ std::string in(indent, ' '); // base indentation
+ stream << in << "type=" << type_ << "(CLASSLESS_STATIC_ROUTE), "
+ << "len=" << (len() - getHeaderLen());
+ int i = 0;
+ for (const auto& route : static_routes_) {
+ stream << ", Route " << ++i
+ << " (subnet " << get<0>(route).toText() << "/"
+ << get<1>(route) << ", router IP "
+ << get<2>(route) << ")";
+ }
+
+ return (stream.str());
}
uint16_t
OptionClasslessStaticRoute::len() const {
- return Option::len();
+ uint16_t len = OPTION4_HDR_LEN;
+ len += data_len_;
+ return (len);
}
uint8_t
}
void
-OptionClasslessStaticRoute::addRoute(StaticRouteTuple& route) {
+OptionClasslessStaticRoute::addRoute(const StaticRouteTuple& route) {
static_routes_.push_back(route);
+ calcDataLen();
}
std::vector<uint8_t>
-OptionClasslessStaticRoute::encodeDestinationDescriptor(StaticRouteTuple& route) {
- auto subnet = get<0>(route).toBytes();
- auto mask_width= get<1>(route).asUint8();
+OptionClasslessStaticRoute::encodeDestinationDescriptor(const StaticRouteTuple& route) {
+ // Encoding as per RFC3442
+ const std::vector<uint8_t>& subnet = get<0>(route).toBytes();
+ const uint8_t& mask_width = get<1>(route);
+
std::vector<uint8_t> res;
res.push_back(mask_width);
if (mask_width == 0) {
+ // there are no significant octets, destination descriptor is 0 value - one octet long
return (res);
}
- uint8_t significant_octets = mask_width / 8 + (mask_width % 8 != 0);
+ uint8_t significant_octets = calcSignificantOctets(mask_width);
res.insert(res.end(), subnet.begin(), subnet.begin() + significant_octets);
return (res);
}
+uint8_t
+OptionClasslessStaticRoute::calcSignificantOctets(const uint8_t& mask_width) {
+ return (mask_width / 8 + (mask_width % 8 != 0));
+}
+
+void
+OptionClasslessStaticRoute::calcDataLen() {
+ uint16_t len = 0;
+ for (const auto& route : static_routes_) {
+ // 1-5 octets of destination descriptor
+ len += calcSignificantOctets(get<1>(route)) + 1;
+ // IP address of the router
+ len += 4;
+ }
+
+ data_len_ = len;
+}
+
} // namespace dhcp
} // namespace isc
-
namespace dhcp {
/// @brief Defines a tuple of Subnet number, Subnet mask width and IPv4 router address.
-typedef std::tuple<asiolink::IOAddress, PrefixLen, asiolink::IOAddress> StaticRouteTuple;
+typedef std::tuple<asiolink::IOAddress, uint8_t, asiolink::IOAddress> StaticRouteTuple;
/// @brief Represents DHCPv4 Classless Static Route %Option (code 121).
class OptionClasslessStaticRoute : public Option {
public:
/// @brief Empty Constructor
- OptionClasslessStaticRoute() : Option(V4, DHO_CLASSLESS_STATIC_ROUTE) {}
+ OptionClasslessStaticRoute() : Option(V4, DHO_CLASSLESS_STATIC_ROUTE), static_routes_(), data_len_(0) {}
/// @brief Constructor of the %Option from on-wire data.
///
/// @return string with text representation.
std::string toText(int indent = 0) const override;
- /// @brief Returns length of the complete option (data length + DHCPv4/DHCPv6
+ /// @brief Returns length of the complete option (data length + DHCPv4
/// option header)
///
/// @return length of the option
/// @brief Adds static route to collection of all static routes.
/// @param route A tuple defining new static route
- void addRoute(StaticRouteTuple& route);
+ void addRoute(const StaticRouteTuple& route);
private:
/// @brief Container holding all static routes.
std::vector<StaticRouteTuple> static_routes_;
+ /// @brief Length in octets of all encoded static routes.
+ uint16_t data_len_;
+
/// @brief Calculates subnet mask width from given uint_32 representation of subnet mask.
/// @param subnet_mask uint_32 representation of a subnet mask IPv4 address
/// @return width of subnet mask in a range of 0-32
/// @param route static route tuple
/// @return Contents of the destination descriptor as a vector
/// of bytes in network-byte order.
- static std::vector<uint8_t> encodeDestinationDescriptor(StaticRouteTuple& route) ;
+ static std::vector<uint8_t> encodeDestinationDescriptor(const StaticRouteTuple& route);
+
+ /// @brief Calculates number of significant octets of the subnet as per RFC3442.
+ ///
+ /// The significant portion of the subnet number is simply all of the
+ /// octets of the subnet number where the corresponding octet in the
+ /// subnet mask is non-zero. The number of significant octets is the
+ /// width of the subnet mask divided by eight, rounding up.
+ ///
+ /// @param mask_width width of subnet mask
+ /// @return number of significant octets
+ static uint8_t calcSignificantOctets(const uint8_t& mask_width);
+
+ /// @brief Calculates length in octets of all encoded static routes and stores it in @c data_len_
+ ///
+ /// Calculation is done according to static routes encoding rules in RFC3442.
+ /// This should be called whenever @c static_routes_ is changed.
+ void calcDataLen();
};
/// A pointer to the @c OptionClasslessStaticRoute object.