Ejemplo n.º 1
0
int
decode_tcp(const uint8_t *pkt, const uint32_t len, Packet *p)
{
    struct tcphdr *tcp = (struct tcphdr *)pkt;

    s_stats.tcps_packets++;
    s_stats.tcps_bytes += len;

    if (len < sizeof *tcp)
    {
        s_stats.tcps_tooshort++;
        return -1;
    }

    unsigned hlen = tcp->th_off * 4;
    if (len < hlen)
    {
        s_stats.tcps_tooshort++;
        return -1;
    }

    packet_layer_ins(p, pkt, hlen, PROTO_TCP);
    p->transport = packet_layer_current(p);

    p->srcport = ntohs(tcp->th_sport);
    p->dstport = ntohs(tcp->th_dport);

    /* Create the pseudo header before adjusting paysize */
    struct pseudo_hdr pseudo;
    pseudo.srcaddr = p->srcaddr;
    pseudo.dstaddr = p->dstaddr;
    pseudo.zero = 0;
    pseudo.protocol = p->protocol;
    pseudo.len = htons(p->paysize);

    p->payload += hlen;
    p->paysize -= hlen;

    /* decode tcp options */
    unsigned short optlen = hlen - sizeof *tcp;
    if (optlen)
    {
        decode_tcp_options(p, pkt + hlen - optlen, optlen);
    }

    /* Lastly check the checksum */
    if (checksum((uint16_t *)tcp, &pseudo, ntohs(pseudo.len)) != 0)
    {
        s_stats.tcps_badsum++;
        return -1;
    }

    return 0;
}
Ejemplo n.º 2
0
int main( int argc, char **argv ) {
    /* Options with their defaults */
    unsigned short opt_buffer = DEFAULT_BUFFER;
    unsigned short opt_daemon = 0;
    unsigned short opt_debug = 0;
    char *opt_filename = NULL;
    unsigned short opt_port = DEFAULT_PORT;
    unsigned short opt_reply = 0;
    int option;
    /* Program logic */
    unsigned short debug_counter = DEFAULT_LOOPS;
    int client_length;
    int recv_bytes;
    int status;
    static char tcp_options_text[MAX_TCPOPT];
    /* Our process ID and Session ID */
    pid_t pid, sid;

    /* We won't run as root */
   /* if ( geteuid() == 0 ) {
        fprintf(stderr,"This program is not intended to run as superuser.\n");
        fprintf(stderr,"Please use a non-privileged user instead.\n");
        exit(EXIT_FAILURE);
    }*

    /* Parse options */
    while ( (option = getopt(argc, argv, "b:dD:f:hrp:")) != -1 ) {
        switch ( option ) {
            case 'b':
                opt_buffer = (unsigned short)( strtoul( optarg, NULL, 10 ) );
                if ( opt_buffer < MIN_BUFFER ) {
                    opt_buffer = MIN_BUFFER;
                }
                break;
            case 'd':
                opt_daemon = 1;
                break;
            case 'D':
                opt_debug = (unsigned short)( strtoul( optarg, NULL, 10 ) );
                break;
            case 'f':
                opt_filename = optarg;
                break;
            case 'h':
                puts("Welcome to tcpsnoop!\\"
                     "Usage: tcpsnoop [-d] [-D debuglevel] [-f filename] [-h] [-p tcpport] [-b buffersize]");
                exit(EXIT_SUCCESS);
                break;
            case 'p':
                opt_port = (unsigned short)( strtoul( optarg, NULL, 10 ) );
                if ( opt_port < 1024 ) {
                    fprintf(stderr,"We can't bind to port %u! It is privileged.\n",opt_port);
                    exit(EXIT_FAILURE);
                }
                break;
            case 'r':
                opt_reply = 1;
                break;
        }
    }
    if ( opt_filename == NULL ) {
        opt_filename = (char *)default_filename;
    }
    /* Check for debug level. */
    if ( opt_debug > 0 ) {
        printf("Welcome to debug level %d!\n\\"
               "Will listen on port %u and write to file %s.\n\\"
               "Will allocate %u bytes for TCP buffer\n",
                opt_debug,opt_port,opt_filename,opt_buffer);
        /* We don't allow daemon mode when debug mode is active, no 
         * matter whether the daemon option is present or not.
         * */
        opt_daemon = 0;
    }
    if ( opt_daemon != 0 ) {
        syslog( LOG_DAEMON | LOG_INFO, "Starting daemon mode.");
    }

    /* Check if we should go into daemon mode */
    if ( opt_daemon != 0 ) {
        /* Fork and get a PID. */
        pid = fork();
        /* Check if forking was successful */
        if ( pid < 0 ) {
            fprintf(stderr,"fork() failed!\n");
            exit(EXIT_FAILURE);
        }
        if ( pid > 0 ) {
            syslog( LOG_DAEMON | LOG_INFO, "Got PID %u.", pid );
            exit(EXIT_SUCCESS);
        }
    }

    /* Set umask */
    umask(022);

    /* Here we open logs and files we probably need */
    /*
     *
     */
    openlog( SYSLOG_IDENTITY, LOG_PID, LOG_DAEMON );
    statistics = fopen( opt_filename, "a+" );
    if ( statistics == NULL ) {
        if ( opt_debug > 0 ) {
            fprintf(stderr,"Could not open statistics file: %s\n",strerror(errno));
        }
        else {
            syslog( LOG_DAEMON | LOG_CRIT, "Could not open statistics file: %s", strerror(errno) );
            exit(EXIT_FAILURE);
        }
    }

    /* Create a new SID for the child process */
    if ( opt_daemon != 0 ) {
        sid = setsid();
        if (sid < 0) {
            /* Could not aquire new SID. */
            syslog( LOG_DAEMON | LOG_CRIT, "%s", "Could not aquire new SID." );
            exit(EXIT_FAILURE);
        }
        /* Close standard file descriptors since daemons don't do standard
         * I/O. They need to use log files or syslog instead
         */
        fclose(stdout);
        fclose(stdin);
        fclose(stderr);
        set_signal_handlers();
    }

    /* Prepare TCP socket. */
    tcp_socket = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
    if ( tcp_socket == -1 ) {
        /* Could not open socket. */
        if ( opt_debug > 0 ) {
            fprintf(stderr,"Could not open TCP socket: %s\n",strerror(errno));
        }
        else {
            syslog( LOG_DAEMON | LOG_CRIT, "Could not open TCP socket: %s", strerror(errno) );
        }
        exit(EXIT_FAILURE);
    }
    else {
        /* Bind to any address on local machine */
        server_address.sin_family = AF_INET;
        server_address.sin_addr.s_addr = inet_addr("10.0.1.2");
        server_address.sin_port = htons(opt_port);
        memset((void *)&(server_address.sin_zero), '\0', 8);
        status = bind( tcp_socket, (struct sockaddr *)&server_address, sizeof(server_address) );
        if ( status == 0 ) {
            /* We can now listen for incoming connections. We only allow a backlog of one
             * connection
             */
            status = listen( tcp_socket, 1 );
            if ( status != 0 ) {
                /* Cannot listen on socket. */
                if ( opt_debug > 0 ) {
                    fprintf(stderr,"Cannot listen on socket: %s\n",strerror(errno));
                }
                else {
                    syslog( LOG_DAEMON | LOG_CRIT, "Cannot listen on socket: %s", strerror(errno) );
                }
                exit(EXIT_FAILURE);
            }
        }
        else {
            /* Cannot bind to socket. */
            if ( opt_debug > 0 ) {
                fprintf(stderr,"Cannot bind to socket: %s\n",strerror(errno));
            }
            else {
                syslog( LOG_DAEMON | LOG_CRIT, "Cannot bind to socket: %s", strerror(errno) );
            }
            exit(EXIT_FAILURE);
        }
    }
    
    /* Allocate Buffer for TCP stream data.
     * (We store it temporarily only since we act as an TCP sink.)
     */
    tcp_buffer = malloc(opt_buffer);
    if ( tcp_buffer == NULL ) {
        if ( opt_debug > 0 ) {
            fprintf(stderr,"Can't allocate buffer for TCP temporary memory.\n");
        }
        else {
            syslog( LOG_DAEMON | LOG_CRIT, "Can't allocate buffer for TCP temporary memory.\n" );
        }
        exit(EXIT_FAILURE);
    }

    /* Our main loop where we wait for (a) TCP connection(s). */
    if ( opt_debug > 0 ) {
        puts("Entering main loop.");
    }
    while ( debug_counter > 0 ) {
        client_length = sizeof(client_address);
        tcp_work_socket = accept( tcp_socket, (struct sockaddr *)&client_address, (socklen_t *)&client_length );
        /* Get time for counting milliseconds. */
        get_now( &time_start, opt_debug );
        /* As soon as we got a connection, we deal with the incoming data by using
         * a second socket. We only read as much as opt_buffer bytes.
         */
        if ( (recv_bytes = recv( tcp_work_socket, tcp_buffer, opt_buffer, 0 ) ) > 0 ) {
            /* Fill tcp_info structure with data to get the TCP options and the client's
             * name.
             */
            tcp_info_length = sizeof(tcp_info);
            if ( getsockopt( tcp_work_socket, SOL_IP, TCP_INFO, (void *)&tcp_info, (socklen_t *)&tcp_info_length ) == 0 ) {
                memset((void *)tcp_options_text, 0, MAX_TCPOPT);
                decode_tcp_options(tcp_options_text,tcp_info.tcpi_options);
                if ( opt_debug > 0 ) {
                    printf("Got a new connection from client %s.\n",inet_ntoa(client_address.sin_addr));
                }
                else {
                    syslog( LOG_DAEMON | LOG_INFO, "Received connection from client at address %s.",
                           inet_ntoa(client_address.sin_addr));
                }
                /* Write some statistics and start of connection to log file. */
                fprintf(statistics,"# Received connection from %s (AdvMSS %u, PMTU %u, options (%0.X): %s)\n",
                        inet_ntoa(client_address.sin_addr),
                        tcp_info.tcpi_advmss,
                        tcp_info.tcpi_pmtu,
                        tcp_info.tcpi_options,
                        tcp_options_text
                       );
            }
        }
        while ( (recv_bytes = recv( tcp_work_socket, tcp_buffer, opt_buffer, 0 ) ) > 0 ) {
            if ( opt_debug > 0 ) {
                printf("\nReceived %d bytes on socket.\n",recv_bytes);
            }
            /* Measure time in order to create time intervals. */
            get_now( &time_now, opt_debug );
            /* Fill tcp_info structure with data */
            tcp_info_length = sizeof(tcp_info);
            if ( getsockopt( tcp_work_socket, SOL_TCP, TCP_INFO, (void *)&tcp_info, (socklen_t *)&tcp_info_length ) == 0 ) {
                if ( opt_debug > 0 ) {
                    printf("snd_cwnd: %u\nsnd_ssthresh: %u\nrcv_ssthresh: %u\nrtt: %u\nrtt_var: %u\n",
                           tcp_info.tcpi_snd_cwnd,
                           tcp_info.tcpi_snd_ssthresh,
                           tcp_info.tcpi_rcv_ssthresh,
                           tcp_info.tcpi_rtt,
                           tcp_info.tcpi_rttvar
                          );
                }
                fprintf(statistics,"%.6f %u %u %u %u %u %u %u %u %u %u %u %u\n",
                        time_to_seconds( &time_start, &time_now ),
                        tcp_info.tcpi_last_data_sent,
                        tcp_info.tcpi_last_data_recv,
                        tcp_info.tcpi_snd_cwnd,
                        tcp_info.tcpi_snd_ssthresh,
                        tcp_info.tcpi_rcv_ssthresh,
                        tcp_info.tcpi_rtt,
                        tcp_info.tcpi_rttvar,
                        tcp_info.tcpi_unacked,
                        tcp_info.tcpi_sacked,
                        tcp_info.tcpi_lost,
                        tcp_info.tcpi_retrans,
                        tcp_info.tcpi_fackets
                       );
                if ( fflush(statistics) != 0 ) {
                    if ( opt_debug > 0 ) {
                        fprintf(stderr, "Cannot flush buffers: %s\n", strerror(errno) );
                    }
                    else {
                        syslog( LOG_DAEMON | LOG_CRIT, "Cannot flush buffers: %s", strerror(errno) );
                    }
                }
                /* Send reply text via TCP connection */
                if ( opt_reply != 0 ) {
                    reply_size = snprintf( reply_string, REPLY_MAXLENGTH, "rcv_ssthresh %u\n", tcp_info.tcpi_rcv_ssthresh );
                    if ( reply_size > 0 ) {
                        if ( send( tcp_work_socket, (void *)reply_string, reply_size, MSG_DONTWAIT ) == -1 ) {
                            if ( opt_debug > 0 ) {
                                fprintf(stderr, "Reply size %u didn't match: %s\n", reply_size, strerror(errno) );
                            }
                            else {
                                syslog( LOG_DAEMON | LOG_ERR, "Reply size %u didn't match: %s", reply_size, strerror(errno) );
                            }
                        }
                    }
                }
            }
        }
        close(tcp_work_socket);
        fprintf(statistics,"# Closed connection from %s.\n",inet_ntoa(client_address.sin_addr));
        if ( fflush(statistics) != 0 ) {
            if ( opt_debug > 0 ) {
                fprintf(stderr, "Cannot flush buffers: %s\n", strerror(errno) );
            }
            else {
                syslog( LOG_DAEMON | LOG_CRIT, "Cannot flush buffers: %s", strerror(errno) );
            }
        }
        if ( opt_debug > 0 ) {
            debug_counter--;
            printf("Closed connection. Decrementing debug counter to %u.\n\n",debug_counter);
        }
        else {
            syslog( LOG_DAEMON | LOG_INFO, "Closed connection from %s",
                   inet_ntoa(client_address.sin_addr));
        }
        sleep(DEFAULT_SLEEP);
    }

    /* That's a happy ending. */
    exit(EXIT_SUCCESS);
}