]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3074] WIP new class for v4 option 121
authorPiotrek Zadroga <piotrek@isc.org>
Tue, 10 Oct 2023 11:36:58 +0000 (13:36 +0200)
committerPiotrek Zadroga <piotrek@isc.org>
Tue, 9 Jan 2024 10:38:08 +0000 (11:38 +0100)
src/lib/dhcp/option_classless_static_route.cc
src/lib/dhcp/option_classless_static_route.h

index 6d0110f2350b2d52b91caebbd43d65d0b161f041..579b09b09e847c26595bda0769ddaaa09c4206d1 100644 (file)
@@ -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<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
-
index 7fe44efa765645abdbb1ccc1d36167322401f56b..6bd1da03002ca535dfbef9f7e768d2a87d313c85 100644 (file)
@@ -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<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.
     ///
@@ -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<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
@@ -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<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.