libnetdude Plugins

Table of Contents
Writing a Feature Plugin
Writing a Protocol Plugin

libnetdude is extendable in three ways:

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:

Warning

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):

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;
}