blob: e4db6d0b3cdaeb6773fead225e2793866233f611 [file] [log] [blame]
/** @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