From: Piotrek Zadroga Date: Tue, 10 Oct 2023 11:36:58 +0000 (+0200) Subject: [#3074] WIP new class for v4 option 121 X-Git-Tag: Kea-2.5.5~68 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cc72f1eacb642f027296cc5b8201ccc49af7c9ac;p=thirdparty%2Fkea.git [#3074] WIP new class for v4 option 121 --- diff --git a/src/lib/dhcp/option_classless_static_route.cc b/src/lib/dhcp/option_classless_static_route.cc index 6d0110f235..579b09b09e 100644 --- a/src/lib/dhcp/option_classless_static_route.cc +++ b/src/lib/dhcp/option_classless_static_route.cc @@ -16,7 +16,7 @@ namespace dhcp { 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); } @@ -27,42 +27,71 @@ OptionClasslessStaticRoute::clone() const { 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 @@ -82,26 +111,47 @@ OptionClasslessStaticRoute::calcMaskWidth(uint32_t subnet_mask) { } void -OptionClasslessStaticRoute::addRoute(StaticRouteTuple& route) { +OptionClasslessStaticRoute::addRoute(const StaticRouteTuple& route) { static_routes_.push_back(route); + calcDataLen(); } std::vector -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& subnet = get<0>(route).toBytes(); + const uint8_t& mask_width = get<1>(route); + std::vector 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 - diff --git a/src/lib/dhcp/option_classless_static_route.h b/src/lib/dhcp/option_classless_static_route.h index 7fe44efa76..6bd1da0300 100644 --- a/src/lib/dhcp/option_classless_static_route.h +++ b/src/lib/dhcp/option_classless_static_route.h @@ -15,13 +15,13 @@ namespace isc { namespace dhcp { /// @brief Defines a tuple of Subnet number, Subnet mask width and IPv4 router address. -typedef std::tuple StaticRouteTuple; +typedef std::tuple 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. /// @@ -65,7 +65,7 @@ public: /// @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 @@ -73,12 +73,15 @@ public: /// @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 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 @@ -88,7 +91,24 @@ private: /// @param route static route tuple /// @return Contents of the destination descriptor as a vector /// of bytes in network-byte order. - static std::vector encodeDestinationDescriptor(StaticRouteTuple& route) ; + static std::vector 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.