[bind10-dev] Plugin proposal

Michal 'vorner' Vaner michal.vaner at nic.cz
Tue Mar 29 09:56:41 UTC 2011


Hello

On Tue, Mar 29, 2011 at 11:21:20AM +0200, Jelte Jansen wrote:
> Just wrote this up, so it's quite fresh, but i thought i'd send it out
> while it was hot, and before i get too wrapped up in other issues again
> here :)

Damn, I just finished mine! Once we don't have a task for something in trac and
it's done by two people right away…

Anyway, mine probably covers some slightly different topis, co when I wrote it,
I send it anyway, we can discuss them (after I read this one). Bonus: you can
format it by asciidoc if you like html more ;-)

-- 
~, sweet ~

Michal 'vorner' Vaner
-------------- next part --------------
Hooks
=====

Ingredients needed for the full hooks
-------------------------------------

Hook point class
~~~~~~~~~~~~~~~~

We need something where we can put the hooks we have. It can be some
kind of wrapper around vector of hooks that can call them one by one
(at last until we decide to implement part of views by it).  There
needs to be some kind of logic to decide if it should continue or not,
depending on the hook result.

If we decide to go with the way of „everything has the same
interface“, then there's just one of them.

To be able to insert hooks into well defined places (for
configuration, like „after receiving“), we need to mark them somehow
inside, for example by a map of names or vector, updating the
positions when adding or removing a hook.

Then the hook point could be called just like an ordinary function.

Hook class
~~~~~~~~~~

We need to be able to put something into the hook point. That might be
just ordinary abstract base class to create functors for the calls.

It might be helpful to provide a wrapper around function pointer for
convenience.

Register of hook points
~~~~~~~~~~~~~~~~~~~~~~~

This one will allow creating new hook points (eg. a hook point for
data sources, hook point for packet processing). This will allow for
plugins to place their hooks into them and for user to see/modify it
in configuration.

It can be just a 'map<string, hook point>' or some wrapper around it.

Ability to load the plugins at runtime
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Some mechanism to load a .so or python script at runtime is needed.
But it would be needed for any kind of hook mechanism.

Description of plugins
~~~~~~~~~~~~~~~~~~~~~~

Because if the plugins are used, we need some kind of dependencies
mechanism, we need something that is able to describe them. Some kind
of spec files comes into mind.

A manager
~~~~~~~~~

Entity that reads the spec files, picks up the enabled plugins, builds
dependencies of them and loads them.

A layer to allow python plugins
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

We need to provide our API to python (which we mostly do already) and
wrap the hook point and register.

Modifications to the current code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The hook points must be added somewhere to be called. Furthermore, if
we want to take full advantage, the current logic can be wrapped into
hooks, which is taking the functions and putting them into functors
and including them at initialization into the hook points.

Additional work from „basic“ hooks
----------------------------------

* The hook point needs to contain multiple hooks instead of just one.
  That's addition of the vector into the class.
* The hook point needs the logic to run them and decide to terminate
  prematurely. That's a single while (or for) loop with break.
* The register needs to work dynamically (eg. the list of hook points
  can be unknown at compile time).
* The descriptions and manager needs to handle dependencies. If we
  didn't use the plugins much, we might ignore them. In theory, this
  could be written in python (because we need the python wrappers for
  the hooks, and we need to be able to run python code from C++ code
  anyway) and share it with Boss's dependency handling. We can ignore
  the dependencies at the beginning, and let the user handle them
  manually, but that is error prone for the long term.
* Putting current code into hooks. This can be deferred for now, and
  use it only for plugins, but it's the place we can benefit from the
  infrastructure ourself (like being able to run configuration check
  without running the whole component).

Examples
--------

These are just mock ups, it can be slightly different and some
unrelated code is abbreviated little bit (and I don't look up the
exact API).

Creating a hook point
~~~~~~~~~~~~~~~~~~~~~

When some important work needs to be done, we can create a hook point
for it. For example, the complete processing can be one (eg. when we
get a packet, we need to generate some answer and then send it back ‒
so we create a hook point for providing the answer).

  #include <hooks/register.h>
  #include <hooks/point.h>
  #include <hooks/manager.h>
  #include ...
  ...

  int main() {
      Hooks::Point provideAnswer;
      Hooks::register()->add("provideAnswer", &provideAnswer);
      Hooks::manager()->loadAllPlugins();
      for (;;) {
          DNSMessage message = parseMessage(receiveMessage());
          DNSMessage answer;
          if (provideAnswer(&message, &answer /*some other arguments, etc*/)) {
              sendMessage(answer);
          }
      }
      Hooks::register()->remove("provideAnswer");
  }

Writing a plugin
~~~~~~~~~~~~~~~~

We now show two plugins, one in C++, another in python.

The first one answers with 192.0.2.1 to example.com/A and NXDOMAIN to
example.org. It passes anything else to the next plugin.

  #include <hooks/hook.h>
  #include <hooks/register.h>
  #include <dns.h>

  namespace {

  class FilterHook : public Hooks::Hook {
  public:
      virtual Hook::Result operator ()(DNSMessage *question, DNSMessage *answer) {
          if (question->name() == "example.com" && question->type() == Type::A) {
              answer->addRRset(RRset("example.com.   300 IN   A 192.0.2.1"));
	      return (Hook::Success);
	  } else if (question->name() == "example.org") {
              answer->setResult(NXDOMAIN);
	      return (Hook::Success);
	  }
	  return (Hook::PROCEED);
      }
  };

  FilterHook *hook;

  }

  // This will be called by the plugin manager when we're loaded
  // The name would be configured trough the description of plugin
  void load() {
      hook = new FilterHook;
      // Insert at the beginning
      Hooks::register()->get("provideAnswer")->insert(0, hook);
  }

  // This will be called before the plugin manager unloads us
  void unload() {
      Hooks::register()->get("provideAnswer")->remove(hook);
      delete hook;
  }

The other plugin silently drops (at last in the implementation above)
any question that has the RD bit set.

  import hooks.register
  import hooks.hook
  import dns

  def filterHook(question, answer):
      if question.getFlag("RD"):
          return hooks.hook.FAIL
      else:
          return hooks.hook.PROCEED

  def load():
      hooks.register.get("provideAnswer").insert(0, filterHook)

  def unload():
      hooks.register.get("provideAnswer").remove(filterHook)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <https://lists.isc.org/pipermail/bind10-dev/attachments/20110329/93472092/attachment.bin>


More information about the bind10-dev mailing list