listener.set_tcp_event(tsd);
tcp_state_handlers[ tcp_state ]->eval(tsd, listener);
tcp_state_handlers[ tcp_state ]->do_post_sm_packet_actions(tsd, listener);
+ if( listener.process_inorder_fin() )
+ {
+ //FIN is in order or we need to process FIN from state_queue
+ tcp_state_handlers[ tcp_state ]->eval(tsd, listener);
+ tcp_state = talker.get_tcp_state( );
+ tcp_state_handlers[ tcp_state ]->eval(tsd, talker);
+ tcp_state_handlers[ tcp_state ]->do_post_sm_packet_actions(tsd, listener);
+ listener.inorder_fin = false;
+ }
return true;
}
tcp_event = TCP_SYN_ACK_SENT_EVENT;
else if ( tcph->is_rst() )
tcp_event = TCP_RST_SENT_EVENT;
- else if ( tcph->is_fin( ) )
+ else if ( tcph->is_fin( ) )
tcp_event = TCP_FIN_SENT_EVENT;
else if ( tcph->is_ack() || tcph->is_psh() )
{
return rst_pkt_sent;
}
+ bool process_inorder_fin() const
+ {
+ return inorder_fin;
+ }
+
virtual void init_tcp_state() = 0;
virtual void init_toolbox() = 0;
uint32_t fin_final_seq = 0;
bool rst_pkt_sent = false;
+ bool inorder_fin = false;
// FIXIT-L make these non-public
public:
seglist.next = tsn->next;
// FIXIT-L this is suboptimal - better to exclude fin from toSeq
- if ( !tracker->fin_set() or SEQ_LT(toSeq, tracker->fin_final_seq) )
+ if ( !tracker->fin_set() or SEQ_LEQ(toSeq, tracker->fin_final_seq) )
tracker->set_tf_flags(TF_MISSING_PKT);
break;
listener->reassembler->set_overlap_count(0);
}
}
+void TcpSession::check_fin_transition_status(TcpSegmentDescriptor& tsd)
+{
+ if((tsd.get_seg_len() != 0) &&
+ SEQ_EQ(listener->get_fin_final_seq(), listener->r_nxt_ack))
+ {
+ listener->set_tcp_event(TcpStreamTracker::TCP_FIN_RECV_EVENT);
+ talker->set_tcp_event(TcpStreamTracker::TCP_FIN_SENT_EVENT);
+ listener->inorder_fin = true;
+ }
+}
+
int TcpSession::process_tcp_data(TcpSegmentDescriptor& tsd)
{
or (config->policy == StreamPolicy::OS_PROXY))
{
process_tcp_data(tsd);
+ //Check if all segments are received. Process FIN transition
+ check_fin_transition_status(tsd);
}
else
{
private:
void set_os_policy() override;
bool flow_exceeds_config_thresholds(TcpSegmentDescriptor&);
+ void check_fin_transition_status(TcpSegmentDescriptor&);
void process_tcp_stream(TcpSegmentDescriptor&);
int process_tcp_data(TcpSegmentDescriptor&);
void process_tcp_packet(TcpSegmentDescriptor&);
bool TcpStateCloseWait::fin_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
Flow* flow = tsd.get_flow();
+ if( trk.process_inorder_fin() )
+ {
+ trk.update_on_fin_recv(tsd);
+ return default_state_action(tsd, trk);
+ }
trk.update_tracker_ack_recv(tsd);
- if ( SEQ_GEQ(tsd.get_seg_seq(), trk.get_fin_final_seq() ) )
+
+ if ( SEQ_GT(tsd.get_seg_seq(), trk.get_fin_final_seq() ) )
{
DebugMessage(DEBUG_STREAM_STATE, "FIN beyond previous, ignoring\n");
trk.session->tel.set_tcp_event(EVENT_BAD_FIN);
Flow* flow = tsd.get_flow();
trk.update_tracker_ack_recv(tsd);
- if ( SEQ_GEQ(tsd.get_seg_seq(), trk.get_fin_final_seq() ) )
+ if ( SEQ_GT(tsd.get_seg_seq(), trk.get_fin_final_seq() ) )
{
DebugMessage(DEBUG_STREAM_STATE, "FIN beyond previous, ignoring\n");
trk.session->tel.set_tcp_event(EVENT_BAD_FIN);
{
trk.session->update_paws_timestamps(tsd);
trk.session->check_for_window_slam(tsd);
-
return true;
}
bool TcpStateEstablished::fin_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
+ TcpStreamTracker* listener = nullptr;
+
+ if ( tsd.get_pkt()->is_from_client() )
+ listener = trk.session->server;
+ else
+ listener = trk.session->client;
trk.update_on_fin_sent(tsd);
- trk.session->eof_handle(tsd.get_pkt());
- trk.set_tcp_state(TcpStreamTracker::TCP_FIN_WAIT1);
+
+ if( SEQ_EQ(tsd.get_end_seq(), (listener->r_nxt_ack + tsd.get_seg_len())) || listener->process_inorder_fin()
+ || !listener->is_segment_seq_valid(tsd) )
+ {
+ trk.session->eof_handle(tsd.get_pkt());
+ trk.set_tcp_state(TcpStreamTracker::TCP_FIN_WAIT1);
+ }
return default_state_action(tsd, trk);
}
trk.update_tracker_ack_recv(tsd);
if ( tsd.get_seg_len() > 0 )
{
- trk.session->handle_data_segment(tsd);
- trk.flush_data_on_fin_recv(tsd);
+ trk.session->handle_data_segment(tsd);
+ trk.flush_data_on_fin_recv(tsd);
}
- if ( trk.update_on_fin_recv(tsd) )
+ if( (tsd.get_end_seq() == trk.r_nxt_ack) || !trk.is_segment_seq_valid(tsd) )
{
- trk.session->update_perf_base_state(TcpStreamTracker::TCP_CLOSING);
- trk.set_tcp_state(TcpStreamTracker::TCP_CLOSE_WAIT);
+ if ( trk.update_on_fin_recv(tsd) )
+ {
+ trk.session->update_perf_base_state(TcpStreamTracker::TCP_CLOSING);
+ trk.set_tcp_state(TcpStreamTracker::TCP_CLOSE_WAIT);
+ }
+ }
+ else
+ {
+ //Out of Order FIN received
+ if ( trk.fin_final_seq == 0 )
+ trk.fin_final_seq = tsd.get_seg_seq();
}
return default_state_action(tsd, trk);
trk.session->update_timestamp_tracking(tsd);
trk.session->update_perf_base_state(TcpStreamTracker::TCP_ESTABLISHED);
trk.set_tcp_state(TcpStreamTracker::TCP_ESTABLISHED);
+ trk.r_nxt_ack = tsd.get_seg_ack();
return default_state_action(tsd, trk);
}
bool TcpStateTimeWait::fin_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
trk.update_tracker_ack_recv(tsd);
- if ( SEQ_GEQ(tsd.get_seg_seq(), trk.get_fin_final_seq() ) )
+ if ( SEQ_GT(tsd.get_seg_seq(), trk.get_fin_final_seq() ) )
{
DebugMessage(DEBUG_STREAM_STATE, "FIN beyond previous, ignoring\n");
trk.session->tel.set_tcp_event(EVENT_BAD_FIN);
// set final seq # any packet rx'ed with seq > is bad
if ( !fin_set() )
- fin_final_seq = tsd.get_end_seq() + 1;
+ fin_final_seq = tsd.get_end_seq();
return true;
}