[Snort-devel] Help with a Segmentation fault, on a preprocessor

Salvo Danilo Giuffrida salvodanilogiuffrida at ...2499...
Tue May 20 10:47:16 EDT 2008

Hello, after having been able to compile my preprocessor, which should
check if 'mandatory' traffic is present or not (the absence could be
the sign of a DOS), and after having fixed some mistakes, I'm stuck
with a segmentation fault that the preprocessor gives me after a while
that it's running.
The code of the preprocessor is this:

/* RequiredTraffic
 * TODO:    Remember to handle errors
 *          Add a reference to the GPL license

#include "config.h"

#include "ctype.h"
#include "event_queue.h"
//Files related functions
#include <sys/file.h>
#include "generators.h"
//Headers for HashTable related functions
#include "hashtable.h"
#include "GeneralHashFunctions.h"
//Limits for integers, chars, etc...
#include <limits.h>
//Functions Add* and RegisterPreprocessor
#include "plugbase.h"
//Preprocessor IDs
#include "preprocids.h"
//Pthread library
#include <pthread.h>
//Rule struct definition, plugins APIs
#include "../../dynamic-plugins/sf_engine/sf_snort_plugin_api.h"
//Signals library
#include <signal.h>
//Memory related functions
#include "smalloc.h"
//Standard C libraries
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
//String library
#include <string.h>
//Time related functions
#include <time.h>
#include <unistd.h>
//Utility functions (LogMessage, ...)
#include "util.h"

//My header file
#include "spp_requiredtraffic.h"

//Signature id for alarms triggered by 'required' traffic rule. To be
adjusted in some way...

/*  Ultimately calls SnortEventqAdd
    Arguments are: gid, sid, rev, classification, priority, message, rule_info
    x: sid, y: message
x, 1, 0, 3, y, 0 ); }
#define ALERT(x,y) { LogMessage(y); }

 * Definition of required structures
struct RequiredTrafficRule {
    Rule normalRule;
    int maxTime;

//struct Entry *hashTable, *hashTableEntry;

struct hashtable* hashTable;
Rule* key;
timer_t* value;
struct itimerspec* timing;
struct sigevent* event;
char* key_concatenation;

//Prototypes of internal functions

/*  Initialization function (usually, takes parameters from the config
file, calls a parseargs function,
 *  like strtok, and initializes local variables)
void RequiredTrafficInit(char*);
//What the pre-processor actually does
void RequiredTrafficFunc(Packet*, void*);
//The termination function
void RequiredTrafficCleanExitFunction();
//The restart function
void RequiredTrafficRestartFunction();
//Function to print an alarm in case of missing traffic. The parameter
contains the reference to the rule that triggered the alarm
void raiseAlarm(void*);
//Function to convert an array of 6 integers to a string representing
the correspondent MAC address (6 hex strings separated by ':')
void arrayIntToEtherAddr(u_int8_t[], char*);

//Prototypes of function used to manage the hashtable
static unsigned int hash(void *k);
static int keys_equal(void *key1, void *key2);

/* Setup function, which will be called from plugbase.c */
void SetupRequiredTraffic(void) {
    /* If you really want this message to appear anyway (not only in
debug mode, the place depends on the type of messaging log,
     * it could even be registered in the syslog) use the function
LogMessage at util.h.
     * To register the preprocessor, call the function
'RegisterPreprocessor': It receives the name of your preprocessor
     * (the one in the config file) and a pointer to the entry point's function
    LogMessage("RequiredTraffic: The preprocessor has been included in
the config file, and is being registered...");
    RegisterPreprocessor("requiredtraffic", RequiredTrafficInit);

/* The initialization function. "args" is a string with the arguments
in the config file (snort.conf: preprocessor: <options>).
 * Arguments can have any separator, at $SNORT_DIR/msplit.h, there is
a good collection of functions to get the right values, eg, msplit.
void RequiredTrafficInit(char* args) {
    LogMessage("RequiredTraffic: The preprocessor is being initialized... \n");
    /* args: Name of the file containing the list of rules for
required traffic (L2 and L3) */

    /* What to do:
     * 1 - Open the file (passed as an argument)
     * 2 - Read each rule (1 line (non empty)=1 rule)
     * 3 - Parse and add it to the internal hashtable mantained by the plugin
     * 4 - Create a timer for each rule,which if armed runs a function
that prints an alert
    //1 - Open the file
    FILE* rulesFile = fopen(args, "r");
    if (rulesFile == NULL)
        perror("Error during the reading of the rules file");
    if ((hashTable = create_hashtable(16, hash, keys_equal)) == NULL)
        perror("Error during the creation of the hashtable");

    char tmpLine[256];

    //2 - Read each rule from the appropriate file
    while (fgets(tmpLine, 256, rulesFile) != NULL) {
        if (strlen(tmpLine) > 0) {
            /* 3 - Parse and add it to the internal list mantained by the plugin
             *  Format of the rules:
             *  1 - Keyword 'required'
             *  2 - Source address (IP or MAC)
             *  3 - Source port (number or 'any', not significant in
case of MAC addresses, but required in any case)
             *  4 - Direction of traffic (should always be '->')
             *  5 - Destination address
             *  6 - Destination port
             *  7 - Maximum time (We don't need to specify minTime, do we?)

            strtok(tmpLine, " "); //Skip the keyword 'required'

            key = (Rule*) malloc(sizeof (Rule));
            key->ip.src_addr = (char*) malloc(sizeof (char) *18);
            key->ip.src_port = (char*) malloc(sizeof (char) *6);
            strcpy(key->ip.src_addr, strtok(NULL, " ")); //Source address
            strcpy(key->ip.src_port, strtok(NULL, " ")); //Source port
            strtok(NULL, " "); //Skip the direction of traffic ('->')


            key->ip.dst_addr = (char*) malloc(sizeof (char) *18);
            key->ip.dst_port = (char*) malloc(sizeof (char) *6);
            strcpy(key->ip.dst_addr, strtok(NULL, " ")); //Destination address
            strcpy(key->ip.dst_port, strtok(NULL, " ")); //Destination port

            event = (struct sigevent*) malloc(sizeof (struct sigevent));
            event->sigev_notify = SIGEV_THREAD; //How the arming of
the timer is handled
            event->sigev_signo = SIGUSR1; //Type of signal sent at
arming of the timer
            event->sigev_notify_function = (void*) raiseAlarm;
//Pointer to the handler function
            event->sigev_notify_attributes = NULL; //Attributes of the
thread (NULL=default)
            event->sigev_value = (sigval_t) (void*) key; //Argument of
the handler function

            timing = (struct itimerspec*) malloc(sizeof (struct itimerspec));
            timing->it_value.tv_sec = atoi(strtok(NULL, " "));
//maxTime=Time of first arming (it's better to always use nanoseconds)
            timing->it_interval = timing->it_value; //and time of all
the subsequent armings

            //4 - Create a timer for each rule
            value = (timer_t*) malloc(sizeof (timer_t));
            if (timer_create(CLOCK_REALTIME, event, value) != 0)
                perror("Error during the creation of the timer");
            if (timer_settime(*(value), 0, timing, NULL) != 0)
                perror("Error during the setting of the timer");
            //I try to insert the key (which corresponds to a rule) in
the hashtable

            key_concatenation = (char*) malloc(sizeof (char) *(18 * 2
+ 6 * 2 + 3));
            strcat(key_concatenation, key->ip.src_addr);
            strcat(key_concatenation, key->ip.src_port);
            strcat(key_concatenation, key->ip.dst_addr);
            strcat(key_concatenation, key->ip.dst_port);
            if (!hashtable_insert(hashTable, key_concatenation, value))
                perror("Error during the inserion of a key in the hashtable");
    /* Add this preprocessor to the preprocessors list.
     * To do this, pass the pointer to the "main" preprocessor's function,
     * that is, the function to be executed everytime a packet arrives
    AddFuncToPreprocList(RequiredTrafficFunc, PRIORITY_APPLICATION,
    //If necessary, register a function to handle a termination order (SIGTERM)
    //AddFuncToCleanExitList(RequiredTrafficCleanExitFunction, NULL);
    //Idem, but to handle a restart
    //AddFuncToRestartList(RequiredTrafficRestartFunction, NULL);

/* The preprocessor's main function */
void RequiredTrafficFunc(Packet* packet, void* context) {
    //LogMessage("RequiredTraffic: The preprocessor's main function is
being called...\n");
    /* Here we should extract the information from the packet that are
interesting to us,
     * build a key with them, and see if there is a corresponding
entry in the hash table.
     * If there is one, we must set the timer corresponding to the
reference present in the
     * entry to its next period.
     * The information that are of interest for us are:
     * - Source addresses (both MAC and IP)
     * - Source port
     * - Destination addresses
     * - Destination port
    /* We must consider all the possible 4 combinations:
     * 1 - L2->L2
     * 2 - L2->L3
     * 3 - L3->L3
     * 4 - L3->L2


    key = (Rule*) calloc(1, sizeof (Rule));

    key->ip.src_addr = (char*) calloc(18, sizeof (char));
    key->ip.src_port = (char*) calloc(6, sizeof (char));
    arrayIntToEtherAddr(packet->eh->ether_src, key->ip.src_addr);
    strcpy(key->ip.src_port, "any");

    key->ip.dst_addr = (char*) calloc(INET_ADDRSTRLEN, sizeof (char));
    key->ip.dst_port = (char*) calloc(6, sizeof (char));
    if (inet_ntop(AF_INET, (void*) &(packet->iph->ip_dst),
key->ip.dst_addr, (socklen_t) (sizeof (char) *
        perror("RequiredTraffic: Error during the reading of the
packet's header");
    //arrayIntToEtherAddr(packet->eh->ether_dst, key->ip.dst_addr);
    strcpy(key->ip.dst_port, "any");

    key_concatenation = (char*) calloc(18 + INET_ADDRSTRLEN + 6 * 2 +
1, sizeof (char));
    strcat(key_concatenation, key->ip.src_addr);
    strcat(key_concatenation, key->ip.src_port);
    strcat(key_concatenation, key->ip.dst_addr);
    strcat(key_concatenation, key->ip.dst_port);

    //Look for a specific rule
    value = (timer_t*) malloc(sizeof (timer_t));
    if ((value = hashtable_search(hashTable, key_concatenation)) != NULL) {
        if (timer_gettime(*value, timing) != 0)
            perror("Error during the retrieval of the timer");
        timing->it_value = timing->it_interval; //it_value contains
the time until the next timer expiration, so I reset it to it_interval
        timer_settime(*value, 0, timing, NULL);

    /*  Now consider L3/L4 rules (a packet must be considered by two
points of view...)
     *  The only differences are:
     *  1 - Different size of the fields src_addr/dst_addr (but this
can also be skipped)
     *  2 - We (should) take into consideration the source and destination port

void RequiredTrafficRestartFunction() {

void RequiredTrafficCleanExitFunction() {
    /* Free pointers and other resources, for example */

void raiseAlarm(void* rule) {
    struct RequiredTrafficRule* ruleOfReference = (struct
RequiredTrafficRule*) rule;
    char message[200];
    sprintf(message, "Traffic missing:\n\t"
            "Source address: %s\n\t"
            "Source port: %s\n\t"
            "Destination address: %s\n\t"
            "Destination port: %s\n",

void arrayIntToEtherAddr(u_int8_t src[], char* dst) {
    char hexOctect[3]; //2 characters
    int i;
    for (i = 0; i < 6; i++) {
        sprintf(hexOctect, "%02X", src[i]);
        if (i > 0)
            strcat(dst, ":");
        strcat(dst, hexOctect);

static unsigned int hash(void *key) {
    return RSHash(key, strlen(key));

static int keys_equal(void *key1, void *key2) {
    if (hash(key1) == hash(key2))
        return 1;
        return 0;

This is a sample (sanitized) 'required.rules' file:
required 00:xx:xx:xx:xx:xx any -> any 15
required 00:xx:xx:xx:xx:xx any -> any 14
required 00:xx:xx:xx:xx:xx any -> any 10
required 00:xx:xx:xx:xx:xx any -> any 3

When I debug Snort with gdb, that's the output of the program:
Initializing rule chains...
0 Snort rules read
    0 detection rules
    0 decoder rules
    0 preprocessor rules
0 Option Chains linked into 0 Chain Headers
0 Dynamic rules

+-------------------[Rule Port Counts]---------------------------------------
|             tcp     udp    icmp      ip
|     src       0       0       0       0
|     dst       0       0       0       0
|     any       0       0       0       0
|      nc       0       0       0       0
|     s+d       0       0       0       0

| memory-cap : 1048576 bytes
| none
| none
| none
Rule application order: activation->dynamic->pass->drop->alert->log
Log directory = /var/log/snort
Verifying Preprocessor Configurations!
0 out of 512 flowbits in use.
Decoding Ethernet on interface eth0

        --== Initialization Complete ==--

   ,,_     -*> Snort! <*-
  o"  )~   Version 2.8.1 (Build 28)
   ''''    By Martin Roesch & The Snort Team: http://www.snort.org/team.html
           (C) Copyright 1998-2008 Sourcefire Inc., et al.
           Using PCRE version: 7.2 2007-06-19

           Rules Engine: SF_SNORT_DETECTION_ENGINE  Version 1.8  <Build 13>
           Preprocessor Object: SF_SSH  Version 1.1  <Build 1>
           Preprocessor Object: SF_SMTP  Version 1.1  <Build 7>
           Preprocessor Object: SF_FTPTELNET  Version 1.1  <Build 10>
           Preprocessor Object: SF_DNS  Version 1.1  <Build 2>
           Preprocessor Object: SF_DCERPC  Version 1.1  <Build 4>
[New Thread -1242231920 (LWP 23636)]
Traffic missing:
        Source address: 00:xx:xx:xx:xx:xx
        Source port: any
        Destination address:
        Destination port: any
[Thread -1242231920 (LWP 23636) exited]

[Other messages of missing traffic follow, printed using the function
LogMessage() (SnortEventqAdd still doesn't work). After a while, when
some traffic is listened on eth0, any traffic, a segmentation fault is
raised on the function RequiredTrafficFunc(Packet* packet, void*

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread -1211107664 (LWP 23632)]
0xb7dfa003 in inet_ntop () from /lib/tls/i686/cmov/libc.so.6
(gdb) bt
#0  0xb7dfa003 in inet_ntop () from /lib/tls/i686/cmov/libc.so.6
#1  0x080c85b6 in RequiredTrafficFunc (packet=0xbf94aa3c, context=0x0)
    at spp_requiredtraffic.c:232
#2  0x08068cc2 in Preprocess (p=0xbf94aa3c) at detect.c:176
#3  0x08063108 in ProcessPacket (user=0x0, pkthdr=0xbf94ae40,
    pkt=0x8cbd812 "������", ft=0x0) at snort.c:2085
#4  0x0806332d in PcapProcessPacket (user=0x0, pkthdr=0xbf94ae40,
    pkt=0x8cbd812 "������") at snort.c:1441
#5  0xb7ea9d67 in ?? () from /usr/lib/libpcap.so.0.8
#6  0x00000000 in ?? ()

I'm not very good in decoding gdb backtraces, but I don't like that
0x00000000 on stack #6. What could be the reason of the segmentation
Also, speaking of the functionality in itself, do you think it would
be better to implement it in the 'main' Snort engine, so that I'll be
able to use the same exact rule sintaxt of 'traditional'
alert/log/pass rules, and the 'required' rules (new keyword) will be
parsed automaticly by Snort functions, and put in the 3-D list that
represents the various kind of rules? My only doubt in doing this is
that I need to check for mandatory traffic not only on L3/L4, but also
on L2 (Ethernet), and also mixed, so that it should be possible to
check if packets from the NIC 00:xx:xx:xx:xx:xx toward the IP address
xyz.xyz.xyz.xyz (mixed L2/L3 check) are flowing through the network.
Could this be implemented in the main Snort engine, by extending the
Rule struct and modifying the parser functions?

More information about the Snort-devel mailing list