static void stop_flow(struct request_stop_flow *request) { DEBUG_MSG(LOG_DEBUG, "stop_flow forcefully unlocked mutex"); pthread_mutex_unlock(&mutex); if (request->flow_id == -1) { /* Stop all flows */ const struct list_node *node = fg_list_front(&flows); while (node) { struct flow *flow = node->data; node = node->next; flow->statistics[FINAL].has_tcp_info = get_tcp_info(flow, &flow->statistics[FINAL].tcp_info) ? 0 : 1; flow->pmtu = get_pmtu(flow->fd); if (flow->settings.reporting_interval) report_flow(flow, INTERVAL); report_flow(flow, FINAL); uninit_flow(flow); remove_flow(flow); } return; } const struct list_node *node = fg_list_front(&flows); while (node) { struct flow *flow = node->data; node = node->next; if (flow->id != request->flow_id) continue; /* On Other OSes than Linux or FreeBSD, tcp_info will contain all zeroes */ flow->statistics[FINAL].has_tcp_info = get_tcp_info(flow, &flow->statistics[FINAL].tcp_info) ? 0 : 1; flow->pmtu = get_pmtu(flow->fd); if (flow->settings.reporting_interval) report_flow(flow, INTERVAL); report_flow(flow, FINAL); uninit_flow(flow); remove_flow(flow); return; } request_error(&request->r, "Unknown flow id"); }
static void stop_flow(struct _request_stop_flow *request) { DEBUG_MSG(LOG_DEBUG, "stop_flow forcefully unlocked mutex"); pthread_mutex_unlock(&mutex); if (request->flow_id == -1) { /* Stop all flows */ for (unsigned int i = 0; i < num_flows; i++) { struct _flow *flow = &flows[i]; flow->statistics[FINAL].has_tcp_info = get_tcp_info(flow, &flow->statistics[FINAL].tcp_info) ? 0 : 1; flow->pmtu = get_pmtu(flow->fd); if (flow->settings.reporting_interval) report_flow(flow, INTERVAL); report_flow(flow, FINAL); uninit_flow(flow); remove_flow(i); } return; } for (unsigned int i = 0; i < num_flows; i++) { struct _flow *flow = &flows[i]; if (flow->id != request->flow_id) continue; /* On Other OSes than Linux or FreeBSD, tcp_info will contain all zeroes */ flow->statistics[FINAL].has_tcp_info = get_tcp_info(flow, &flow->statistics[FINAL].tcp_info) ? 0 : 1; flow->pmtu = get_pmtu(flow->fd); if (flow->settings.reporting_interval) report_flow(flow, INTERVAL); report_flow(flow, FINAL); uninit_flow(flow); remove_flow(i); return; } request_error(&request->r, "Unknown flow id"); }
int tcpdemux::process_pkt(const be13::packet_info &pi) { int r = 1; // not processed yet switch(pi.ip_version()){ case 4: r = process_ip4(pi); break; case 6: r = process_ip6(pi); break; } if(r!=0){ // packet not processed? /* Write the packet if we didn't process it */ if(pwriter) pwriter->writepkt(pi.pcap_hdr,pi.pcap_data); } /* Process the timeout, if there is any */ if(tcp_timeout){ std::vector<flow *> to_close; for(flow_map_t::iterator it = flow_map.begin(); it!=flow_map.end(); it++){ tcpip &tcp = *(it->second); flow &f = tcp.myflow; uint32_t age = pi.ts.tv_sec - f.tlast.tv_sec; if (age > tcp_timeout){ to_close.push_back(&f); } } for(std::vector<flow *>::iterator it = to_close.begin(); it!=to_close.end(); it++){ remove_flow(*(*it)); } } return r; }
void test_removing_flow( void** state ) { expect_string( dladm_flow_remove, flow, flow ); expect_value( dladm_flow_remove, temporary, temporary ); will_return( dladm_flow_remove, DLADM_STATUS_OK ); assert_true( XBOW_STATUS_OK == remove_flow( flow, temporary ) ); }
int tcpdemux::process_pkt(const be13::packet_info &pi) { DEBUG(10)("process_pkt.............................................................................."); int r = 1; // not processed yet switch(pi.ip_version()){ case 4: r = process_ip4(pi); break; case 6: r = process_ip6(pi); break; } if(r!=0){ // packet not processed? /* Write the packet if we didn't process it */ if(pwriter) pwriter->writepkt(pi.pcap_hdr,pi.pcap_data); } /* Process the timeout, if there is any */ if(tcp_timeout){ /* Get a list of the flows that need to be closed. */ std::vector<flow_addr *> to_close; for(flow_map_t::iterator it = flow_map.begin(); it!=flow_map.end(); it++){ tcpip &tcp = *(it->second); uint32_t age = pi.ts.tv_sec - tcp.myflow.tlast.tv_sec; if (age > tcp_timeout){ to_close.push_back(&tcp.myflow); } } /* Close them. This removes the flows from the flow_map(), which is why we need * to create the list first. */ for(std::vector<flow_addr *>::iterator it = to_close.begin(); it!=to_close.end(); it++){ remove_flow(*(*it)); } } return r; }
static void process_select(fd_set *rfds, fd_set *wfds, fd_set *efds) { unsigned int i = 0; while (i < num_flows) { struct _flow *flow = &flows[i]; DEBUG_MSG(LOG_DEBUG, "processing pselect() for flow %d", flow->id); if (flow->listenfd_data != -1 && FD_ISSET(flow->listenfd_data, rfds)) { DEBUG_MSG(LOG_DEBUG, "ready for accept"); if (flow->state == GRIND_WAIT_ACCEPT) { if (accept_data(flow) == -1) { DEBUG_MSG(LOG_ERR, "accept_data() " "failed"); goto remove; } } } if (flow->fd != -1) { if (FD_ISSET(flow->fd, efds)) { int error_number, rc; socklen_t error_number_size = sizeof(error_number); DEBUG_MSG(LOG_DEBUG, "sock of flow %d in efds", flow->id); rc = getsockopt(flow->fd, SOL_SOCKET, SO_ERROR, (void *)&error_number, &error_number_size); if (rc == -1) { warn("failed to get errno for" "non-blocking connect"); goto remove; } if (error_number != 0) { warnc(error_number, "connect"); goto remove; } } if (FD_ISSET(flow->fd, wfds)) if (write_data(flow) == -1) { DEBUG_MSG(LOG_ERR, "write_data() failed"); goto remove; } if (FD_ISSET(flow->fd, rfds)) if (read_data(flow) == -1) { DEBUG_MSG(LOG_ERR, "read_data() failed"); goto remove; } } i++; continue; remove: if (flow->fd != -1) { flow->statistics[FINAL].has_tcp_info = get_tcp_info(flow, &flow->statistics[FINAL].tcp_info) ? 0 : 1; } flow->pmtu = get_pmtu(flow->fd); report_flow(flow, FINAL); uninit_flow(flow); remove_flow(i); DEBUG_MSG(LOG_ERR, "removed flow %d", flow->id); } }
static int prepare_fds() { DEBUG_MSG(LOG_DEBUG, "prepare_fds() called, num_flows: %d", num_flows); unsigned int i = 0; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); FD_SET(daemon_pipe[0], &rfds); maxfd = daemon_pipe[0]; struct timespec now; gettime(&now); while (i < num_flows) { struct _flow *flow = &flows[i++]; if (started && (flow->finished[READ] || !flow->settings.duration[READ] || (!flow_in_delay(&now, flow, READ) && !flow_sending(&now, flow, READ))) && (flow->finished[WRITE] || !flow->settings.duration[WRITE] || (!flow_in_delay(&now, flow, WRITE) && !flow_sending(&now, flow, WRITE)))) { /* On Other OSes than Linux or FreeBSD, tcp_info will contain all zeroes */ flow->statistics[FINAL].has_tcp_info = get_tcp_info(flow, &flow->statistics[FINAL].tcp_info) ? 0 : 1; flow->pmtu = get_pmtu(flow->fd); if (flow->settings.reporting_interval) report_flow(flow, INTERVAL); report_flow(flow, FINAL); uninit_flow(flow); remove_flow(--i); continue; } if (flow->state == GRIND_WAIT_ACCEPT && flow->listenfd_data != -1) { FD_SET(flow->listenfd_data, &rfds); maxfd = MAX(maxfd, flow->listenfd_data); } if (!started) continue; if (flow->fd != -1) { FD_SET(flow->fd, &efds); maxfd = MAX(maxfd, flow->fd); prepare_wfds(&now, flow, &wfds); prepare_rfds(&now, flow, &rfds); } } return num_flows; }
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 } } }