Background

Relevant RFCs:

  • Reset Generation in RFC 793, page 36-37
  • RFC 2525, section 2.17

Code

  1. 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);
  1. 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);
  1. 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=%pISpc dest=%pISpc 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