Jun 2011
dnet Part 1
Many programs come with modules that can registered and loaded. Some are
on demand, others compiled in while still others are precompiled and
can be loaded on demand (several Operating System kernels come to mind that
have such a capability). In this text, an example of a program that
allows a module to be written and compiled onto a program with relative
ease. The example program is the dnet test program which ships
with libdnet written by Dug Song.
The first half of the series will examine how the software is implemented
for dnet. The second half will cover making our own
program use the same methods with some minor tweaking.
dnet Do?
The dnet test program is used as a sort of regression tool
for libdnet itself. Many have found dnet to be
useful as a diagnostic tool as well. The rocks cluster ships with
dnet for example.
Following is the mod.h file:
/*
* mod.h
*
* Copyright (c) 2002 Dug Song <dugsong@monkey.org>
*
* $Id: mod.h 316 2002-03-29 06:07:27Z dugsong $
*/
#ifndef MOD_H
#define MOD_H
struct mod {
char *name;
int type;
int (*main)(int argc, char *argv[]);
};
/*
* Module types
*/
#define MOD_TYPE_DATA 0x01 /* generate data */
#define MOD_TYPE_ENCAP 0x02 /* encapsulate data */
#define MOD_TYPE_XMIT 0x04 /* send datagrams */
#define MOD_TYPE_KERN 0x08 /* kernel network info */
#endif /* MOD_H */
First the module structure:
struct mod {
char *name;
int type;
int (*main)(int argc, char *argv[]);
};
The first two variables are simple. The string name of the module
and the type (which is defined later). The third variable is a pointer
to the entrance of execution of the module. In a sense, it is almost
easier to think of the third variable as the main()
routine of the module, for that is in fact precisely what
it is. Following are the currently supported module types by
number:
/* * Module types */ #define MOD_TYPE_DATA 0x01 /* generate data */ #define MOD_TYPE_ENCAP 0x02 /* encapsulate data */ #define MOD_TYPE_XMIT 0x04 /* send datagrams */ #define MOD_TYPE_KERN 0x08 /* kernel network info */
It may not be clear at this point, but one of the interesting points
about this method is that new additions can be bolted onto
the existing program without too much fuss.
dnet.c Source
Following is the source code for the main portion of the
dnet program:
/* * dnet.c * * Copyright (c) 2001 Dug Song <dugsong@monkey.org> * * $Id: dnet.c 317 2002-03-29 06:07:49Z dugsong $ */ #includeconfig.h#include <sys/types.h> #include <err.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #includednet.h#includemod.h/* * XXX - new modules should be registered here */ extern struct mod mod_addr; extern struct mod mod_hex; extern struct mod mod_rand; extern struct mod mod_eth; extern struct mod mod_arp; extern struct mod mod_ip; extern struct mod mod_icmp; extern struct mod mod_tcp; extern struct mod mod_udp; extern struct mod mod_send; extern struct mod mod_fw; extern struct mod mod_intf; extern struct mod mod_route; static struct mod *modules[] = { &mod_addr, &mod_hex, &mod_rand, &mod_eth, &mod_arp, &mod_ip, &mod_icmp, &mod_tcp, &mod_udp, &mod_send, &mod_fw, &mod_intf, &mod_route, NULL }; static void print_modules(int type, char *string) { struct mod **m; int i; fprintf(stderr, "%s commands:\n", string); for (i = 1, m = modules; *m != NULL; m++) { if ((m[0]->type & type) != 0) { fprintf(stderr, "%-10s", m[0]->name); if ((i++ % 8) == 0) fprintf(stderr, "\n"); } } fprintf(stderr, "\n\n"); } static void print_usage(void) { fprintf(stderr, "Usage: dnet <command> <args> ...\n\n"); print_modules(MOD_TYPE_DATA, "Payload generation"); print_modules(MOD_TYPE_ENCAP, "Packet encapsulation"); print_modules(MOD_TYPE_XMIT, "Packet transmission"); print_modules(MOD_TYPE_KERN, "Kernel interface"); } static int do_command(int argc, char *argv[]) { struct mod **m; for (m = modules; *m != NULL; m++) { if (strcmp(argv[0], m[0]->name) == 0) return (m[0]->main(argc, argv)); } return (-1); } int main(int argc, char *argv[]) { if (argc < 2) { print_usage(); exit(1); } if (do_command(argc - 1, argv + 1) < 0) { print_usage(); exit(1); } exit(0); }
Well... that is a handful. So let us factor it down by part. Forgetting the includes, first up are the module declarations:
/* * XXX - new modules should be registered here */ extern struct mod mod_addr; extern struct mod mod_hex; extern struct mod mod_rand; extern struct mod mod_eth; extern struct mod mod_arp; ....
Using the extern keyword we make the program aware of
the module structures that are in other source files. Next each module is
pointed to in a modules array that is NULL terminated:
&mod_addr, &mod_hex, &mod_rand, &mod_eth, &mod_arp, &mod_ip, &mod_icmp,
&mod_tcp, &mod_udp, &mod_send, &mod_fw, &mod_intf, &mod_route, NULL
};
The next two functions are helpers. One prints out module information while the other one prints a usage message (which in turn calls the module print function):
static void
print_modules(int type, char *string)
{
struct mod **m;
int i;
fprintf(stderr, "%s commands:\n", string);
for (i = 1, m = modules; *m != NULL; m++) {
if ((m[0]->type & type) != 0) {
fprintf(stderr, "%-10s", m[0]->name);
if ((i++ % 8) == 0)
fprintf(stderr, "\n");
}
}
fprintf(stderr, "\n\n");
}
static void
print_usage(void)
{
fprintf(stderr, "Usage: dnet <command> <args> ...\n\n");
print_modules(MOD_TYPE_DATA, "Payload generation");
print_modules(MOD_TYPE_ENCAP, "Packet encapsulation");
print_modules(MOD_TYPE_XMIT, "Packet transmission");
print_modules(MOD_TYPE_KERN, "Kernel interface");
}
Note in the print_modules() function that the
NULL terminator is put to good use.
Jumping ahead here is the main():
int
main(int argc, char *argv[])
{
if (argc < 2) {
print_usage();
exit(1);
}
if (do_command(argc - 1, argv + 1) < 0) {
print_usage();
exit(1);
}
exit(0);
}
Make sure the syntax is correct..ish; then pass on
the command to the function do_command():
static int
do_command(int argc, char *argv[])
{
struct mod **m;
for (m = modules; *m != NULL; m++) {
if (strcmp(argv[0], m[0]->name) == 0)
return (m[0]->main(argc, argv));
}
return (-1);
}
At first glance, that all might look a little strange. So going line by line:
1 struct mod **m;
2 for (m = modules; *m != NULL; m++) {
3 if (strcmp(argv[0], m[0]->name) == 0)
return (m[0]->main(argc, argv)); }
4 return (-1);
NULL terminator spin through modules.main() with the argument values.woopsee!
Based on how Dug setup C modules in dnet we can easily
see how we roll our own in the next text.