=================== 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 analyzers. - 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. Quick Start =========== Writing a basic plugin is quite straight-forward as long as one follows a few conventions. In the following we walk a simple example plugin that adds a new built-in function (bif) to Bro: we'll add ``rot13(s: string) : string``, a function that rotates every character in a string by 13 places. Generally, 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. 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/rot13.bif``. It's initially empty, but we'll add our new bif there as follows:: # cat src/rot13.bif module CaesarCipher; function rot13%(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; } BroString* bs = new BroString(1, reinterpret_cast(rot13), strlen(rot13)); return new StringVal(bs); %} 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 configure script put in place by ``init-plugin`` where the Bro source tree is located (Bro needs to have been built there first):: # ./configure --bro-dist=/path/to/bro/dist && make [... 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 - (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 ``config.description`` line in ``src/Plugin.cc``, like this:: [...] plugin::Configuration Configure() { plugin::Configuration config; config.name = "Demo::Rot13"; config.description = "Caesar cipher rotating a string's characters by 13 places."; config.version.major = 1; config.version.minor = 0; return config; } [...] # 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 , 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 ``/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 ```` to represent a plugin's top-level directory. ``/__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"). ``/lib/--.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. ``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. ``lib/bif/`` Directory with auto-generated Bro scripts that declare the plugin's bif elements. The files here are produced by ``bifcl``. By convention, a plugin should put its custom scripts into sub folders of ``scripts/``, i.e., ``scripts//