| /** @file |
| |
| Include file for all the IP reputation classes. |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| #pragma once |
| |
| #include <string> |
| #include <cstdint> |
| #include <tuple> |
| #include <unordered_map> |
| #include <list> |
| #include <vector> |
| #include <chrono> |
| #include <arpa/inet.h> |
| |
| #include <yaml-cpp/yaml.h> |
| #include "ts/ts.h" |
| #include "utilities.h" |
| |
| namespace IpReputation |
| { |
| using KeyClass = uint64_t; |
| using SystemClock = std::chrono::system_clock; |
| |
| // Key / Count / bucket # (rank, 0-<n>) / time added |
| using LruEntry = std::tuple<KeyClass, uint32_t, uint32_t, std::chrono::time_point<SystemClock>>; |
| |
| // This is a wrapper around a std::list which lets us size limit the list to a |
| // certain size. |
| class SieveBucket : public std::list<LruEntry> |
| { |
| using self_type = SieveBucket; |
| |
| public: |
| SieveBucket(uint32_t max_size) : _max_size(max_size) {} |
| |
| SieveBucket() = delete; |
| SieveBucket(self_type &&) = delete; |
| self_type &operator=(const self_type &) = delete; |
| self_type &operator=(self_type &&) = delete; |
| |
| bool |
| full() const |
| { |
| return (_max_size > 0 ? (size() >= _max_size) : false); |
| } |
| |
| size_t |
| max_size() const |
| { |
| return _max_size; |
| } |
| |
| // Move an element to the top of an LRU. This *can* move it from the current LRU (bucket) |
| // to another, when promoted to a higher rank. |
| void |
| moveTop(SieveBucket *source_lru, SieveBucket::iterator &item) |
| { |
| splice(begin(), *source_lru, item); |
| } |
| |
| // Debugging tools |
| size_t memorySize() const; |
| |
| private: |
| uint32_t _max_size; |
| }; |
| |
| using HashMap = std::unordered_map<KeyClass, SieveBucket::iterator>; // The hash map for finding the entry |
| |
| // This is a concept / POC: Ranked LRU buckets |
| // |
| // Also, obviously the std::string here is not awesome, rather, we ought to save the |
| // hashed value from the IP as the key (just like the hashed in cache_promote). |
| class SieveLru |
| { |
| using self_type = SieveLru; |
| |
| public: |
| SieveLru(std::string &name) : _lock(TSMutexCreate()) { _name = name; } |
| |
| SieveLru() = delete; |
| SieveLru(self_type &&) = delete; |
| self_type &operator=(const self_type &) = delete; |
| self_type &operator=(self_type &&) = delete; |
| |
| ~SieveLru() |
| { |
| for (auto &bucket : _buckets) { |
| delete bucket; |
| } |
| } |
| |
| bool parseYaml(const YAML::Node &node); |
| |
| // Return value is the bucket (0 .. num_buckets) that the IP is in, and the |
| // current count of "hits". The lookup version is similar, except it doesn't |
| // modify the status of the IP (read-only). |
| std::tuple<uint32_t, uint32_t> increment(KeyClass key); |
| |
| std::tuple<uint32_t, uint32_t> |
| increment(const sockaddr *sock) |
| { |
| return increment(hasher(sock)); |
| } |
| |
| // Move an IP to the perm-block or perma-allow LRUs. A zero (default) maxage is indefinite (no timeout). |
| uint32_t |
| block(KeyClass key) |
| { |
| return move_bucket(key, blockBucket()); |
| } |
| |
| uint32_t |
| block(const sockaddr *sock) |
| { |
| return move_bucket(hasher(sock), blockBucket()); |
| } |
| |
| // Lookup the current state of an IP |
| std::tuple<uint32_t, uint32_t> lookup(KeyClass key) const; |
| |
| std::tuple<uint32_t, uint32_t> |
| lookup(const sockaddr *sock) const |
| { |
| return lookup(hasher(sock)); |
| } |
| |
| // A helper function to hash an INET or INET6 sockaddr to a 64-bit hash. |
| static uint64_t hasher(const sockaddr *sock); |
| static uint64_t hasher(const std::string &ip, u_short family = AF_INET); |
| |
| // Identifying some of the special buckets: |
| // |
| // entryBucket == the highest bucket, where new IPs enter (also the biggest bucket) |
| // lastBucket == the last bucket, which is most likely to be abusive |
| // blockBucket == the bucket where we "permanently" block bad IPs |
| uint32_t |
| entryBucket() const |
| { |
| return _num_buckets; |
| } |
| |
| constexpr uint32_t |
| lastBucket() const |
| { |
| return 1; |
| } |
| |
| constexpr uint32_t |
| blockBucket() const |
| { |
| return 0; |
| } |
| |
| size_t |
| bucketSize(uint32_t bucket) const |
| { |
| if (bucket <= (_num_buckets + 1)) { |
| return _buckets[bucket]->size(); |
| } else { |
| return 0; |
| } |
| } |
| |
| bool |
| initialized() const |
| { |
| return _initialized; |
| } |
| |
| const std::string & |
| name() const |
| { |
| return _name; |
| } |
| |
| uint32_t |
| numBuckets() const |
| { |
| return _num_buckets; |
| } |
| |
| uint32_t |
| size() const |
| { |
| return _size; |
| } |
| |
| uint32_t |
| percentage() const |
| { |
| return _percentage; |
| } |
| |
| uint32_t |
| permablock_count() const |
| { |
| return _permablock_limit; |
| } |
| |
| uint32_t |
| permablock_threshold() const |
| { |
| return _permablock_threshold; |
| } |
| |
| std::chrono::seconds |
| maxAge() const |
| { |
| return _max_age; |
| } |
| |
| std::chrono::seconds |
| permaMaxAge() const |
| { |
| return _permablock_max_age; |
| } |
| |
| // Debugging tool, dumps some info around the buckets |
| void dump(); |
| size_t memoryUsed() const; |
| |
| protected: |
| int32_t move_bucket(KeyClass key, uint32_t to_bucket); |
| |
| private: |
| HashMap _map; |
| std::vector<SieveBucket *> _buckets; |
| std::string _name; |
| bool _initialized = false; // If this has been properly initialized yet |
| TSMutex _lock; // The lock around all data access |
| // Standard options |
| uint32_t _num_buckets = 10; // Leave this at 10 ... |
| uint32_t _size = 0; // Set this up to initialize |
| uint32_t _percentage = 90; // At what percentage of limit do we start blocking |
| std::chrono::seconds _max_age = std::chrono::seconds::zero(); // Aging time in the SieveLru (default off) |
| // Perma-block options |
| uint32_t _permablock_limit = 0; // "Hits" limit for blocking permanently |
| uint32_t _permablock_threshold = 0; // Pressure threshold for permanent block |
| std::chrono::seconds _permablock_max_age = std::chrono::seconds::zero(); // Aging time in the SieveLru for perma-blocks |
| }; |
| |
| } // namespace IpReputation |