/**************************************************************************** ** ** File: tcp.c ** ** Author: Mike Borella ** ** Comments: Dump TCP header information. ** ** $Id: tcp.c,v 1.24 2002/01/03 00:04:01 mborella Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU Library General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ** *****************************************************************************/ #include "global.h" #include "ip_services.h" #include "tcp.h" #include "payload.h" #define HOLDER_SIZE 64 /* * TCP option map */ strmap_t tcp_option_map [] = { { TCP_OPTION_EOL, "end of options" }, { TCP_OPTION_NOP, "no op" }, { TCP_OPTION_MSS, "maximum segment size" }, { TCP_OPTION_WS, "window scale" }, { TCP_OPTION_SACKOK, "SACK permitted" }, { TCP_OPTION_SACK, "SACK" }, { TCP_OPTION_TS, "timestamp" }, { 192, "cwnd" }, { 0, "" } }; extern struct arg_t *my_args; extern strmap_t port_map[]; /*---------------------------------------------------------------------------- ** ** dump_tcp_options() ** ** Parse TCP options ** **---------------------------------------------------------------------------- */ void dump_tcp_options(packet_t *pkt, u_int8_t length) { u_int8_t option_bytes_read; char holder[HOLDER_SIZE]; u_int8_t option_length; u_int8_t kind; u_int16_t mss; u_int32_t ts1, ts2; #ifdef DEBUG printf("\nEntering TCP options\n"); #endif option_bytes_read = 0; while(option_bytes_read < length) { /* read the kind of option */ if (get_packet_bytes((u_int8_t *) &kind, pkt, 1) == 0) return; option_bytes_read ++; /* take care of the 1-byte options */ if (kind == TCP_OPTION_EOL || kind == TCP_OPTION_NOP) { if (!my_args->m) { snprintf(holder, HOLDER_SIZE, "%d (%s)", kind, map2str(tcp_option_map, kind)); display_string("Option", holder); } continue; } /* display the option's name */ if (!my_args->m) { snprintf(holder, HOLDER_SIZE, "%d (%s)", kind, map2str(tcp_option_map, kind)); display_string("Option", holder); } else { display_minimal_string("<"); display_minimal_string(map2str(tcp_option_map, kind)); } /* get and display the length field */ if (get_packet_bytes((u_int8_t *) &option_length, pkt, 1) == 0) return; if (!my_args->m) display(" Length", (u_int8_t *) &option_length, 1, DISP_DEC); option_bytes_read += 1; /* read the 'length'-2 bytes because the field counts kind and itself */ if (get_packet_bytes((u_int8_t *) holder, pkt, option_length-2) == 0) return; option_bytes_read += (option_length - 2); /* read the field */ switch(kind) { case TCP_OPTION_EOL: case TCP_OPTION_NOP: /* these should be taken care of above... */ break; case TCP_OPTION_MSS: memcpy((void *) &mss, holder, 2); mss = ntohs(mss); if (!my_args->m) display(" MSS", (u_int8_t *) &mss, 2, DISP_DEC); else { display_minimal_string(" "); display_minimal((u_int8_t *) &mss, 2, DISP_DEC); display_minimal_string(">"); } break; case TCP_OPTION_WS: if (!my_args->m) display(" Shift count", (u_int8_t *) holder, 1, DISP_DEC); else { display_minimal_string(" "); display_minimal((u_int8_t *) holder, 1, DISP_DEC); display_minimal_string(">"); } break; case TCP_OPTION_TS: memcpy((void *) &ts1, holder, 4); ts1 = ntohl(ts1); memcpy((void *) &ts2, holder+4, 4); ts2 = ntohl(ts2); if (!my_args->m) { display(" Timestamp value", (u_int8_t *) &ts1, 4, DISP_DEC); display(" Timestamp reply", (u_int8_t *) &ts2, 4, DISP_DEC); } else { display_minimal_string(" "); display_minimal((u_int8_t *) &ts1, 4, DISP_DEC); display_minimal_string(" "); display_minimal((u_int8_t *) &ts2, 4, DISP_DEC); display_minimal_string(">"); } break; /* case 64: display_minimal_string(" "); memcpy((void *) &ts1, holder, 4); ts1 = ntohl(ts1); display_minimal((u_int8_t *) &ts1, 4, DISP_DEC); display_minimal_string(">"); break; */ default: if (my_args->m) display_minimal_string(">"); break; } } /* while */ /* * Trailing space for minimal mode */ if (my_args->m) display_minimal_string(" "); #ifdef DEBUG printf("\nLeaving TCP options\n"); #endif } /*---------------------------------------------------------------------------- ** ** dump_tcp() ** ** Parse TCP header and dump fields ** **---------------------------------------------------------------------------- */ void dump_tcp(packet_t *pkt) { tcp_header_t tcp; u_int8_t hlen, unused; char flag_str[8]; service_func_t app_src; service_func_t app_dst; char holder[64]; #ifdef DEBUG printf("\nEntering TCP\n"); #endif /* Set the layer */ set_layer(LAYER_TRANSPORT); /* * Stats accounting */ stats_update(STATS_TCP); /* * Grab the static TCP header */ if (get_packet_bytes((u_int8_t *) &tcp, pkt, 20) == 0) return; /* * Conversions */ tcp.src = ntohs(tcp.src); tcp.dst = ntohs(tcp.dst); tcp.seq_number = ntohl(tcp.seq_number); tcp.ack_number = ntohl(tcp.ack_number); hlen = tcp.header_length; unused = tcp.unused; tcp.window = ntohs(tcp.window); tcp.checksum = ntohs(tcp.checksum); tcp.urgent = ntohs(tcp.urgent); /* * Prepare flag string */ flag_str[0] = '\0'; if (tcp.flags & TCP_FLAG_SYN) strcat(flag_str, "S"); if (tcp.flags & TCP_FLAG_FIN) strcat(flag_str, "F"); if (tcp.flags & TCP_FLAG_RST) strcat(flag_str, "R"); if (tcp.flags & TCP_FLAG_PSH) strcat(flag_str, "P"); if (tcp.flags & TCP_FLAG_ACK) strcat(flag_str, "A"); if (tcp.flags & TCP_FLAG_URG) strcat(flag_str, "U"); /* * Minimal mode */ if (my_args->m) { display_minimal_string("| TCP "); display_minimal((u_int8_t *) &tcp.src, 2, DISP_DEC); display_minimal_string("->"); display_minimal((u_int8_t *) &tcp.dst, 2, DISP_DEC); display_minimal_string(" ("); display_minimal(flag_str, strlen(flag_str), DISP_STRING); display_minimal_string(","); display_minimal((u_int8_t *) &tcp.seq_number, 4, DISP_DEC); display_minimal_string(","); display_minimal((u_int8_t *) &tcp.ack_number, 4, DISP_DEC); display_minimal_string(","); display_minimal((u_int8_t *) &tcp.window, 2, DISP_DEC); display_minimal_string(") "); } else { /* Dump TCP header announcement */ display_header_banner("TCP Header"); /* port fields */ display_strmap("Source port", tcp.src, port_map); display_strmap("Destination port", tcp.dst, port_map); /* sequence and acknowledgement */ display("Sequence number", (u_int8_t *) &tcp.seq_number, 4, DISP_DEC); display("Acknowledgement number", (u_int8_t *) &tcp.ack_number, 4, DISP_DEC); /* header length and unused bits */ snprintf(holder, HOLDER_SIZE, "%d (%d bytes)", hlen, hlen*4); display_string("Header length", holder); display("Unused", (u_int8_t *) &unused, 1, DISP_DEC); /* flags */ display("Flags", (u_int8_t *) &flag_str, strlen(flag_str), DISP_STRING); /* window size, checksum and urgent ptr */ display("Window size", (u_int8_t *) &tcp.window, 2, DISP_DEC); display("Checksum", (u_int8_t *) &tcp.checksum, 2, DISP_DEC); display("Urgent", (u_int8_t *) &tcp.urgent, 2, DISP_DEC); } /* Add state */ state_set_srcport(tcp.src); state_set_dstport(tcp.dst); /* * Handle any options. There should be option(s) if the header length * is > 20 bytes (5 words). Pass in the total length of the options. */ if (hlen > 5) dump_tcp_options(pkt, hlen*4 - 20); /* dump the hex buffer */ hexbuffer_flush(); /* * If there is something left of the packet, process the application * data */ if (get_packet_apparentbytesleft(pkt)) { /* * Let's try to decode the application from the port number. If both * match a decoder, we choose the lowest, assuming that the higher * one is probably ephemeral. */ app_src = port2func(tcp.src); app_dst = port2func(tcp.dst); if (app_src && app_dst) { if (tcp.src < tcp.dst) app_src(pkt); else app_dst(pkt); } else { if (app_src) app_src(pkt); else if (app_dst) app_dst(pkt); } } #ifdef DEBUG printf("\nLeaving TCP\n"); #endif return; }