libnetdude Plugins

`libnetdude` Manual
Prev |  | Next

# `libnetdude` Plugins

`libnetdude` is extendable in three ways:

- Feature Plugins: these encapsulate arbitrary pieces of code and make them conveniently accessible withing the `libnetdude` framework. Why is this a good thing? It is good because the current practice is that everybode codes up small helper tools that often only exist as executable programs, not providing a library interface that would make that code easily reusable.

  If developers provide their applications in form of `libnetdude` plugins, these contributions become instantly useful to other developers, at the programming level. Developers would no longer have to manage a set of diverse libraries and tools -- everything is available in one framework. If you want an executable, you just base it on `libnetdude` and pick what you need.

- Protocol Plugins: those are used to extend `libnetdude`'s knowledge during packet initialization. As developers contribute protocol plugins, it will become increasingly likely that requesting particular protocol headers as described in the section on packet initialization will be successful.

- Bribing the authors: this method is not recommended. However, depending on the amount of money, beer, or other luxury goods involved, we might be convinced that your new feature idea is convincing. As they say, bribery always works...

The following two section explain the two types of plugins in `libnetdude` in more detail.

# Writing a Feature Plugin

If you have a piece of packet mangling code that you want to provide to other developers, then `libnetdude` feature plugins are the best way to do so. The approach is simple: if you want people to be able to dynamically check whether or not your plugin is installed, you wrap a single, well-defined entrace point around your code. Other people's code can then query your plugin by name. If it's found, they can call your plugin, if not, then they know it's not installed. If you want to sacrifice run-time availability testing, you provide an API of your choosing in a header file and ship that with your plugin. provide

The advantage in both approaches is that developers do not have to manage lots of small libraries spread around their system themselves; rather, `libnetdude` does it for them.

In order to write a feature plugin, you should download from SourceForge the plugin template package for the version of `libnetdude` you are using. It has everything set up so that you can plug in your code right away. Follow these steps:

- Extract the package

- Replace "template" with the name of your plugin in `configure.in`/`configure.ac`, and adjust `Makefile.am` to your source files.

- The template code in `src/libnd_template.c` just requires you to fill in a name for the plugin, your name and a version number.

| Do NOT use any whitespace in your plugin name. Using whitespace makes it harder to use lndtool --run, so by convention, `libnetdude` plugin names do not use whitespace.

There are two reserved function names that have special meaning and which you should implement in your plugins (depending on your plugin's requirements):

- `gboolean init(void)` performs any initialization that your plugin has to do. You do not need to provide this function if your plugin does not require it, but if you do, then make sure that it returns `TRUE` when initialization succeeds, and `FALSE` otherwise.

- `int run(LND_Trace *trace, void *user_data)` is the main entrance point to your plugin, so hook the main functionality of your plugin in here. The first parameter is a trace that the plugin can operate on, and the second is arbitrary data passed through from `libnd_plugin_run()`. For plugins that are meant to be run using lndtool --run, the LND_Trace argument is not currently used, while the second parameter is a pointer to a LND_PluginArgs structure. See the section on linking with plugins for details. The return value of the `run()` hook is an error code. You can implement any error code convention you like; however, since the lndtool passes any error codes back to the shell, it is a good idea to follow the convention that 0 means success.

Plugin initialization is normally simple, but if your plugin requires the installation of other plugins, use `libnd_plugin_find()`. It scans the installed plugins and looks for one with the given name (obviously you need to know the name of the required plugins in order to use this mechanism).

The template code from the template package is shown below:

    /* your copyright here */

    #ifdef HAVE_CONFIG_H
    #  include <config.h>
    #endif

    #include <libnd.h>

    /* This is a basic libnetdude plugin skeleton, tweak as you see fit!
     * You don't need to fill in everything, the default functions simply
     * do nothing. The return values are currently not used, except for
     * name() of course.
     *
     * At the shell prompt, you can always find out where plugins are
     * installed by typing "libnetdude-config --plugin-dir".
     */

    /* Return the name of the plugin, to be displayed
     * in the Plugins menu.
     *
     * NOTE: NO WHITESPACE!
     */
    const char *
    name(void)
    {
      return "Template";
    }

    const char *
    author(void)
    {
      return "You, <your@email.foo>";
    }

    const char *
    version(void)
    {
      return "0.1";
    }

    /* This function is automatically called when the plugin is registered.
     * You can perform an initialization that your plugin requires here.
     * Return TRUE when initialization succeeds, FALSE otherwise.
     */
    gboolean
    init(void)
    {
      D(("Template plugin initialized.\n"));
      return TRUE;
    }

    /* Here's where the action is -- run() is called to launch the plugin.
     * Report on the outcome of the operation via the returned error code.
     * This is returned to the shell when invoked via lndtool, so returning
     * 0 is generally a good idea when everything went successfully.
     */
    int
    run(LND_Trace *trace, void *user_data)
    {
      D(("Yay! Running Template plugin ...\n"));
      return 0;
    }

# Writing a Protocol Plugin

To understand protocol plugins, it is important to understand how packet initialization in `libnetdude` works, so this is discussed first.

## Packet Initialization

Let's have a look at how packet initialization works. When `libnetdude` obtains a packet (usually via `libpcap`), it only sees the raw packet data and the information provided by the `libpcap` packet header structure: a timestamp, full packet length, and captured packet length. At this point, a protocol demuxer built into `libnetdude` tries to find the linklevel protocol type for the packet that is initialized (taken from `libnd_packet_init()`):

    LND_Protocol *proto;
    int type = pcap_datalink(pcap);

    switch (type)
      {
      case DLT_NULL:
        /* We'll assume it's IP for now */
      case DLT_RAW:
        proto = libnd_proto_registry_find(LND_PROTO_LAYER_NET, 0x0800);
        break;

      default:
        proto = libnd_proto_registry_find(LND_PROTO_LAYER_LINK, type);
      }

    if (!proto)
      proto = libnd_raw_proto_get();

    proto->init_packet(packet, packet->data, libnd_packet_get_end(packet));

It's now the protocol's job to record in the packet structure that the packet data starts with this protocol. If there is another protocol nested inside the current one (like TCP in IP etc), the protocol implementation then passes initialization on to that protocol, adjusting the start- and end data pointers for that protocol accordingly (e.g., skipping the Ethernet header if protocol is Ethernet). At the end of this process, each packet structure has associated with it a list of protocol data structures that describe the nesting of protocols in the packet data.

## Writing a Protocol Plugin

First of all, you should download from SourceForge the protocol template package for the version of `libnetdude` you are using. It has a configuration setup ready to allow you to just start writing your code.

- Extract the package

- Replace "template" with the name of your protocol in `configure.in`/`configure.ac`, and adjust `Makefile.am` as you see fit.

- The example code in `src/libnd_template.c` explains in detail what you need to do. Follow those instructions. When in doubt, consult some of the existing plugin implementations in the `libnetdude` source tree.

The template code is shown below. It is not meant to be instantly compilable. Instead the comments explain how you need to fill things in to make them work for your protocol.

    /* your copyright here */

    /* Enable debugging output */
    #define LIBND_DEBUG 1

    #include <libnd_template.h>

    /* This is a basic libetdude protocol skeleton, tweak as you see fit!
     * Looking at the code of more complex protocol plugins, like those
     * for IP or TCP (in the libnetdude source tree) is highly recommended.
     *
     * At the shell prompt, you can always find out where protocols are
     * installed by typing "libnetdude-config --proto-dir".
     */

    static LND_Protocol *template;

    /* Plugin hook implementations: ---------------------------------------- */

    const char *
    name(void)
    {
      return "Template Protocol Plugin";
    }

    const char *
    description(void)
    {
      return "A plugin providing Template support.";
    }

    const char *
    author(void)
    {
      return "You <your@email.foo>";
    }

    const char *
    version(void)
    {
      return "0.1";
    }

    /* This is the important part -- you need to create a new protocol structure
     * and return it. The library takes care of registering it properly.
     */
    LND_Protocol *
    init(void)
    {
      template = libnd_proto_new("Template",
    			     LND_PROTO_LAYER_LINK, /* Choose a layer here */
    			     0xf000);              /* Choose a constant here */

      /* Now hook the functions into the protocol that your protocol
       * needs. Have a look at libnd_protocol.h and the manual for
       * your options. You do not need to supply functions for all
       * the methods, just those you require.
       */

      /* You will always need an initialization method! */
      template->init_packet     = libnd_template_init_packet;
      template->header_complete = libnd_template_header_complete;

      /* Depending on your protocol's needs, there will be more here. */

      return template;
    }

    /* Protocol method implementations: ------------------------------------ */

    /* A function to calculate whether a packet contains the full
     * protocol data. If not, it's good to not use your plugin but
     * instead use the raw packet data plugin, although you don't
     * have to.
     */
    static gboolean
    template_header_complete(const LND_Packet *packet, guchar *data)
    {
      /* Input validation */
      if (!data)
        return FALSE;

      /* It depends on your protocol how you can figure out if you
       * have the entire protocol header present in the packet.
       * Here we assume that this is constant and TEMPLATE_HEADER_LENGTH
       * defines that size. This could be more complex if your header
       * has options etc.
       */
      return (data + TEMPLATE_HEADER_LENGTH <= libnd_packet_get_end(packet));
    }

    void
    libnd_template_init_packet(LND_Packet *packet, guchar *data, guchar *data_end)
    {
      /* A protocol for the protocol nested inside yours, if any */
      LND_Protocol *payload_proto;

      /* Let's assume that struct template_header defines what your
       * protocol header looks like
       */
      struct template_header *th;

      /* Using the enter/exit debugging macros is always a good idea at first */
      D_ENTER;

      /* If we don't have the full protocol data, let's fall back
       * on the raw protocol data. Much easier.
       */
      if (!template_header_complete(packet, data))
        {
          libnd_raw_proto_get()->init_packet(packet, data, data_end);

          /* If you use the enter/exit macros, always make sure you
           * balance them correctly!
           */
          D_RETURN;
        }

      /* Add a new protocol data segment to the packet, saying that this
       * segment is occupied by your protocol. Here we assume that the
       * data extend to the end given to us, this may not always be the
       * case.
       */
      libnd_packet_add_proto_data(packet, template, data, data_end);

      /* The rest of this code assumes that your protocol can carry other
       * protocol data. If this is not the case, you do not have to figure
       * out the protocol to continue packet initialization with.
       */
      th = (struct template_header *) data;

      /* Investigate header fields in th to figure out what is the
       * next protocol. Let's assume there's a field proto_type in
       * th that defines this.
       */
      payload_proto =
        libnd_proto_registry_find(LND_PROTO_LAYER_NET, /* Choose one level up from your protocol */
    			      th->proto_type);     /* May need byte order fixing if > 1 byte */

      /* This lookup can fail if no plugin supports the required
       * protocol -- in this case NULL is returned. You MUST check
       * for this case!
       */
      if (! payload_proto)
        payload_proto = libnd_raw_proto_get();

      /* Pass initialization on to the next protocol. We need to adjust
       * the data pointers so that the next protocol "sees" only the
       * chunk that is relevant to it. Here we assume that our protocol
       * has no trailer. If this is not the case, we would have to
       * subtract the size occupied by that trailer from data_end.
       */
      payload_proto->init_packet(packet, data + TEMPLATE_HDR_LEN, data_end);

      /* And remember -- keep 'em in sync :) */
      D_RETURN;
    }

    /* This one should also be implemented. It tells users whether your
     * protocol's header is completely present in a packet, at a given
     * nesting level (think nested IP for example -- the first, outermost
     * occurrence is nesting level 0, the next (tunneled) one is nesting
     * level 1 etc).
     */
    gboolean
    libnd_template_header_complete(const LND_Packet *packet, guint nesting)
    {
      guchar *data;

      if (!packet)
        return FALSE;

      /* This is actually really easy -- first try to obtain a pointer
       * to the spot in the packet data where your protocol starts at
       * the given nesting level. The result may be NULL here.
       */
      if (! (data = libnd_packet_get_data(packet, template, nesting)))
        return FALSE;

      return template_header_complete(packet, data);
      TOUCH(nesting);
    }

    /* Just a simple accessor function for your protocol */
    LND_Protocol *
    libnd_template_get(void)
    {
      return template;
    }

Prev | Home | Next
Using `libnetdude` |  | `libnetdude` API Reference