int tcpdemux::process_tcp(const ipaddr &src, const ipaddr &dst,sa_family_t family, const u_char *ip_data, uint32_t ip_payload_len, const be13::packet_info &pi) { if (ip_payload_len < sizeof(struct be13::tcphdr)) { DEBUG(6) ("received truncated TCP segment! (%u<%u)", (u_int)ip_payload_len,(u_int)sizeof(struct be13::tcphdr)); return 1; } struct be13::tcphdr *tcp_header = (struct be13::tcphdr *) ip_data; /* fill in the flow_addr structure with info that identifies this flow */ flow_addr this_flow(src,dst,ntohs(tcp_header->th_sport),ntohs(tcp_header->th_dport),family); be13::tcp_seq seq = ntohl(tcp_header->th_seq); bool syn_set = FLAG_SET(tcp_header->th_flags, TH_SYN); bool ack_set = FLAG_SET(tcp_header->th_flags, TH_ACK); bool fin_set = FLAG_SET(tcp_header->th_flags, TH_FIN); bool rst_set = FLAG_SET(tcp_header->th_flags, TH_RST); /* calculate the total length of the TCP header including options */ u_int tcp_header_len = tcp_header->th_off * 4; /* Find the beginning of the tcp data. */ const u_char *tcp_data = ip_data + tcp_header_len; /* figure out how much tcp data we have, taking into account tcp options */ size_t tcp_datalen = (ip_payload_len > tcp_header_len) ? (ip_payload_len - tcp_header_len) : 0; /* see if we have state about this flow; if not, create it */ int32_t delta = 0; // from current position in tcp connection; must be SIGNED 32 bit! tcpip *tcp = find_tcpip(this_flow); DEBUG(60)("%s%s%s%s tcp_header_len=%d tcp_datalen=%d seq=%u tcp=%p", (syn_set?"SYN ":""),(ack_set?"ACK ":""),(fin_set?"FIN ":""),(rst_set?"RST ":""),(int)tcp_header_len,(int)tcp_datalen,(int)seq,tcp); /* If this_flow is not in the database and the start_new_connections flag is false, just return */ if(tcp==0 && start_new_connections==false) return 0; if(syn_set && tcp && tcp->syn_count>0 && tcp->pos>0){ std::cerr << "SYN TO IGNORE! SYN tcp="<<tcp << " flow="<<this_flow<<"\n"; return 1; } if(tcp==0){ if(tcp_datalen==0){ // zero length packet if(fin_set) return 0; // FIN on a connection that's unknown; safe to ignore if(rst_set) return 0; // RST on a connection that's unknown; safe to ignore if(syn_set==false && ack_set==false) return 0; // neither a SYN nor ACK; return } else { /* Data present on a flow that is not actively being demultiplexed. * See if it is a saved flow. If so, see if the data in the packet * matches what is on the disk. If so, return. * */ saved_flow_map_t::const_iterator it = saved_flow_map.find(this_flow); if(it!=saved_flow_map.end()){ uint32_t offset = seq - it->second->isn - 1; bool data_match = false; int fd = open(it->second->saved_filename.c_str(),O_RDONLY | O_BINARY); if(fd>0){ char *buf = (char *)malloc(tcp_datalen); if(buf){ DEBUG(100)("lseek(fd,%" PRId64 ",SEEK_SET)",(int64_t)(offset)); lseek(fd,offset,SEEK_SET); ssize_t r = read(fd,buf,tcp_datalen); data_match = (r==(ssize_t)tcp_datalen) && memcmp(buf,tcp_data,tcp_datalen)==0; free(buf); } close(fd); } DEBUG(60)("Packet matches saved flow. offset=%u len=%d filename=%s data match=%d\n", (u_int)offset,(u_int)tcp_datalen,it->second->saved_filename.c_str(),(u_int)data_match); if(data_match) return 0; } } } /* flow is in the database; make sure the gap isn't too big.*/ if(tcp){ /* Compute delta based on next expected sequence number. * If delta will be too much, start a new flow. * * NOTE: I hope we don't get a packet from the old flow when * we are processing the new one. Perhaps we should be able to have * multiple flows at the same time with the same quad, and they are * at different window areas... * */ delta = seq - tcp->nsn; // notice that signed offset is calculated if(abs(delta) > opt.max_seek){ remove_flow(this_flow); delta = 0; tcp = 0; } } /* At this point, tcp may be NULL because: * case 1 - It's a new connection and SYN IS SET; normal case * case 2 - Extra packets on a now-closed connection * case 3 - Packets for which the initial part of the connection was missed * case 4 - It's a connecton that had a huge gap and was expired out of the databsae * * THIS IS THE ONLY PLACE THAT create_tcpip() is called. */ /* q: what if syn is set AND there is data? */ /* q: what if syn is set AND we already know about this connection? */ if (tcp==NULL){ /* Don't process if this is not a SYN and there is no data. */ if(syn_set==false && tcp_datalen==0) return 0; /* Create a new connection. * delta will be 0, because it's a new connection! */ be13::tcp_seq isn = syn_set ? seq : seq-1; tcp = create_tcpip(this_flow, isn, pi); } /* Now tcp is valid */ tcp->myflow.tlast = pi.ts; // most recently seen packet tcp->last_packet_number = packet_counter++; tcp->myflow.packet_count++; /* * 2012-10-24 slg - the first byte is sent at SEQ==ISN+1. * The first byte in POSIX files have an LSEEK of 0. * The original code overcame this issue by introducing an intentional off-by-one * error with the statement tcp->isn++. * * With the new TCP state-machine we simply follow the spec. * * The new state machine works by examining the SYN and ACK packets * in accordance with the TCP spec. */ if(syn_set){ /* If the syn is set this is either a SYN or SYN-ACK. We use this information to set the direction * flag, but that's it. The direction flag is only used for coloring. */ if(tcp->syn_count>1){ DEBUG(2)("Multiple SYNs (%d) seen on connection %s",tcp->syn_count,tcp->flow_pathname.c_str()); } tcp->syn_count++; if( !ack_set ){ DEBUG(50) ("packet is handshake SYN"); /* First packet of three-way handshake */ tcp->dir = tcpip::dir_cs; // client->server } else { DEBUG(50) ("packet is handshake SYN/ACK"); /* second packet of three-way handshake */ tcp->dir = tcpip::dir_sc; // server->client } if(tcp_datalen>0){ tcp->violations++; DEBUG(1) ("TCP PROTOCOL VIOLATION: SYN with data! (length=%d)",(int)tcp_datalen); } } if(tcp_datalen==0) DEBUG(50) ("got TCP segment with no data"); // seems pointless to notify /* process any data. * Notice that this typically won't be called for the SYN or SYN/ACK, * since they both have no data by definition. */ if (tcp_datalen>0){ if (opt.console_output) { tcp->print_packet(tcp_data, tcp_datalen); } else { if (opt.store_output){ tcp->store_packet(tcp_data, tcp_datalen, delta,pi.ts); } } } if (rst_set){ remove_flow(this_flow); // take it out of the map return 0; } /* Count the FINs. * If this is a fin, determine the size of the stream */ if (fin_set){ tcp->fin_count++; if(tcp->fin_count==1){ tcp->fin_size = (seq+tcp_datalen-tcp->isn)-1; } } else { open_flows.move_to_end(tcp); } /* If a fin was sent and we've seen all of the bytes, close the stream */ DEBUG(50)("%d>0 && %d == %d",tcp->fin_count,tcp->seen_bytes(),tcp->fin_size); if (tcp->fin_count>0 && tcp->seen_bytes() == tcp->fin_size){ DEBUG(50)("all bytes have been received; removing flow"); remove_flow(this_flow); // take it out of the map } DEBUG(50)("fin_set=%d seq=%u fin_count=%d seq_count=%d len=%d isn=%u", fin_set,seq,tcp->fin_count,tcp->syn_count,(int)tcp_datalen,tcp->isn); return 0; // successfully processed }
void tcpdemux::process_tcp(const struct timeval &ts,const u_char *data, uint32_t length, const ipaddr &src, const ipaddr &dst,int32_t vlan,sa_family_t family) { if (length < sizeof(struct tcphdr)) { DEBUG(6) ("received truncated TCP segment!"); return; } struct tcphdr *tcp_header = (struct tcphdr *) data; /* calculate the total length of the TCP header including options */ u_int tcp_header_len = tcp_header->th_off * 4; /* fill in the flow_addr structure with info that identifies this flow */ flow_addr this_flow(src,dst,ntohs(tcp_header->th_sport),ntohs(tcp_header->th_dport),family); tcp_seq seq = ntohl(tcp_header->th_seq); bool syn_set = IS_SET(tcp_header->th_flags, TH_SYN); bool ack_set = IS_SET(tcp_header->th_flags, TH_ACK); //std::cerr << "\n*** process_tcp seq=" << seq << " \n"; /* recalculate the beginning of data and its length, moving past the * TCP header */ data += tcp_header_len; length -= tcp_header_len; /* see if we have state about this flow; if not, create it */ uint64_t connection_count = 0; int32_t delta = 0; // from current position in tcp connection; must be SIGNED 32 bit! tcpip *tcp = find_tcpip(this_flow); /* If this_flow is not in the database and the start_new_connections flag is false, just return */ if(tcp==0 && start_new_connections==false) return; /* flow is in the database; find it */ if(tcp) { /* Compute delta based on next expected sequence number. * If delta will be too much, start a new flow. */ delta = seq - tcp->nsn; // notice that signed offset is calculated if(abs(delta) > opt.max_seek) { connection_count = tcp->myflow.connection_count+1; remove_flow(this_flow); tcp = 0; } } /* At this point, tcp may be NULL because: * case 1 - a connection wasn't found or because * case 2 - a new connections should be started (jump is too much) * * THIS IS THE ONLY PLACE THAT create_tcpip() is called. */ if (tcp==NULL) { /* Create a new connection. * delta will be 0, because it's a new connection! */ tcp_seq isn = syn_set ? seq : seq-1; tcp = create_tcpip(this_flow, vlan, isn, ts,connection_count); } /* Now tcp is valid */ tcp->myflow.tlast = ts; // most recently seen packet tcp->myflow.packet_count++; /* * 2012-10-24 slg - the first byte is sent at SEQ==ISN+1. * The first byte in POSIX files have an LSEEK of 0. * The original code overcame this issue by introducing an intentional off-by-one * error with the statement tcp->isn++. * * With the new TCP state-machine we simply follow the spec. * * The new state machine works by examining the SYN and ACK packets * in accordance with the TCP spec. */ if(syn_set) { if(tcp->syn_count>1) { DEBUG(1)("Multiple SYNs (%d) seen on a single connection.",tcp->syn_count); } tcp->syn_count++; if( ack_set ) { DEBUG(50) ("packet is handshake SYN"); /* First packet of three-way handshake */ tcp->dir = tcpip::dir_cs; // client->server } else { DEBUG(50) ("packet is handshake SYN/ACK"); /* second packet of three-way handshake */ tcp->dir = tcpip::dir_sc; // server->client } if(length>0) { tcp->violations++; DEBUG(1) ("TCP PROTOCOL VIOLATION: SYN with data! (length=%d)",length); } } if(length==0) DEBUG(50) ("got TCP segment with no data"); // seems pointless to notify /* process any data. * Notice that this typically won't be called for the SYN or SYN/ACK, * since they both have no data by definition. */ if (length>0) { if (opt.console_output) { tcp->print_packet(data, length); } else { if (opt.opt_output_enabled) { tcp->store_packet(data, length, delta); } } } /* Finally, if there is a FIN, then kill this TCP connection*/ if (IS_SET(tcp_header->th_flags, TH_FIN)) { if(opt.opt_no_purge==false) { DEBUG(50)("packet is FIN; closing connection"); remove_flow(this_flow); // take it out of the map } } }