mirror of
https://github.com/zeek/zeek.git
synced 2025-10-08 01:28:20 +00:00
Start of a plugin writing how-to.
See doc/devel/plugins.rst. It includes a simple example and background on how things work.
This commit is contained in:
parent
a80dd10215
commit
87a1618309
1 changed files with 339 additions and 0 deletions
339
doc/devel/plugins.rst
Normal file
339
doc/devel/plugins.rst
Normal file
|
@ -0,0 +1,339 @@
|
||||||
|
|
||||||
|
===================
|
||||||
|
Writing Bro Plugins
|
||||||
|
===================
|
||||||
|
|
||||||
|
Bro is internally moving to a plugin structure that enables extending
|
||||||
|
the system dynamically, without modifying the core code base. That way
|
||||||
|
custom code remains self-contained and can be maintained, compiled,
|
||||||
|
and installed independently. Currently, plugins can add the following
|
||||||
|
functionality to Bro:
|
||||||
|
|
||||||
|
- Bro scripts.
|
||||||
|
|
||||||
|
- Builtin functions/events/types for the scripting language.
|
||||||
|
|
||||||
|
- Protocol and file analyzers.
|
||||||
|
|
||||||
|
- Packet sources and packet dumpers. TODO: Not yet.
|
||||||
|
|
||||||
|
- Logging framework backends. TODO: Not yet.
|
||||||
|
|
||||||
|
- Input framework readers. TODO: Not yet.
|
||||||
|
|
||||||
|
A plugin's functionality is available to the user just as if Bro had
|
||||||
|
the corresponding code built-in. Indeed, internally many of Bro's
|
||||||
|
pieces are structured as plugins as well, they are just statically
|
||||||
|
compiled into the binary rather than loaded dynamically at runtime, as
|
||||||
|
external plugins are.
|
||||||
|
|
||||||
|
Quick Start
|
||||||
|
===========
|
||||||
|
|
||||||
|
Writing a basic plugin is quite straight-forward as long as one
|
||||||
|
follows a few conventions. In the following we walk through adding a
|
||||||
|
new built-in function (bif) to Bro; we'll add `a `rot13(s: string) :
|
||||||
|
string``, a function that rotates every character in a string by 13
|
||||||
|
places.
|
||||||
|
|
||||||
|
A plugin comes in the form of a directory following a certain
|
||||||
|
structure. To get started, Bro's distribution provides a helper script
|
||||||
|
``aux/bro-aux/plugin-support/init-plugin`` that creates a skeleton
|
||||||
|
plugin that can then be customized. Let's use that::
|
||||||
|
|
||||||
|
# mkdir rot13-plugin
|
||||||
|
# cd rot13-plugin
|
||||||
|
# init-plugin Demo Rot13
|
||||||
|
|
||||||
|
As you can see the script takes two arguments. The first is a
|
||||||
|
namespace the plugin will live in, and the second a descriptive name
|
||||||
|
for the plugin itself. Bro uses the combination of the two to identify
|
||||||
|
a plugin. The namespace serves to avoid naming conflicts between
|
||||||
|
plugins written by independent developers; pick, e.g., the name of
|
||||||
|
your organisation (and note that the namespace ``Bro`` is reserved for
|
||||||
|
functionality distributed by the Bro Project). In our example, the
|
||||||
|
plugin will be called ``Demo::Rot13``.
|
||||||
|
|
||||||
|
The ``init-plugin`` script puts a number of files in place. The full
|
||||||
|
layout is described later. For now, all we need is
|
||||||
|
``src/functions.bif``. It's initially empty, but we'll add our new bif
|
||||||
|
there as follows::
|
||||||
|
|
||||||
|
# cat scripts/functions.bif
|
||||||
|
module CaesarCipher;
|
||||||
|
|
||||||
|
function camel_case%(s: string%) : string
|
||||||
|
%{
|
||||||
|
char* rot13 = copy_string(s->CheckString());
|
||||||
|
|
||||||
|
for ( char* p = rot13; *p; p++ )
|
||||||
|
{
|
||||||
|
char b = islower(*p) ? 'a' : 'A';
|
||||||
|
*p = (*p - b + 13) % 26 + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StringVal(strlen(rot13), rot13);
|
||||||
|
%}
|
||||||
|
|
||||||
|
The syntax of this file is just like any other ``*.bif`` file; we
|
||||||
|
won't go into it here.
|
||||||
|
|
||||||
|
Now we can already compile our plugin, we just need to tell the
|
||||||
|
Makefile put in place by ``init-plugin`` where the Bro source tree is
|
||||||
|
located (Bro needs to have been built there first)::
|
||||||
|
|
||||||
|
# make BRO=/path/to/bro/dist
|
||||||
|
[... cmake output ...]
|
||||||
|
|
||||||
|
Now our ``rot13-plugin`` directory has everything that it needs
|
||||||
|
for Bro to recognize it as a dynamic plugin. Once we point Bro to it,
|
||||||
|
it will pull it in automatically, as we can check with the ``-N``
|
||||||
|
option:
|
||||||
|
|
||||||
|
# export BRO_PLUGIN_PATH=/path/to/rot13-plugin
|
||||||
|
# bro -N
|
||||||
|
[...]
|
||||||
|
Plugin: Demo::Rot13 - <Insert brief description of plugin> (dynamic, version 1)
|
||||||
|
[...]
|
||||||
|
|
||||||
|
That looks quite good, except for the dummy description that we should
|
||||||
|
replace with something nicer so that users will know what our plugin
|
||||||
|
is about. We do this by editing the ``BRO_PLUGIN_DESCRIPTION`` line
|
||||||
|
in ``src/Plugin.cc``, like this:
|
||||||
|
|
||||||
|
# cat src/Plugin.cc
|
||||||
|
|
||||||
|
#include <plugin/Plugin.h>
|
||||||
|
|
||||||
|
BRO_PLUGIN_BEGIN(Demo, Rot13)
|
||||||
|
BRO_PLUGIN_VERSION(1);
|
||||||
|
BRO_PLUGIN_DESCRIPTION("Caesar cipher rotating a string's characters by 13 places.");
|
||||||
|
BRO_PLUGIN_BIF_FILE(consts);
|
||||||
|
BRO_PLUGIN_BIF_FILE(events);
|
||||||
|
BRO_PLUGIN_BIF_FILE(functions);
|
||||||
|
BRO_PLUGIN_END
|
||||||
|
|
||||||
|
# make
|
||||||
|
[...]
|
||||||
|
# bro -N | grep Rot13
|
||||||
|
Plugin: Demo::Rot13 - Caesar cipher rotating a string's characters by 13 places. (dynamic, version 1)
|
||||||
|
|
||||||
|
Better. Bro can also show us what exactly the plugin provides with the
|
||||||
|
more verbose option ``-NN``::
|
||||||
|
|
||||||
|
# bro -NN
|
||||||
|
[...]
|
||||||
|
Plugin: Demo::Rot13 - Caesar cipher rotating a string's characters by 13 places. (dynamic, version 1)
|
||||||
|
[Function] CaesarCipher::rot13
|
||||||
|
[...]
|
||||||
|
|
||||||
|
There's our function. Now let's use it::
|
||||||
|
|
||||||
|
# bro -e 'print CaesarCipher::rot13("Hello")'
|
||||||
|
Uryyb
|
||||||
|
|
||||||
|
It works. We next install the plugin along with Bro itself, so that it
|
||||||
|
will find it directly without needing the ``BRO_PLUGIN_PATH``
|
||||||
|
environment variable. If we first unset the variable, the function
|
||||||
|
will no longer be available::
|
||||||
|
|
||||||
|
# unset BRO_PLUGIN_PATH
|
||||||
|
# bro -e 'print CaesarCipher::rot13("Hello")'
|
||||||
|
error in <command line>, line 1: unknown identifier CaesarCipher::rot13, at or near "CaesarCipher::rot13"
|
||||||
|
|
||||||
|
Once we install it, it works again::
|
||||||
|
|
||||||
|
# make install
|
||||||
|
# bro -e 'print CaesarCipher::rot13("Hello")'
|
||||||
|
Uryyb
|
||||||
|
|
||||||
|
The installed version went into
|
||||||
|
``<bro-install-prefix>/lib/bro/plugins/Demo_Rot13``.
|
||||||
|
|
||||||
|
We can distribute the plugin in either source or binary form by using
|
||||||
|
the Makefile's ``sdist`` and ``bdist`` target, respectively. Both
|
||||||
|
create corrsponding tarballs::
|
||||||
|
|
||||||
|
# make sdist
|
||||||
|
[...]
|
||||||
|
Source distribution in build/sdist/Demo_Rot13.tar.gz
|
||||||
|
|
||||||
|
# make bdist
|
||||||
|
[...]
|
||||||
|
Binary distribution in build/Demo_Rot13-darwin-x86_64.tar.gz
|
||||||
|
|
||||||
|
The source archive will contain everything in the plugin directory
|
||||||
|
except any generated files. The binary archive will contain anything
|
||||||
|
needed to install and run the plugin, i.e., just what ``make install``
|
||||||
|
puts into place as well. As the binary distribution is
|
||||||
|
platform-dependent, its name includes the OS and architecture the
|
||||||
|
plugin was built on.
|
||||||
|
|
||||||
|
Plugin Directory Layout
|
||||||
|
=======================
|
||||||
|
|
||||||
|
A plugin's directory needs to follow a set of conventions so that Bro
|
||||||
|
(1) recognizes it as a plugin, and (2) knows what to load. While
|
||||||
|
``init-plugin`` takes care of most of this, the following is the full
|
||||||
|
story. We'll use ``<base>`` to represent a plugin's top-level
|
||||||
|
directory.
|
||||||
|
|
||||||
|
``<base>/__bro_plugin__``
|
||||||
|
A file that marks a directory as containing a Bro plugin. The file
|
||||||
|
must exist, and its content must consist of a single line with the
|
||||||
|
qualified name of the plugin (e.g., "Demo::Rot13").
|
||||||
|
|
||||||
|
``<base>/lib/<plugin-name>-<os>-<arch>.so``
|
||||||
|
The shared library containing the plugin's compiled code. Bro will
|
||||||
|
load this in dynamically at run-time if OS and architecture match
|
||||||
|
the current platform.
|
||||||
|
|
||||||
|
``lib/bif/``
|
||||||
|
Directory with auto-generated Bro scripts that declare the plugins
|
||||||
|
bif elements. The files here are produced by ``bifcl``.
|
||||||
|
|
||||||
|
``scripts/``
|
||||||
|
A directory with the plugin's custom Bro scripts. When the plugin
|
||||||
|
gets activated, this directory will be automatically added to
|
||||||
|
``BROPATH``, so that any scripts/modules inside can be
|
||||||
|
``@load``ed.
|
||||||
|
|
||||||
|
``scripts``/__load__.bro
|
||||||
|
A Bro script that will be loaded immediately when the plugin gets
|
||||||
|
activated. See below for more information on activating plugins.
|
||||||
|
|
||||||
|
By convention, a plugin should put its custom scripts into sub folders
|
||||||
|
of ``scripts/``, i.e., ``scripts/<script-namespace>/<script>.bro`` to
|
||||||
|
avoid conflicts. As usual, you can then put a ``__load__.bro`` in
|
||||||
|
there as well so that, e.g., ``@load Demo/Rot13`` could load a whole
|
||||||
|
module in the form of multiple individual scripts.
|
||||||
|
|
||||||
|
Note that in addition to the paths above, the ``init-plugin`` helper
|
||||||
|
puts some more files and directories in place that help with
|
||||||
|
development and installation (e.g., ``CMakeLists.txt``, ``Makefile``,
|
||||||
|
and source code in ``src/``). However, all these do not have a special
|
||||||
|
meaning for Bro at runtime and aren't necessary for a plugin to
|
||||||
|
function.
|
||||||
|
|
||||||
|
``init-plugin``
|
||||||
|
===============
|
||||||
|
|
||||||
|
``init-plugin`` puts a basic plugin structure in place that follows
|
||||||
|
the above layout and augments it with a CMake build and installation
|
||||||
|
system. Note that plugins with this structure can be used both
|
||||||
|
directly out of their source directory (after ``make`` and setting
|
||||||
|
Bro's ``BRO_PLUGIN_PATH``), and when installed alongside Bro (after
|
||||||
|
``make install``).
|
||||||
|
|
||||||
|
``make install`` copies over the ``lib`` and ``scripts`` directories,
|
||||||
|
as well as the ``__bro_plugin__`` magic file and the ``README`` (which
|
||||||
|
you should customize). One can add further CMake ``install`` rules to
|
||||||
|
install additional files if neeed.
|
||||||
|
|
||||||
|
.. todo::
|
||||||
|
|
||||||
|
Describe the other files that the script puts in place.
|
||||||
|
|
||||||
|
Activating a Plugin
|
||||||
|
===================
|
||||||
|
|
||||||
|
A plugin needs to be *activated* to make it available to the user.
|
||||||
|
Activating a plugin will:
|
||||||
|
|
||||||
|
1. Load the dynamic module
|
||||||
|
2. Make any bif items available
|
||||||
|
3. Add the ``scripts/`` directory to ``BROPATH``
|
||||||
|
4. Load ``scripts/__load__.bro``
|
||||||
|
|
||||||
|
By default, Bro will automatically activate all dynamic plugins found
|
||||||
|
in its search path ``BRO_PLUGIN_PATH``. However, in bare mode (``bro
|
||||||
|
-b``), no dynamic plugins will be activated by default; instead the
|
||||||
|
user can selectively enable individual plugins in scriptland using the
|
||||||
|
``@load-plugin <qualified-plugin-name>`` directive (e.g.,
|
||||||
|
``@load-plugin Demo::Rot13``).
|
||||||
|
|
||||||
|
``bro -N`` shows activated and found yet unactivated plugins
|
||||||
|
separately. Note that plugins compiled statically into Bro are always
|
||||||
|
activated, and hence show up as such even in bare mode.
|
||||||
|
|
||||||
|
.. todo::
|
||||||
|
|
||||||
|
Is this the right activation model?
|
||||||
|
|
||||||
|
|
||||||
|
Plugin Component
|
||||||
|
================
|
||||||
|
|
||||||
|
The following gives additional information about providing individual
|
||||||
|
types of functionality via plugins. Note that a single plugin can
|
||||||
|
provide more than one type. For example, a plugin could provide
|
||||||
|
multiple protocol analyzers at once; or both a logging backend and
|
||||||
|
input reader at the same time.
|
||||||
|
|
||||||
|
We now walk briefly through the specifics of providing a specific type
|
||||||
|
of functionality (a *component*) through plugin. We'll focus on their
|
||||||
|
interfaces to the plugin system, rather than specifics on writing the
|
||||||
|
corresponding logic (usually the best way to get going on that is to
|
||||||
|
start with an existing plugin providing a corresponding component and
|
||||||
|
adapt that). We'll also point out how the CMake infrastructure put in
|
||||||
|
place by the ``init-plugin`` helper script ties the various pieces
|
||||||
|
together.
|
||||||
|
|
||||||
|
Bro Scripts
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Scripts are easy: just put them into ``scripts/``, as described above.
|
||||||
|
The CMake infrastructure will automatically install them, as well
|
||||||
|
include them into the source and binary plugin distributions.
|
||||||
|
|
||||||
|
Builtin Language Elements
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Functions
|
||||||
|
TODO
|
||||||
|
|
||||||
|
Events
|
||||||
|
TODO
|
||||||
|
|
||||||
|
Types
|
||||||
|
TODO
|
||||||
|
|
||||||
|
Protocol Analyzers
|
||||||
|
------------------
|
||||||
|
|
||||||
|
TODO.
|
||||||
|
|
||||||
|
File Analyzers
|
||||||
|
--------------
|
||||||
|
|
||||||
|
TODO.
|
||||||
|
|
||||||
|
Logging Writer
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Not yet implemented.
|
||||||
|
|
||||||
|
Input Reader
|
||||||
|
------------
|
||||||
|
|
||||||
|
Not yet implemented.
|
||||||
|
|
||||||
|
Packet Sources
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Not yet implemented.
|
||||||
|
|
||||||
|
Packet Dumpers
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Not yet implemented.
|
||||||
|
|
||||||
|
Documenting Plugins
|
||||||
|
===================
|
||||||
|
|
||||||
|
..todo::
|
||||||
|
|
||||||
|
Integrate all this with Broxygen.
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue