The ONL Tutorial

Tutorial >> Writing Your First Plugin TOC

Writing a Simple Plugin

We will first write the most basic mycounter plugin to count packets and return the packet counter when queried by the RLI via the CP. As such, it will behave exactly like a simplified form of the COUNTER plugin in ~onl/stdPlugins/. Although the plugin is very simple, it will demonstrate most of the basic plugin concepts.

Let's look at the mycounter-1100/mycounter.h file as produced by newplugin.pl. You will see that the header file:

In practice, the only functions that will require much new code are the functions to handle a packet and to handle a message from the CP. The amount of additional code depends on the nature of the plugin service. Since mycounter is very simple, even these two functions will not require much new code.

First, we extend the mycounter_instance structure in the mycounter.h file with the packet counter variable pkt_counter (we have marked the added lines of code with '+' in the left margin).

    struct mycounter_instance {
      struct rp_instance rootinstance;	// base class part
+     int       pkt_count;
    };
This code just declares what the structure tag mycounter_instance represents but does not allocate memory. The space will be allocated when we create a plugin instance. We are done with the changes in the mycounter.h file, and will now move on to the changes in the mycounter.c file.

Now, we add the code to zero the packet counter pkt_count in the function mycounter_create_instance( ) in the mycounter.c file. As shown below, we only need to add one line to the function.

    struct rp_instance *
    mycounter_create_instance(
      struct rp_class *myclass,         // points to class structure
      u_int32_t instanceid)             // new instance identifier
    {
      struct mycounter_instance *myinst; 
    
      // allocate memory for local instance struct
      MSR_PLUGIN_MALLOC(myinst,struct mycounter_instance *, 
                        sizeof(struct mycounter_instance),
                        M_MSR, 
                        M_WAITOK);
      if (myinst == NULL)   return NULL;
    
      // fill in instance pointers to local methods
      myinst->rootinstance.rpclass         = &mycounter_class;
      myinst->rootinstance.handle_packet   = mycounter_handle_packet;
      myinst->rootinstance.free_instance   = mycounter_free_instance;
      myinst->rootinstance.bind_instance   = mycounter_bind_instance;
      myinst->rootinstance.unbind_instance = mycounter_unbind_instance;
      myinst->rootinstance.handle_msg      = mycounter_handle_msg;
    
      myinst->rootinstance.instanceid = instanceid;
    
+     myinst->pkt_count = 0;        // initialization code
    
      return (struct rp_instance *)myinst;
    }
Note that mycounter_create_instance already contains the code to allocate the space for a plugin instance. MSR_PLUGIN_MALLOC is a macro that allocates kernel memory for the plugin instance. The amount of memory is sizeof(struct mycounter_instance) which includes the additional variable pkt_counter. Also, the existing code already initializes the base part of the instance entry that contains the function pointers. Finally, myinst is returned as the value of the mycounter_create_instance function. But the type returned is a pointer to a structure of type struct rp_instance which is only the base component of struct mycounter_instance. The Plugin Control Unit (PCU) that manages plugins does not know or care about the packet counter variable myinst->pkt_count, but the plugin programmer does.

The only remaining code additions are in the functions mycounter_handle_packet and mycounter_handle_message. The memory deallocation code already in mycounter_free_instance is sufficient for our plugin. We make no code additions to the mycounter_bind_instance and mycounter_unbind_instance functions. If we wanted to zero the packet counter each time we rebound the instance to another filter, we could add code to one of these two functions.

Now, we add the code to the mycounter_handle_packet function to increment the packet counter when it is called.

    void
    mycounter_handle_packet(
      struct rp_instance *this,		// points to instance structure
      void *bufferList)			// points to list of pkt buffers
    {
      struct COUNTER_instance *inst = (struct COUNTER_instance *)this;

+     inst->pkt_count++;
    }
The parameter this points to our plugin instance. We cast it to the proper type so that inst->pkt_count is properly defined. The bufferList parameter is a pointer to a list of buffer headers. This list always has one buffer header pointing to a packet buffer. For now, we will skip the description of packet buffers and buffer headers until later since we don't examine the packet in this plugin. Now, the packet counter for this instance will be incremented everytime this function is called (i.e., for every packet passed to it).

The above code demonstrates a suttle point. The function is passed a pointer of type struct rp_class which is only that part of the instance structure known to the environment external to the plugin. But inside a plugin instance, we use the type struct mycounter_instance which does include both the base (or root) components and our addition (i.e., the packet counter).

We want the plugin to respond in the following manner:

Code Command Arguments
0 Return (plugin ID, instance number) None
1 Return the packet count None
2 Return the packet count and then zero it None
all else Return the packet count and then zero it None

Finally, we add the code to the mycounter_handle_message function to respond to commands from the CP.

    int
    mycounter_handle_msg(
      struct rp_instance *this, // points to instance structure
      void *buf,                // points to request/reply buffer
      u_int8_t flags,           // flags
      u_int8_t seq,             // sequence number
      u_int8_t *len)            // IN:  request is in buf[0] thru buf[*len/4-1]
                                // OUT: reply is in buf[0] thru buf[*len/4-1]
    {
      struct mycounter_instance *inst = (struct mycounter_instance *)this;
      u_int32_t *vals   = (u_int32_t *)buf;
      u_int32_t id      = (u_int32_t)ntohl(*vals);
      u_int32_t cmnd    = (u_int32_t)ntohl(*(vals + 1));
      struct msr_bufhdr_t *hdr;
    
      switch (cmnd) {
        case 0: // Hello
          *vals   = (u_int32_t)htonl(mycounter_ID);
          *(vals + 1) = (u_int32_t)htonl(id);
          *len = 2 * sizeof(u_int32_t);
          break;
        default:
+         *vals   = (u_int32_t)htonl(inst->pkt_count);
+         if (cmnd >= 2)        inst->pkt_count = 0;
+         *len = 1 * sizeof(u_int32_t);
          break;
      }
    
      return 0;
    }
This function deserves further explanation. Recall that the RLI can send messages (or requests) to plugin instances, and reply messages will be displayed in a separate reply window for each instance. An RLI message is relayed to the appropriate SPC by the SPC's CP, and the Plugin Control Unit (PCU) within the SPC kernel demultiplexes the command and forwards it to the plugin instance by calling the instance's mycounter_handle_message function.

A message is just an array of integers that is large enough to fit into a single AAL0 ATM cell; i.e., 12 32-bit words (48 bytes). The first word contains the fields that are passed in the parameters flags, seq and *len. The remaining 11 integer words are passed through the *buf pointer parameter. The *len parameter indicates the number of bytes starting at *buf that contain data in the request message and in the reply message; i.e., the request buffer is reused for the reply buffer and is overwritten.

The mycounter_handle_msg function does not use the two variables flags and seq. However, it is worthwhile now explaining why they are there. The NSP command protocol uses a single ATM cell to transmit a command to an SPC and expects a single ATM cell in reply. The seq field is an 8-bit sequence number in the command protocol header. The flags field is an 8-bit flag field used in the command protocol. For example, one bit indicates if the cell contains a command while another bit indicates it contains a reply. These are primarily only of interest to the PCU.

By convention, word 0 contains the instance number of the plugin and word 1 contains the command or operation code. Both words are unsigned integers in Network-Byte-Order (NBO). id and cmnd are the corresponding values in Host-Byte-Order (HBO) and have be obtained from these first two words by calling the ntohl funtion. The remaining 9 words in the message are user-defined.

By convention, the command 0 is a 'hello' command in which the plugin returns the plugin ID followed by the instance number. All other command codes will return the packet count in word 0 of buf. In addition, if the command code is 2 or larger, the packet count will be zeroed.

We are done writing the plugin and should now try to find and fix any syntactic errors by compiling the code. Before doing that, review the modifications shown in the table below. You can also view the entire source code here.

File Code Fragment Modification
mycounter.h struct mycounter_instance Define packet counter variable pkt_count
mycounter.c mycounter_create_instance Zero pkt_count

mycounter_handle_packet Increment pkt_count

mycounter_handle_message Handle commands to return/zero pkt_count

 Revised:  Tue, Aug 15, 2006 

  
  

Tutorial >> Writing Your First Plugin TOC