Logo Search packages:      
Sourcecode: tcpreplay version File versions

flownode.c

/* $Id: flownode.c 767 2004-10-06 12:48:49Z aturner $ */

/*
 * Copyright (c) 2001-2004 Aaron Turner.
 * 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.
 * 3. Neither the names of the copyright owners nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``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 AUTHORS OR COPYRIGHT HOLDERS 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.
 */

#include <libnet.h>
#include "config.h"
#include "flowreplay.h"
#include "flownode.h"
#include "flowkey.h"
#include "flowstate.h"
#include "cidr.h"
#include "err.h"

extern struct session_tree tcproot, udproot;
extern int nfds, NoSyn;
extern struct in_addr targetaddr;
extern CIDR *clients, *servers;

/* prepare the RB trees for tcp and udp sessions */
RB_PROTOTYPE(session_tree, session_t, node, rbsession_comp)
RB_GENERATE(session_tree, session_t, node, rbsession_comp)


/*
 * returns the session_t structure
 * based upon the key given for the RB root (one root per
 * protocol).  If the key doesn't exist, it will return NULL
 *
 * NOTE: This function is broken!  key's are not guaranteed
 * to be unique for all combinations of sessions.  What we
 * really should be doing is using a rbtree using a 32bit
 * key and then solving for collisions via a linked list.
 * this would probably be faster for the common case and still
 * provide adequate speed for collisions rather then ignoring
 * the collsion problem all together.
 */
struct session_t *
getnodebykey(char proto, u_char * key)
{
    struct session_t *node = NULL;
    struct session_t like;

    like.socket = -1;
    memcpy(like.key, key, RBKEYLEN);

    if (proto == IPPROTO_TCP) {
        if ((node = RB_FIND(session_tree, &tcproot, &like)) == NULL) {
            dbg(3, "Couldn't find TCP key: 0x%llx", pkeygen(key));
            return (NULL);
        }
    }

    else if (proto == IPPROTO_UDP) {
        if ((node = RB_FIND(session_tree, &udproot, &like)) == NULL) {
            dbg(3, "Couldn't find UDP key: 0x%llx", pkeygen(key));
            return (NULL);
        }
    }

    else {
        warnx("Invalid tree protocol: 0x%x", proto);
        return (NULL);
    }

    dbg(3, "Found 0x%llx in the tree", pkeygen(key));
    return (node);

}

/*
 * inserts a node into a tree.
 * we fill out the node and create a new open socket 
 * we then return the node or NULL on error
 */
struct session_t *
newnode(char proto, u_char * key, ip_hdr_t * ip_hdr, void *l4)
{
    struct sockaddr_in sa;
    struct session_t *newnode = NULL;
    const int on = 1;
    tcp_hdr_t *tcp_hdr = NULL;
    udp_hdr_t *udp_hdr = NULL;


    dbg(2, "Adding new node: 0x%llx", pkeygen(key));

    if ((newnode =
         (struct session_t *)malloc(sizeof(struct session_t))) == NULL)
        errx(1, "Unable to malloc memory for a new node");

    memset(newnode, '\0', sizeof(struct session_t));

    memcpy(newnode->key, key, RBKEYLEN);

    newnode->proto = ip_hdr->ip_p;

    /* create a TCP or UDP socket & insert it in the tree */
    if (newnode->proto == IPPROTO_TCP) {
        /* is this a Syn packet? */
        tcp_hdr = (tcp_hdr_t *) l4;

        /* No new flows for non-Syn packets, unless NoSyn is set */
        if ((tcp_hdr->th_flags != TH_SYN) && (NoSyn == 0)) {
            free(newnode);
            warnx("We won't connect (%s:%d -> %s:%d) on non-Syn packets",
                  libnet_addr2name4(ip_hdr->ip_src.s_addr, LIBNET_DONT_RESOLVE),
                  ntohs(tcp_hdr->th_sport),
                  libnet_addr2name4(ip_hdr->ip_dst.s_addr, LIBNET_DONT_RESOLVE),
                  ntohs(tcp_hdr->th_dport));
            return (NULL);
        }

        /* otherwise, continue on our merry way */
        newnode->server_ip = ip_hdr->ip_dst.s_addr;
        newnode->server_port = tcp_hdr->th_dport;

        /* figure out what we should set the state to */
        tcp_state(tcp_hdr, newnode);

        newnode->direction = C2S;
        newnode->wait = DONT_WAIT;

        if ((newnode->socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
            free(newnode);
            warnx("Unable to create new TCP socket: %s", strerror(errno));
            return (NULL);
        }

        /* make our socket reusable */
        setsockopt(newnode->socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

        RB_INSERT(session_tree, &tcproot, newnode);
        sa.sin_port = tcp_hdr->th_dport;
    }

    else if (newnode->proto == IPPROTO_UDP) {
        udp_hdr = (udp_hdr_t *) l4;
        /* 
         * we're not as smart about UDP as TCP so we just assume
         * the first UDP packet is client->server unless we're 
         * told otherwise
         */

        if ((clients != NULL)
            && (check_ip_CIDR(clients, ip_hdr->ip_src.s_addr))) {
            /* source IP is client */
            dbg(3, "UDP match client CIDR.  Server is destination IP: %s",
                libnet_addr2name4(ip_hdr->ip_dst.s_addr, LIBNET_DONT_RESOLVE));
            newnode->server_ip = ip_hdr->ip_dst.s_addr;
        }
        else if ((servers != NULL)
                 && (check_ip_CIDR(servers, ip_hdr->ip_src.s_addr))) {
            /* source IP is server */
            dbg(3, "UDP match server CIDR.  Server is source IP: %s",
                libnet_addr2name4(ip_hdr->ip_src.s_addr, LIBNET_DONT_RESOLVE));
            newnode->server_ip = ip_hdr->ip_src.s_addr;
        }
        else {
            /* first packet is client */
            dbg(3, "UDP client is first sender.  Server is: %s",
                libnet_addr2name4(ip_hdr->ip_src.s_addr, LIBNET_DONT_RESOLVE));
            newnode->server_ip = ip_hdr->ip_dst.s_addr;
        }
        newnode->server_port = udp_hdr->uh_dport;
        newnode->direction = C2S;
        newnode->wait = DONT_WAIT;

        if ((newnode->socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
            free(newnode);
            warnx("Unable to create new UDP socket: %s", strerror(errno));
            return (NULL);
        }

        /* make our socket reusable */
        setsockopt(newnode->socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

        RB_INSERT(session_tree, &udproot, newnode);
        sa.sin_port = udp_hdr->uh_dport;
    }

    /* connect to socket */
    sa.sin_family = AF_INET;

    /* set the appropriate destination IP */
    if (targetaddr.s_addr != 0) {
        sa.sin_addr = targetaddr;
    }
    else {
        sa.sin_addr = ip_hdr->ip_dst;
    }

    if (connect
        (newnode->socket, (struct sockaddr *)&sa,
         sizeof(struct sockaddr_in)) < 0) {
        free(newnode);
        warnx("Unable to connect to %s:%hu: %s", inet_ntoa(sa.sin_addr),
              ntohs(sa.sin_port), strerror(errno));
        return (NULL);
    }

    dbg(2, "Connected to %s:%hu as socketID: %d", inet_ntoa(sa.sin_addr),
        ntohs(sa.sin_port), newnode->socket);

    /* increment nfds so our select() works */
    if (nfds <= newnode->socket)
        nfds = newnode->socket + 1;

    return (newnode);
}

/*
 * compare two session_t structs for the RB_TREE compare
 */
int
rbsession_comp(struct session_t *a, struct session_t *b)
{
    return (memcmp(a->key, b->key, RBKEYLEN));

}

/*
 * A wrapper around RB_REMOVE to delete a node from a tree
 */

void
delete_node(struct session_tree *root, struct session_t *node)
{
    dbg(2, "Deleting node 0x%llx", pkeygen(node->key));
    RB_REMOVE(session_tree, root, node);
}


void
close_sockets(void)
{
    int tcpcount = 0, udpcount = 0;
    struct session_t *node = NULL;

    /* close the TCP sockets */
    RB_FOREACH(node, session_tree, &tcproot) {
        close(node->socket);
        tcpcount++;
    }

    /* close the UDP sockets */
    RB_FOREACH(node, session_tree, &udproot) {
        close(node->socket);
        udpcount++;
    }
    dbg(1, "Closed %d tcp and %d udp socket(s)", tcpcount, udpcount);
}

Generated by  Doxygen 1.6.0   Back to index