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. ifTCP_SYN_SENT
transitioned toTCP_CLOSE
, and anSYN & 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.