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...
libnetdude
in more detail.
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, |
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; } |
To understand protocol plugins, it is important to understand how packet initialization
in libnetdude
works, so this is discussed first.
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)); |
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; } |