Introduction | About OPERA | Router Services | Documentation | Downloads | Contact us  
 

:: A Tutorial on Router Extension

Contents

  1. Iptables Basics
  2. Using Ethereal
  3. File Hierarchy
  4. Template for User-space Extension
  5. Example 1: The simplest ipqueue
  6. Example 2: A quick way to build a user-space extension
  7. Useful Tools and References
  8. Notes



  1. Iptables Basics
  2. A good reference is the man page, you can get the answers by using "man iptables" or "iptables -h" in many cases.
    For tutorials on netfilter/iptables, please refer to the tutorial section on netfilter offical web page.


  3. Using Ethereal
    1. Run Ethereal.


    2. Set up a capture.
    3.   Menu -> Capture -> Start
        Choose Interface: any / lo / eth0 / ...
        Set Filter: type, for example,"icmp and host 127.0.0.1" in the filter field
        Set Display Options: real time update / auto-scroll
        OK
      


  4. File Hierarchy
  5. $(ROOTDIR)
    
    src/
    router/
    ipqext/
    ipqextd.c
    ipqext.c
    ...
    extensions/
    ipqext_template/
    ipqext_template.c

    ipqext_test1/
    ipqext_test1.c

    nfmod/
    lib/
    include/
    ...
    bin/
    ...
    scripts/
    preinit
    killdaemon
    flushtables
    simple_queue
    test1_queue


  6. Template for User-space Extension
  7. The template for user-space extension can be found in ipqext_template.c. It is ready for compilation and execution. To write a new extension, a quick method is to copy an instance of the template code and then do a blind replacement of every word "template" to the name of the new extension.

    #define _IPQEXT_EXTENSION_
    
    // uncomment the following line to switch on the debug messages
    // #define __DEBUG__
    
    ...
    
    /*
     * define extension name here
     *
     * extension name must match with the output module name
     * ( i.e. ipqext_{extension name} )
     */
    const char extension_name[] = "template";
    
    ...
    


    Extension Information Structure

    Each extension contains this structure to store its internal data and a set of function pointers. The default structure declaration needs not to be changed unless the function mapping of the extension is altered.

    static ipqext_t ext = {
      prev      : NULL,   // should not be altered
      next      : NULL,   // should not be altered
      handle    : NULL,   // should not be altered
      active    : 0,      // should not be altered
      name      : extension_name,
      version   : "1.0",
      groupmask : 0,      // should not be altered
      init      : &init,
      fini      : &fini,
      proc      : &proc,
      start     : &start,
      stop      : &stop,
      reset     : &reset,
      info      : &info,
      help      : &help,
    };
    


    Packet Processing Function

    In proc function, you can define your own packet processing routine. Each filtered packet is passed to this function as a pointer to a packet message structure, which is declared as follows:
           typedef struct ipq_packet_msg {
               unsigned long packet_id;        /* ID of queued packet */
               unsigned long mark;             /* Netfilter mark value */
               long timestamp_sec;             /* Packet arrival time (seconds) */
               long timestamp_usec;            /* Packet arrvial time (+useconds) */
               unsigned int hook;              /* Netfilter hook we rode in on */
               char indev_name[IFNAMSIZ];      /* Name of incoming interface */
               char outdev_name[IFNAMSIZ];     /* Name of outgoing interface */
               unsigned short hw_protocol;     /* Hardware protocol (network order) */
               unsigned short hw_type;         /* Hardware type */
               unsigned char hw_addrlen;       /* Hardware address length */
               unsigned char hw_addr[8];       /* Hardware address */
               size_t data_len;                /* Length of packet data */
               unsigned char payload[0];       /* Optional packet data */
           } ipq_packet_msg_t;
    
    What we are most interested in is the payload field of the packet message. For any IP packet, the payload field stores the IP header, other network protocol header and data as a plain buffer. We can directly manipulate the content of this buffer.

    
    /*
     * packet processing function
     *
     * return: NF_ACCEPT packet is accepted and leaves ipqext immediately
     *         NF_QUEUE packet is queued for further ipqext processing
     *         NF_DROP packet is explicitly dropped
     *
     * negative verdict to indicate packet is altered
     */
    int proc(ipq_packet_msg_t *m)
    {
       unsigned int retval = NF_QUEUE;
    
       // put packet processing code here
       
       return retval;
    }
    


    Extension Start/Stop/Reset Function

    The start/stop/reset function is executed when an extension start/stop/reset command is issued by the extension manager. These functions should return 0 on success, -1 or nonzero value on error.

      int start() { ... }
      int stop() { ... }
      int reset() { ... }
    


    Extension Information/Help Function

    The info/help function is executed when an extension info/help command is issued by the extension manager. These functions should return a pointer to a char buffer or NULL. The info function is used to retrieve the status/information of an extension during its execution. The help function is used to display the usage of an extension (which is not necessarily added or activated by the daemon). Both of them use a char buffer as the medium for message passing.

    char infobuf[] = " info here ";
    const char* info()
    {
      ...
      return infobuf;
    }
    
    const char helpbuf[] = " help here ";
    const char* help()
    {
      ...
      return helpbuf;
    }
    


    Extension Initial Pass Function

    This function is executed after the extension manager issues an add/delete command and the extension initialization function is called. All parameters are passed to the extension as a string, thus user is responsible for parsing the parameters. For instnace, if the extension manager issues the following command to the daemon:
          ipqext -A dummy param1,param2,param3
    The string "param1,param2,param3" will be passed as a fixed-size char buffer to the initial pass function.

    int init_pass(void *handle, char *param, int paramlen)
    {
      // set module handle
      ext.handle = handle;
    
      // parse parameter buffer
    
      return 0;
    }
    


    Extension Initialization/Finalization Function

    The initialization/finalization function is executed after the extension manager issues an add/delete command. These functions should return 0 on success, -1 or nonzero value on error.

    int init()
    {
      // initalization codes here
    
      // register function must always be the last call
      ipqext_register(&ext);
    
      return 0;
    }
    
    int fini()
    {
      // unregister function must always be the first call
      ipqext_unregister(&ext);
    
      // finialization codes here
    
      return 0;
    }
    


    Module(Shared Object) Initialization/Finalization Function

    These functions are defined in shared object modules. The initialization/finalization function is executed after the extension manager loads/unloads an extension. They are not used (left blank) in most cases, the extension initialization and finalization functions should be used instead.

      void _init(void) {}
      void _fini(void) {}
    


  8. Example 1: The simplest ipqueue
    1. Download the archive "tuto.tar.gz" and un-tar it.


    2. Modify ipqtest.c and then compile it.
    3.   $ cd ~/tuto/src/router/test
        modify ipqtest.c ...
        $ make clean
        $ make
      

    4. Load ipqueue and filter a particular kind of packet with iptables.
    5.   $ ~/tuto/script/simple_queue
        or
        $ modprobe ip_queue
        $ iptables -A INPUT -p icmp -j QUEUE
      

    6. Run the simple ipqueue program.
    7.   $ ~/tuto/bin/ipqtest
      

    8. Do a simple test
    9.   $ ping localhost
      


  9. Example 2: A quick way to build a user-space extension
    1. Compile the ipqueue extension daemon and manager.
    2.   cd ~/tuto/src/router/ipqext
        make clean
        make
      

    3. Clone a instance of ipqext_template as ipqext_test1. Rename the cloned copy as ipqext_test1.c and then do a blind replacement (i.e. convert every word "template" to "test1") in both C source file and Makefile.


    4. Modify ipqext_test1.c and then compile it as an extension module.
    5.   $ cd tuto/src/router/ipqext/extension/ipqext_test1
        modify ipqext_test1.c ...
        $ make clean
        $ make
      

    6. Set up the environment and then run the daemon.
    7.   $ ~/tuto/scripts/preinit
        $ ~/tuto/bin/ipqextd -e ~/tuto/bin/extension
      

    8. Load ipqueue and filter a particular kind of packet with iptables.
    9.   $ ~/tuto/script/test1_queue
        or
        $ modprobe ip_queue
        $ iptables -t mangle -A INPUT -p icmp -j MARK --set-mark 1
        $ iptables -A INPUT -p icmp -m mark ! --mark 0x0 -j QUEUE
      

    10. Load and activate the extension.
    11.   $ cd ~/tuto/bin
        $ ipqext -A test1
        $ ipqext -G test1 1
        $ ipqext -S test1
      

    12. Do a simple test
    13.   $ ping localhost
      

    14. Unload the extension, kill the daemon and flush all rules in iptables.
    15.   $ ~/tuto/bin/ipqext -X test1
        $ ~/tuto/scripts/killdaemon
        $ ~/tuto/scripts/flushtables
      


  10. Useful Tools and References
    1. The netfilter/iptables project


  11. Notes
    1. Why do I get an error message when my program trys to bind ipqueue?

    2. Please check if there is already a process which binds the ipqueue. Also, check whether ip_queue modules is loaded by using "lsmod", load it when necessary using "modprobe ip_queue".

    3. Why do I get an netlink socket error message?

    4. In many cases, it is due to queue overflow in the ipqueue module. The packet retrival rate of your program is not as fast as the packet en-queuing rate. One simple and quick solution is to enlarge the kernel receive buffer as follow:
      echo 655360 > /proc/sys/net/core/rmem_max
      echo 655360 > /proc/sys/net/core/rmem_default
      
      If the above method can't help, you need to modify your program to make it more efficient in packet retrival or swtich to kernel extension.

 

 
  Disclaimer > last update @ 2000.09.12