From: Greg Farnum Date: Tue, 30 Nov 2021 18:29:46 +0000 (+0000) Subject: osd: Check range_blocklist in is_blocklisted(): we actually blocklist ranges X-Git-Tag: v16.2.11~103^2~103^2~13 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=7a8e5608f0d009bc3d215e79a2c30fb02b65d00b;p=ceph.git osd: Check range_blocklist in is_blocklisted(): we actually blocklist ranges 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 (cherry picked from commit 3e26209cbc61cb7fbd4e3f310a28c4cd0f6bb287) --- diff --git a/src/osd/OSDMap.cc b/src/osd/OSDMap.cc index 1c584cb1da5c9..1aca8c389f37c 100644 --- a/src/osd/OSDMap.cc +++ b/src/osd/OSDMap.cc @@ -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(addr.get_nonce()), 64); + int32_t upper_shift = std::max(64- //(128-b.first.get_nonce())-64 + static_cast(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 } diff --git a/src/osd/OSDMap.h b/src/osd/OSDMap.h index e84bdede64362..83ab75e0db427 100644 --- a/src/osd/OSDMap.h +++ b/src/osd/OSDMap.h @@ -584,8 +584,31 @@ private: std::shared_ptr< mempool::osdmap::vector > osd_uuid; mempool::osdmap::vector 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 blocklist; mempool::osdmap::map range_blocklist; + mempool::osdmap::map calculated_ranges; /// queue of snaps to remove mempool::osdmap::map 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 > *bl, std::list > *rl) const; void get_blocklist(std::set *bl,