I want a process that can centralize the access to the database and my
pf tables.  Authorized clients will be allowed to make changes via
encrypted TCP connections.

libDwmPf has what I need to manipulate pf address tables.

Currently, my database only handles a single table.  That's likely OK,
but it means I need a separate McBlock::Db instance per table.

Adding entries
--------------
If the entry matches an existing active entry:
  - do nothing?
  - or should I bump the expiration time to make sure it's at least
    a week away?
If the entry matches an existing inactive entry:
  - update the interval start time of the entry to now
  - if the entry's time interval was less than a year, double its
    time interval in database
  - activate the entry in the database
  - save the database
  - add the entry to pf table
If no entry exists:
  - if it's a prefix, just add it?
  - if it's a host:
    - get country from dwmrdapd
    - apply rules
    - add according to rules

API inputs: table name, prefix, what else?

Deleting entries
----------------
Only delete exact matches.

Periodically purge entries in pf which have expired in the database.

API inputs: table name, prefix

Handling log data
-----------------
I want to centralize the handling of auth.log, web log and mail log data.
A host should be able to run a simple parser that groks a log and just
sends violation IP addresses to mcblockd.  mcblockd _should_ be able to
apply all of the necessary policy, including threshold and AddRules.
On the mcblockd side, this would mean keeping log entries for some period
of time, but it's at worst a vector of timestamps per IP (or perhaps per
prefix).  I don't really expcet to keep entries for more than a week,
maybe a month?  Note that something used as a pipe reader via syslog.conf
is going to be restarted regularly due to SIGHUP to syslogd (thanks to
newsyslog and other reasons).  Hence it's likely smarter to keep long-term
state in mcblockd anyway.  Plus it allows me to sum log hits from more
than one host.

Note that to apply policy, I need the country of the offending IP since
my policy is defined by country.  So at the top level I need to be keyed
by pf table name, then below that I can either key by prefix and have
the data hold the country name, or combine the prefix and country in
the key.

When a log parsing client sends a violating IP address, I could look up
the prefix in RDAP, then enqueue a timestamp in a
map<Ipv4Prefix,deque<TimeValue64>>.  While enqueueing, I should expire
old entries (according the threshold settings).  Then check if the
threshold was exceeded and add a pf table entry if needed.

So, a log hit arrives...

  - if it's already covered in the pf table, do nothing
  - if it's not covered, do the dwmrdapd lookup and then the AddRules lookup.
    If the AddRules lookup says the threshold is 1 hit in one day, just
    add the entry to the table and to the database after applying the add
    rule.
    Else If the AddRules lookup says we need more than one hit:
      - add the hit to a LogTrackerEntry in a map keyed by the IP prefix
        found from dwmrdapd
      - if the LogTrackerEntry is over threshold:
          - add the entry to the pf table and the database after applying
	    the add rule, and remove the entry from the map of
	    LogTrackerEntry objects.
      - else:
         - clear old entries from the LogTrackerEntry.
	 - If it's then empty, remove it from the map of LogTrackerEntry
	   objects.

  - periodically purge entries from the LogTrackerEntry map by clearing
    old log hits and then deleting LogTrackerEntry objects that are empty.

Container-wise...

  At the top level:
    map<string,PerTableContainer>

  At the per-table level:
    map<Ipv4Prefix,LogEntryTracker>

  At the per-prefix level...
    I just need a deque<TimeValue64>, and a means to pass in a rule?  No,
    I should also store the country so I can avoid redundant dwmrdapd
    lookups.  So...

    class LogEntryTracker
    {
    public:
      LogEntryTracker() = default;
      LogEntryTracker(const std::string & country);
      void Add(const TimeValue64 & logTime);
      const std::string & Country() const;
      bool HitThreshold(const AddRulesForTable & addRules) const;
      void ClearOld(const AddRulesForTable & addRules);
      
      ...
    private:
      std::string              _country;
      std::deque<TimeValue64>  _logTimes;
    };

    class TableLogEntryTracker
    {
    public:
      TableLogEntryTracker() = default;
      Add(const Ipv4Prefix & prefix, const std::string & country,
          const TimeValue64 & logTime);
      bool HitThreshold(const Ipv4Prefix & prefix,
                        const AddRulesForTable & addRules) const;
      void ClearOld(const AddRulesForTable & addRules) const;
      
    private:
      map<Ipv4Prefix, LogEntryTracker>  _trackers;
    };

    typedef std::map<std::string, TableLogEntryTracker>  LogEntryTrackers;