Vde switch plugins

From Virtualsquare
Revision as of 19:31, 27 December 2012 by Renzo (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

VDE switches support plugins.

Packet processing like dumping or filtering can be implemented by plugins.

The API to create VDE plugins is described in the file vdeplugin.h, the source code hierarchy provide some examples of plugins in the direrectory src/vde_switch/plugins/.

A plugin is a dynamic library. Its constructor must initialize plugin's features. A destructor must be provided for cleaning up plugin's data structures. A vde plugin must define a struct plugin variable named vde_plugin_data.

 struct plugin vde_plugin_data={
 .name="test",
 .help="a simple plugin for vde",
 };

VDE plugins use the event driven paradigm. Functions can be activated by commands or by switch related events.

A plugin can add its own management commands. All the plugin commands (or menu definition) must be defined in a struct comlist array, loaded by the macro ADDCL and unloaded by DELCL. The following example is a simple (useless) plugin. It manages an integer value, the user can store a new value or print the current value.

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <vdeplugin.h>

int testglobal;

struct plugin vde_plugin_data={
  .name="test",
  .help="a simple plugin for vde",
};

static int testvalue(int val)
{
  testglobal=val;
  return 0;
}

static int testprint(FILE *f)
{
  fprintf(f,"Test Plugin value %d\n",testglobal);
  return 0;
}

static struct comlist cl[]={
  {"test","============","test plugin",NULL,NOARG},
  {"test/change","N","change value",testvalue,INTARG},
  {"test/print","","change value",testprint,WITHFILE},
};

static void
__attribute__ ((constructor))
init (void)
{
  ADDCL(cl);
}

static void
__attribute__ ((destructor))
fini (void)
{
  DELCL(cl);
}

The fields of struct comlinst are:

  • the command path
  • a systax help (============ for the menu definition)
  • a description
  • the pointer to the implementation function
  • a flag field, implementation functions have different arguments depending on tags.

NOARG (or 0),INTARG and STRARG are used to forward the command parametes to the implementation function. INTARG means that there is one integer parameter, with STRARG all the characters up to the end of line get passed as a string. It is up to the implementation function parsing the syntax of the parameters and splitting multiple parameters. The last argument of the implementation function must be an int for INTFUN and a char * for a STRARG. If a function need to return a long output then WITHFILE must be set. The first argument of the implementation function is a FILE * variable in this case, this (virtual) file is used to write the output that is sent at the end of the function using the 0000 DATA END WITH '.' rule of the management protocol. All the output is buffered and sent when the implementation function exits to avoid interleaving problems with debug outputs. The use of WITHFD is rare. When this flag is set the file descriptor of the management session is passed to the implementation function as a parameter (after the WITHFILE argument and before the parameter INTARG or STRARG). This integer file descriptor should be never used to send or receive data (it would cause interleaving problems and protocol misalignments) but it can be used to identify the connection.

The example above can be compiled as a shared library:

 gcc -shared -o testplugin.so testplugin.c

and loaded in a running vde_switch by the command plugin/add:

 vde$ plugin/add ./testplugin.so

(in this case the plugin is in the working directory of the switch, otherwise use the complete path or just testplugin.so if the library is in a standard directory or in a directory named in LD_LIBRARY_PATH.)

 vde$ plugin/list
 0000 DATA END WITH '.'
 NAME                   HELP
 ------------           ----
 test                   a simple plugin for vde
 .
 1000 Success
 vde$ help
 0000 DATA END WITH '.'
 COMMAND PATH       SYNTAX          HELP
 ------------       --------------  ------------
 .....
 test               ============    test plugin
 test/change        N               change value
 test/print                         change value
 .
 1000 Success

The plugin has been loaded and the its commands are available. Let us try.

vde$ test/print  
0000 DATA END WITH '.'
Test Plugin value 0
.
1000 Success

vde$ test/change 42
1000 Success

vde$ test/print
0000 DATA END WITH '.'
Test Plugin value 42
.
1000 Success

A plugin can also subscribe for switch event notifications. These functions are used by plugins to subscribe or unsubscribe event notifications:

 int eventadd(int (*fun)(struct dbgcl *event,void *arg,va_list v),char *path,void *arg);
 int eventdel(int (*fun)(struct dbgcl *event,void *arg,va_list v),char *path,void *arg);

An event causes fun function to be called. The vararg parameters depends on the event. Vde_switch defines the following events:

PATH           FLAGS                   VARARG PARAMETERS
-------------------------------------------------------------------------
port/+         D_PORT|D_PLUS           int portno
port/-         D_PORT|D_MINUS          int portno
port/descr     D_PORT|D_DESCR          int portno, int fd, char * descr
port/ep/+      D_EP|D_PLUS             int portno, int fd
port/ep/-      D_EP|D_MINUS            int portno, int fd
packet/in      D_PACKET|D_IN           int portno, char *packet, int len
packet/out     D_PACKET|D_OUT          int portno, char *packet, int len
hash/+         D_HASH|D_PLUS           char *extmac
hash/-         D_HASH|D_MINUS          char *extmac
fstp/status    D_FSTP|D_STATUS         int portno, int vlan, int status
fstp/root      D_FSTP|D_ROOT           int portno, int vlan, char *extmac
fstp/+         D_FSTP|D_PLUS           int portno, int vlan
fstp/-         D_FSTP|D_MINUS          int portno, int vlan

The path argument for eventadd/eventdel is the kind of event the plugin need to handle as listed in the first column of the table above. If the path is just a prefix, all the events matching the prefix gets subscribed. The arg parameter is an opaque argument passed to the function (to keep the internal state of the plugin). The packet/in and packet/out event management functions can drop packets and/or change the packet contents, this is the way to implement packet filtering plugins. For packet/{in,out} management function (the fun passed to eventadd) the return value is the length of the packet. If the return value is less or equal to zero the packet is dropped. It is also possible to rewrite the packet: the buffer is large enough to store MTU bytes long packets.

Plugins can also register their own debug/event items. Each item is described by a struct dbgcl element of an array. The method to register or unregister debug/event items is similar to what has been described for commands above. The plugin should define the following fields of struct dbgcl:

  • path: the path of the event,
  • help: the comment line shown by debug/list, if help==NULL it is just an event item for other plugins, it cannot be directly used by the management interface (link built in packet/in, packet/out).
  • tag: a numerical tag to speed up the discovery of the event type (to avoid strcmp).

When a plugin needs to send an avent notification it uses:

 EVENTOUT(CL, ...)

for a debugging output:

 DBGOUT(CL, FORMAT, ...)

where CL is the struct dbgcl item. DBGOUT has a similar syntax of fprintf, while the signature of EVENTOUT is open, the sequence of parameters must match those retrieved by the event management function of the client plugin. EVENTOUT should never include newline chars ('\n') and should be called once per notification.

The following code (dump.c) uses a combination of all the support descibed above. This plugin implements a simple hexadecimal packet dumping.

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <vdeplugin.h>

static int testevent(struct dbgcl *tag,void *arg,va_list v);
static int dump(char *arg);

struct plugin vde_plugin_data={
  .name="dump",
  .help="dump packets",
};

static struct comlist cl[]={
  {"dump","============","DUMP Packets",NULL,NOARG},
  {"dump/active","0/1","start dumping data",dump,STRARG},
};

#define D_DUMP 0100 
static struct dbgcl dl[]= {
   {"dump/packetin","dump incoming packet",D_DUMP|D_IN},
   {"dump/packetout","dump outgoing packet",D_DUMP|D_OUT},
};

static int dump(char *arg)
{
  int active=atoi(arg);
  int rv;
  if (active)
    rv=eventadd(testevent,"packet",dl);
  else
    rv=eventdel(testevent,"packet",dl);
  return 0;
}

static int testevent(struct dbgcl *event,void *arg,va_list v)
{
  struct dbgcl *this=arg;
  switch (event->tag) {
    case D_PACKET|D_OUT:
      this++;
    case D_PACKET|D_IN:
      {
        int port=va_arg(v,int);
        unsigned char *buf=va_arg(v,unsigned char *);
        int len=va_arg(v,int);
        char *pktdump;
        size_t dumplen;
        FILE *out=open_memstream(&pktdump,&dumplen);
        if (out) {
          int i;
          fprintf(out,"Pkt: Port %04d len=%04d ",
              port,
              len);
          for (i=0;i<len;i++)
            fprintf(out,"%02x ",buf[i]);
          fclose(out);
          DBGOUT(this, "%s",pktdump);
          free(pktdump);
        }
      }
  }
  return 0;
}

static void
__attribute__ ((constructor))
init (void)
{
  ADDCL(cl);
  ADDDBGCL(dl);
}

static void
__attribute__ ((destructor))
fini (void)
{
  DELCL(cl);
  DELDBGCL(dl);
}

Personal tools
Namespaces

Variants
Actions
Navigation
Toolbox