Background
Relevant RFCs:
Reset Generation in RFC 793, page 36-37
RFC 2525, section 2.17
Code
data was lost when tcp_close()
is called.
} else if (data_was_unread) {
/* Unread data was tossed, zap the connection. */
NET_INC_STATS ( sock_net (sk), LINUX_MIB_TCPABORTONCLOSE);
tcp_set_state (sk, TCP_CLOSE);
tcp_send_active_reset (sk, sk->sk_allocation,
SK_RST_REASON_NOT_SPECIFIED);
Under these states, tcp_disconnect()
can send RST packets.
/* These states need RST on ABORT according to RFC793 */
static inline bool tcp_need_reset ( int state )
{
return ( 1 << state) &
(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_FIN_WAIT1 |
TCPF_FIN_WAIT2 | TCPF_SYN_RECV);
}
} else if ( tcp_need_reset (old_state) ||
(tp -> snd_nxt != tp -> write_seq &&
( 1 << old_state) & (TCPF_CLOSING | TCPF_LAST_ACK))) {
/* The last check adjusts for discrepancy of Linux wrt. RFC
* states
*/
tcp_send_active_reset (sk, gfp_any (), SK_RST_REASON_NOT_SPECIFIED);
WRITE_ONCE (sk->sk_err, ECONNRESET);
If the connection is in TCP_CLOSE
state (skb is gone), an RST packet is sent in response to any incoming segment except another reset, e.g. if TCP_SYN_SENT
transitioned to TCP_CLOSE
, and an SYN & ACK
packet was received later.
lookup:
sk = __inet_lookup_skb (net -> ipv4.tcp_death_row.hashinfo,
skb, __tcp_hdrlen (th), th -> source ,
th -> dest , sdif, & refcounted );
if ( ! sk)
goto no_tcp_socket;
# code is simplified for reading
no_tcp_socket:
drop_reason = SKB_DROP_REASON_NO_SOCKET;
if ( tcp_checksum_complete (skb)) {
drop_reason = SKB_DROP_REASON_TCP_CSUM;
} else {
tcp_v4_send_reset ( NULL , skb, sk_rst_convert_drop_reason (drop_reason));
}
Both tcp_send_active_reset()
and tcp_v4_send_reset()
/ tcp_v6_send_reset()
call trace_tcp_send_reset()
, which is defined in include/trace/events/tcp.h
.
/*
* skb of trace_tcp_send_reset is the skb that caused RST. In case of
* active reset, skb should be NULL
*/
TRACE_EVENT (tcp_send_reset,
TP_PROTO ( const struct sock * sk ,
const struct sk_buff * skb ,
const enum sk_rst_reason reason ),
TP_ARGS (sk, skb, reason),
TP_STRUCT__entry (
__field ( const void * , skbaddr)
__field ( const void * , skaddr)
__field ( int , state)
__field ( enum sk_rst_reason , reason)
__array (__u8, saddr, sizeof ( struct sockaddr_in6))
__array (__u8, daddr, sizeof ( struct sockaddr_in6))
),
TP_fast_assign (
__entry -> skbaddr = skb;
__entry -> skaddr = sk;
/* Zero means unknown state. */
__entry -> state = sk ? sk->sk_state : 0 ;
memset (__entry -> saddr , 0 , sizeof ( struct sockaddr_in6));
memset (__entry -> daddr , 0 , sizeof ( struct sockaddr_in6));
if (sk && sk_fullsock (sk)) {
const struct inet_sock * inet = inet_sk (sk);
TP_STORE_ADDR_PORTS (__entry, inet, sk);
} else if (skb) {
const struct tcphdr * th = ( const struct tcphdr * )skb->data;
/*
* We should reverse the 4-tuple of skb, so later
* it can print the right flow direction of rst.
*/
TP_STORE_ADDR_PORTS_SKB (skb, th, entry->daddr, entry->saddr);
}
__entry -> reason = reason;
),
TP_printk ( "skbaddr= %p skaddr= %p src= %p ISpc dest= %p ISpc state= %s reason= %s " ,
__entry -> skbaddr , __entry -> skaddr ,
__entry -> saddr , __entry -> daddr ,
__entry -> state ? show_tcp_state_name (__entry->state) : "UNKNOWN" ,
__print_symbolic (__entry -> reason , DEFINE_RST_REASON (FN, FNe)))
);
This version of trace_tcp_send_reset
is from Linux v6.10. On older versions of the kernel, sk_rst_reason
is not available, and RST packets on time-wait sockets or no socket are not traced.
References