Adding a new packet builder, static header size, no checksum


Adding a new packet building module to libnet is usually pretty simple. The following short document details how to add a packet builder to libnet for a protocol that has a static header size and does not require a checksum. We'll use the Sebek protocol as an example to walk through the process.


  1. Make sure you have a good reference for the protocol in question. Be it an RFC or an official release doc from the author or vendor, you'll need something comprehensive. For Sebek, the comprehensive reference is here: http://project.honeynet.org.
  2. Figure out how big the header is and add it to the top of libnet-headers.h:
    #define LIBNET_SEBEK_H          0x30    /* sebek header:          48 bytes */   
    
  3. Create the protocol header structure and add it to the end of libnet-headers.h. Take care to use POSIX datatypes to define all of your values. Structure naming conventions are more or less up to you. Since they're never exported to the user, it's not a big deal, but try to keep them short and descriptive. Convention is to add the symbolic constant #defines above the structure members they apply to.
    /*
     *  Sebek header
     *  Static header size: 48 bytes
     */
    struct libnet_sebek_hdr
    {
        u_int32_t magic;                /* identify packets that should be hidden */
        u_int16_t version;              /* protocol version, currently 1 */
    #define SEBEK_PROTO_VERSION 1
        u_int16_t type;                 /* type of record */
    #define SEBEK_TYPE_READ     0       /* currently, only read is supported */
    #define SEBEK_TYPE_WRITE    1
        u_int32_t counter;              /* PDU counter */
        u_int32_t time_sec;             /* EPOCH timer */
        u_int32_t time_usec;            /* residual microseconds */
        u_int32_t pid;                  /* PID */
        u_int32_t uid;                  /* UID */
        u_int32_t fd;                   /* FD */
    #define SEBEK_CMD_LENGTH   12
        u_int8_t cmd[SEBEK_CMD_LENGTH]; /* 12 first characters of the command */
        u_int32_t length;               /* PDU length */
    };
    
  4. Append a pblock identifier to the end of the list in libnet-structures.h. The ID number is not imporant as long as it is UNIQUE. As such, just find the last entry, append the new entry after it, and increase the pblock ID by one:
    #define LIBNET_PBLOCK_SEBEK_H           0x3f    /* Sebek header */
    
  5. Create your new builder file in src/. Adhere to the "libnet_build_PROTOCOL.c" naming convention. I recommend copying over one of the existing builder modules and modifying it as you go.
  6. Add in the libnet and BSD preamble. Make sure to add your name and my name!
    /*
     *  libnet
     *  libnet_build_sebek.c - sebek packet assembler
     *
     *  Copyright (c) 2004 Frederic Raynal (pappy@security-labs.org)
     *  Copyright (c) 1998 - 2004 Mike D. Schiffman (mike@infonexus.com)
     *  All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     * SUCH DAMAGE.
     *
     */
    
  7. The following conditional include statements are required to include the proper code:
    #if (HAVE_CONFIG_H)
    #include "../include/config.h"
    #endif
    #if (!(_WIN32) || (__CYGWIN__)) 
    #include "../include/libnet.h"
    #else
    #include "../include/win32/libnet.h"
    #endif
    
  8. Next write your function header. Note that every libnet builder function returns a ptag. Make sure to include every argument for every header option. Be cognizant of order. Finally, don't forget to include the last four arguments (which are always the same for every regular builder function).
    libnet_ptag_t
    libnet_build_sebek(u_int32_t magic, u_int16_t version, u_int16_t type,
    u_int32_t counter, u_int32_t time_sec, u_int32_t time_usec, u_int32_t pid,
    u_int32_t uid, u_int32_t fd, u_int8_t cmd[SEBEK_CMD_LENGTH], u_int32_t length,
    u_int8_t *payload, u_int32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
    
  9. Declare your volatiles. Use the following convention:
    {
        /*
         *  n is the size of the protocol unit.  This is usually the header size
         *  plus the payload size.  This is also how many bytes are allocated on
         *  the heap to hold this protocol unit.
         */
        u_int32_t n;
    
        /*
         *  p will be used to refer to the protocol block that will either be
         *  allocated if the function's ptag argument is 0, or located if ptag 
         *  refers to a previously created protocol unit.
         */
        libnet_pblock_t *p;
    
        /*
         * Finally, we have the header structure that will be overlaid onto the
         * allocated memory by way of a memcpy.
         */
        struct libnet_sebek_hdr sebek_hdr;
    
        /*
         *  h is an optional value used for protocols that have header checksums.
         *  It is used inside the pblock structure to let libnet know how big
         *  much data to checksum.  This is different for different protocols.
         *  The IPv4 checksum covers the IP header only, while TCP and UDP
         *  checksums cover header and data.
         *  Since the Sebtek protocol does not have a checksum, h is not used.
         */
        u_int16_t h;
    
  10. First operational thing we do, sanity check to make sure we have a live libnet context:
        if (l == NULL)
        {
            return (-1);
        }
    
  11. Next set the size of the memory block:
        n = LIBNET_SEBEK_H + payload_s;               /* size of memory block */
    
  12. Then, depending on the ptag argument, either find an existing pblock, or create a new one:
        p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_SEBEK_H);
        if (p == NULL)
        {
            return (-1);
        }
    
  13. Build your packet. It's always a good idea to clear out heap memory before we use it, so we'll do that now. Make sure you get endianness correct. Multi-byte values should be memcpy'd.
        memset(&sebek_hdr, 0, sizeof(sebek_hdr));
        sebek_hdr.magic     = htonl(magic);
        sebek_hdr.version   = htons(version);
        sebek_hdr.type      = htons(type);
        sebek_hdr.counter   = htonl(counter);
        sebek_hdr.time_sec  = htonl(time_sec);
        sebek_hdr.time_usec = htonl(time_usec);
        sebek_hdr.pid       = htonl(pid);
        sebek_hdr.uid       = htonl(uid);
        sebek_hdr.fd        = htonl(fd);
        memcpy(sebek_hdr.cmd, cmd, SEBEK_CMD_LENGTH*sizeof(u_int8_t));
        sebek_hdr.length = htonl(length);
    
  14. Append the newly created packet header to the pblock list:
        n = libnet_pblock_append(l, p, (u_int8_t *)&sebek_hdr, LIBNET_SEBEK_H);
        if (n == -1)
        {
            goto bad;
        }
    
  15. Handle all of the payload stuff:
        /* boilerplate payload sanity check / append macro */
        LIBNET_DO_PAYLOAD(l, p);
    
  16. Finally, return the proper ptag ID and update the protocol block's meta information (if required).
        return (ptag ? ptag : libnet_pblock_update(l, p, 0, LIBNET_PBLOCK_SEBEK_H));
    bad:
        libnet_pblock_delete(l, p);
        return (-1);
    }
    
  17. To finish up, you should REALLY test your new builder a great deal and probably write a sample application on top of it. When you're done, send the unified diffs to Mike Schiffman at infoneblahblah dot calm.

Back