]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
osd: Check range_blocklist in is_blocklisted(): we actually blocklist ranges
authorGreg Farnum <gfarnum@redhat.com>
Tue, 30 Nov 2021 18:29:46 +0000 (18:29 +0000)
committerGreg Farnum <gfarnum@redhat.com>
Tue, 31 May 2022 23:30:20 +0000 (23:30 +0000)
Carry a parallel map from cidr addresses to a new
range_bits class (stored entirely as ephemeral state) so that we
don't need to re-compute masks and bit mappings too often, and to
separate out the unpleasant ipv6 bit mapping logic. Then check
against those with range_bits::matches() the same way we check
for equality on specific-entity matches. Nice and simple loops!

Fixes: https://tracker.ceph.com/issues/53050
Signed-off-by: Greg Farnum <gfarnum@redhat.com>
(cherry picked from commit 3e26209cbc61cb7fbd4e3f310a28c4cd0f6bb287)

src/osd/OSDMap.cc
src/osd/OSDMap.h

index 1c584cb1da5c95eebe44e109389b8d2d763d8ab1..1aca8c389f37cd37efc69f38b734ccaa5f1d111c 100644 (file)
@@ -1358,9 +1358,72 @@ void OSDMap::set_epoch(epoch_t e)
     pool.second.last_change = e;
 }
 
-bool OSDMap::is_blocklisted(const entity_addr_t& orig) const
+OSDMap::range_bits::range_bits() : ipv6(false) {
+  memset(&bits, 0, sizeof(bits));
+}
+
+OSDMap::range_bits::range_bits(const entity_addr_t& addr) : ipv6(false) {
+  memset(&bits, 0, sizeof(bits));
+  parse(addr);
+}
+
+void OSDMap::range_bits::get_ipv6_bytes(unsigned const char *addr,
+                                       uint64_t *upper, uint64_t *lower)
+{
+  *upper = ((uint64_t)(ntohl(*(uint32_t*)(addr)))) << 32 |
+    ((uint64_t)(ntohl(*(uint32_t*)(&addr[4]))));
+  *lower = ((uint64_t)(ntohl(*(uint32_t*)(&addr[8])))) << 32 |
+    ((uint64_t)(ntohl(*(uint32_t*)(&addr[12]))));
+}
+
+void OSDMap::range_bits::parse(const entity_addr_t& addr) {
+  // parse it into meaningful data
+  if (addr.is_ipv6()) {
+    get_ipv6_bytes(addr.in6_addr().sin6_addr.s6_addr,
+                  &bits.ipv6.upper_64_bits, &bits.ipv6.lower_64_bits);
+    int32_t lower_shift = std::min(128-
+                                  static_cast<int32_t>(addr.get_nonce()), 64);
+    int32_t upper_shift = std::max(64- //(128-b.first.get_nonce())-64
+                                  static_cast<int32_t>(addr.get_nonce()), 0); 
+
+    auto get_mask = [](int32_t shift) -> uint64_t {
+      if (shift >= 0 && shift < 64) {
+       return UINT64_MAX << shift;
+      }
+      return 0;
+    };
+
+    bits.ipv6.lower_mask = get_mask(lower_shift);
+    bits.ipv6.upper_mask = get_mask(upper_shift);
+    ipv6 = true;
+  } else if (addr.is_ipv4()) {
+    bits.ipv4.ip_32_bits = ntohl(addr.in4_addr().sin_addr.s_addr);
+    bits.ipv4.mask = UINT32_MAX << (32-addr.get_nonce());
+  } else {
+    // uh...
+  }
+}
+
+bool OSDMap::range_bits::matches(const entity_addr_t& addr) const {
+  if (addr.is_ipv4() && !ipv6) {
+    return ((ntohl(addr.in4_addr().sin_addr.s_addr) & bits.ipv4.mask) ==
+           (bits.ipv4.ip_32_bits & bits.ipv4.mask));
+  } else if (addr.is_ipv6() && ipv6) {
+    uint64_t upper_64, lower_64;
+    get_ipv6_bytes(addr.in6_addr().sin6_addr.s6_addr, &upper_64, &lower_64);
+    return (((upper_64 & bits.ipv6.upper_mask) ==
+            (bits.ipv6.upper_64_bits & bits.ipv6.upper_mask)) &&
+           ((lower_64 & bits.ipv6.lower_mask) ==
+            (bits.ipv6.lower_64_bits & bits.ipv6.lower_mask)));
+  }
+  return false;
+}
+
+bool OSDMap::is_blocklisted(const entity_addr_t& orig, CephContext *cct) const
 {
-  if (blocklist.empty()) {
+  if (cct) ldout(cct, 25) << "is_blocklisted: " << orig << dendl;
+  if (blocklist.empty() && range_blocklist.empty()) {
+    if (cct) ldout(cct, 30) << "not blocklisted: " << orig << dendl;
     return false;
   }
 
@@ -1375,6 +1438,7 @@ bool OSDMap::is_blocklisted(const entity_addr_t& orig) const
 
   // this specific instance?
   if (blocklist.count(a)) {
+    if (cct) ldout(cct, 20) << "blocklist contains " << a << dendl;
     return true;
   }
 
@@ -1383,20 +1447,31 @@ bool OSDMap::is_blocklisted(const entity_addr_t& orig) const
     a.set_port(0);
     a.set_nonce(0);
     if (blocklist.count(a)) {
+      if (cct) ldout(cct, 20) << "blocklist contains " << a << dendl;
       return true;
     }
   }
 
+  // is it in a blocklisted range?
+  for (const auto& i : calculated_ranges) {
+    bool blocked = i.second.matches(a);
+    if (blocked) {
+      if (cct) ldout(cct, 20) << "range_blocklist contains " << a << dendl;
+      return true;
+    }
+  }
+
+  if (cct) ldout(cct, 25) << "not blocklisted: " << orig << dendl;
   return false;
 }
 
-bool OSDMap::is_blocklisted(const entity_addrvec_t& av) const
+bool OSDMap::is_blocklisted(const entity_addrvec_t& av, CephContext *cct) const
 {
-  if (blocklist.empty())
+  if (blocklist.empty() && range_blocklist.empty())
     return false;
 
   for (auto& a : av.v) {
-    if (is_blocklisted(a)) {
+    if (is_blocklisted(a, cct)) {
       return true;
     }
   }
@@ -2318,13 +2393,15 @@ int OSDMap::apply_incremental(const Incremental &inc)
   for (const auto &addr : inc.old_blocklist)
     blocklist.erase(addr);
 
-  if (!inc.new_range_blocklist.empty()) {
-    range_blocklist.insert(inc.new_range_blocklist.begin(),
-                          inc.new_range_blocklist.end());
+  for (const auto& addr_p : inc.new_range_blocklist) {
+    range_blocklist.insert(addr_p);
+    calculated_ranges.emplace(addr_p.first, addr_p.first);
     new_blocklist_entries = true;
   }
-  for (const auto &addr : inc.old_range_blocklist)
+  for (const auto &addr : inc.old_range_blocklist) {
+    calculated_ranges.erase(addr);
     range_blocklist.erase(addr);
+  }
 
   for (auto& i : inc.new_crush_node_flags) {
     if (i.second) {
@@ -3489,6 +3566,10 @@ void OSDMap::decode(ceph::buffer::list::const_iterator& bl)
     }
     if (struct_v >= 11) {
       decode(range_blocklist, bl);
+      calculated_ranges.clear();
+      for (const auto& i : range_blocklist) {
+       calculated_ranges.emplace(i.first, i.first);
+      }
     }
     DECODE_FINISH(bl); // osd-only data
   }
index e84bdede6436256d2c745c4d2a3a9b3bde0a4e92..83ab75e0db427ddeb651ae9ba47fe3a2478300c9 100644 (file)
@@ -584,8 +584,31 @@ private:
   std::shared_ptr< mempool::osdmap::vector<uuid_d> > osd_uuid;
   mempool::osdmap::vector<osd_xinfo_t> osd_xinfo;
 
+  class range_bits {
+    struct ip6 {
+      uint64_t upper_64_bits, lower_64_bits;
+      uint64_t upper_mask, lower_mask;
+    };
+    struct ip4 {
+      uint32_t ip_32_bits;
+      uint32_t mask;
+    };
+    union {
+      ip6 ipv6;
+      ip4 ipv4;
+    } bits;
+    bool ipv6;
+    static void get_ipv6_bytes(unsigned const char *addr,
+                       uint64_t *upper, uint64_t *lower);
+  public:
+    range_bits();
+    range_bits(const entity_addr_t& addr);
+    void parse(const entity_addr_t& addr);
+    bool matches(const entity_addr_t& addr) const;
+  };
   mempool::osdmap::unordered_map<entity_addr_t,utime_t> blocklist;
   mempool::osdmap::map<entity_addr_t,utime_t> range_blocklist;
+  mempool::osdmap::map<entity_addr_t,range_bits> calculated_ranges;
 
   /// queue of snaps to remove
   mempool::osdmap::map<int64_t, snap_interval_set_t> removed_snaps_queue;
@@ -696,8 +719,8 @@ public:
   const utime_t& get_created() const { return created; }
   const utime_t& get_modified() const { return modified; }
 
-  bool is_blocklisted(const entity_addr_t& a) const;
-  bool is_blocklisted(const entity_addrvec_t& a) const;
+  bool is_blocklisted(const entity_addr_t& a, CephContext *cct=nullptr) const;
+  bool is_blocklisted(const entity_addrvec_t& a, CephContext *cct=nullptr) const;
   void get_blocklist(std::list<std::pair<entity_addr_t,utime_t > > *bl,
                     std::list<std::pair<entity_addr_t,utime_t> > *rl) const;
   void get_blocklist(std::set<entity_addr_t> *bl,