Add new options -C and -e requested by Debian users. --- a/src/main.c +++ b/src/main.c @@ -70,7 +70,9 @@ int max_flows = 0; int max_desired_fds = 0; int console_only = 0; +int suppress_header = 0; int strip_nonprint = 0; +int use_color = 0; char error[PCAP_ERRBUF_SIZE]; @@ -83,7 +85,9 @@ fprintf(stderr, " [-i iface] [-w file] [expression]\n\n"); fprintf(stderr, " -b: max number of bytes per flow to save\n"); fprintf(stderr, " -c: console print only (don't create files)\n"); + fprintf(stderr, " -C: console print only, but without the display of source/dest header\n"); fprintf(stderr, " -d: debug level; default is %d\n", DEFAULT_DEBUG_LEVEL); + fprintf(stderr, " -e: output each flow in alternating colors\n"); fprintf(stderr, " -f: maximum number of file descriptors to use\n"); fprintf(stderr, " -h: print this help message\n"); fprintf(stderr, " -i: network interface on which to listen\n"); @@ -124,7 +128,7 @@ opterr = 0; - while ((arg = getopt(argc, argv, "b:cd:f:hi:pr:sv")) != EOF) { + while ((arg = getopt(argc, argv, "b:cCd:ef:hi:pr:sv")) != EOF) { switch (arg) { case 'b': if ((bytes_per_flow = atoi(optarg)) < 0) { @@ -134,6 +138,10 @@ DEBUG(10) ("capturing max of %d bytes per flow", bytes_per_flow); } break; + case 'C': + suppress_header = 1; + DEBUG(10) ("packet header dump suppressed"); + /* fall through */ case 'c': console_only = 1; DEBUG(10) ("printing packets to console only"); @@ -172,6 +180,10 @@ case 'v': debug_level = 10; break; + case 'e': + use_color = 1; + DEBUG(10) ("using colors"); + break; default: DEBUG(1) ("error: unrecognized switch '%c'", optopt); need_usage = 1; --- a/src/tcpip.c +++ b/src/tcpip.c @@ -55,8 +55,10 @@ #include "tcpflow.h" extern int console_only; +extern int suppress_header; extern int bytes_per_flow; extern int strip_nonprint; +extern int use_color; /*************************************************************************/ @@ -123,6 +125,7 @@ flow_t this_flow; u_int tcp_header_len; tcp_seq seq; + flow_state_t *state; if (length < sizeof(struct tcphdr)) { DEBUG(6) ("received truncated TCP segment!"); @@ -132,12 +135,6 @@ /* calculate the total length of the TCP header including options */ tcp_header_len = tcp_header->th_off * 4; - /* return if this packet doesn't have any data (e.g., just an ACK) */ - if (length <= tcp_header_len) { - DEBUG(50) ("got TCP segment with no data"); - return; - } - /* fill in the flow_t structure with info that identifies this flow */ this_flow.src = src; this_flow.dst = dst; @@ -150,15 +147,45 @@ data += tcp_header_len; length -= tcp_header_len; + /* see if we have state about this flow; if not, create it */ + if ((state = find_flow_state(this_flow)) == NULL) { + state = create_flow_state(this_flow, seq); + } + + /* Handle empty packets */ + if (length == 0) { + /* examine TCP flags for initial TCP handshake segments: + * - SYN means that the flow is a client -> server flow + * - SYN/ACK means that the flow is a server -> client flow. */ + if ((state->isn - seq) == 0) { + if (IS_SET(tcp_header->th_flags, TH_SYN) + && IS_SET(tcp_header->th_flags, TH_ACK)) { + SET_BIT(state->flags, FLOW_DIR_SC); + DEBUG(50) ("packet is handshake SYN/ACK"); + /* If the SYN flag is set the first data byte is offset by one, + account for it (note: if we're here we have just created + state, so it's safe to change isn). */ + state->isn++; + } else if (IS_SET(tcp_header->th_flags, TH_SYN)) { + SET_BIT(state->flags, FLOW_DIR_CS); + DEBUG(50) ("packet is handshake SYN"); + state->isn++; + } + } + DEBUG(50) ("got TCP segment with no data"); + return; + } + /* strip nonprintable characters if necessary */ if (strip_nonprint) data = do_strip_nonprint(data, length); /* store or print the output */ if (console_only) { - print_packet(this_flow, data, length); + print_packet(this_flow, state, data, length); } else { - store_packet(this_flow, data, length, seq); + store_packet(this_flow, state, data, length, seq, + IS_SET(tcp_header->th_flags, TH_SYN)); } } @@ -186,26 +213,46 @@ /* print the contents of this packet to the console */ -void print_packet(flow_t flow, const u_char *data, u_int32_t length) +void print_packet(flow_t flow, flow_state_t *state, const u_char *data, + u_int32_t length) { - printf("%s: ", flow_filename(flow)); + int current_color = 0; + + /* Colors: green, blue, red */ + char *color[3] = { "\033[0;32m", "\033[0;34m", "\033[0;31m" }; + + if (use_color) { + if (IS_SET(state->flags, FLOW_DIR_CS)) + current_color = 1; + else if (IS_SET(state->flags, FLOW_DIR_SC)) + current_color = 2; + printf("%s", color[current_color]); + } + + if (suppress_header == 0) + printf("%s: ", flow_filename(flow)); + fwrite(data, length, 1, stdout); + + if (use_color) + printf("\033[0m"); + putchar('\n'); fflush(stdout); } /* store the contents of this packet to its place in its file */ -void store_packet(flow_t flow, const u_char *data, u_int32_t length, - u_int32_t seq) +void store_packet(flow_t flow, flow_state_t *state, const u_char *data, + u_int32_t length, u_int32_t seq, int syn_set) { - flow_state_t *state; tcp_seq offset; long fpos; - /* see if we have state about this flow; if not, create it */ - if ((state = find_flow_state(flow)) == NULL) { - state = create_flow_state(flow, seq); + /* If we got a SYN reset the sequence number */ + if (syn_set) { + DEBUG(50) ("resetting isn due to extra SYN"); + state->isn = seq - state->pos + 1; } /* if we're done collecting for this flow, return now */ --- a/src/tcpflow.h +++ b/src/tcpflow.h @@ -82,6 +82,10 @@ #define FLOW_FINISHED (1 << 0) #define FLOW_FILE_EXISTS (1 << 1) +/* Direction flags: server to client or client to server. */ +#define FLOW_DIR_SC (1 << 2) +#define FLOW_DIR_CS (1 << 3) + typedef struct flow_state_struct flow_state_t; @@ -131,9 +135,10 @@ void process_ip(const u_char *data, u_int32_t length); void process_tcp(const u_char *data, u_int32_t length, u_int32_t src, u_int32_t dst); -void print_packet(flow_t flow, const u_char *data, u_int32_t length); -void store_packet(flow_t flow, const u_char *data, u_int32_t length, - u_int32_t seq); +void print_packet(flow_t flow, flow_state_t *state, const u_char *data, + u_int32_t length); +void store_packet(flow_t flow, flow_state_t *state, const u_char *data, + u_int32_t length, u_int32_t seq, int syn_set); u_char *do_strip_nonprint(const u_char *data, u_int32_t length); /* flow.c */ --- a/doc/tcpflow.1.in +++ b/doc/tcpflow.1.in @@ -6,7 +6,7 @@ .na .B tcpflow [\c -.BI \-chpsv\fR\c +.BI \-cCehpsv\fR\c ] [\c .BI \-b \ max_bytes\fR\c @@ -62,6 +62,18 @@ .B -s ). .TP +.B \-C +Console print without the packet source and destination details being +printed. Print the contents of packets to stdout as they are received, +without storing any captured data to files (implies +.B -s +). +.TP +.B \-e +When outputting to the console each flow will be output in different +colors (blue for client to server flows, red for server to client flows, +green for undecided flows). +.TP .B \-d Debug level. Set the level of debugging messages printed to stderr to \fIdebug_level\fP. Higher numbers produce more messages.