Commit 709c0314 authored by Jakub Kicinski's avatar Jakub Kicinski Committed by David S. Miller
Browse files

tcp: add tracepoint for checksum errors



Add a tracepoint for capturing TCP segments with
a bad checksum. This makes it easy to identify
sources of bad frames in the fleet (e.g. machines
with faulty NICs).

It should also help tools like IOvisor's tcpdrop.py
which are used today to get detailed information
about such packets.

We don't have a socket in many cases so we must
open code the address extraction based just on
the skb.

v2: add missing export for ipv6=m

Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7466b60a
Loading
Loading
Loading
Loading
+76 −0
Original line number Diff line number Diff line
@@ -295,6 +295,82 @@ TRACE_EVENT(tcp_probe,
		  __entry->srtt, __entry->rcv_wnd, __entry->sock_cookie)
);

#define TP_STORE_ADDR_PORTS_SKB_V4(__entry, skb)			\
	do {								\
		const struct tcphdr *th = (const struct tcphdr *)skb->data; \
		struct sockaddr_in *v4 = (void *)__entry->saddr;	\
									\
		v4->sin_family = AF_INET;				\
		v4->sin_port = th->source;				\
		v4->sin_addr.s_addr = ip_hdr(skb)->saddr;		\
		v4 = (void *)__entry->daddr;				\
		v4->sin_family = AF_INET;				\
		v4->sin_port = th->dest;				\
		v4->sin_addr.s_addr = ip_hdr(skb)->daddr;		\
	} while (0)

#if IS_ENABLED(CONFIG_IPV6)

#define TP_STORE_ADDR_PORTS_SKB(__entry, skb)				\
	do {								\
		const struct iphdr *iph = ip_hdr(skb);			\
									\
		if (iph->version == 6) {				\
			const struct tcphdr *th = (const struct tcphdr *)skb->data; \
			struct sockaddr_in6 *v6 = (void *)__entry->saddr; \
									\
			v6->sin6_family = AF_INET6;			\
			v6->sin6_port = th->source;			\
			v6->sin6_addr = ipv6_hdr(skb)->saddr;		\
			v6 = (void *)__entry->daddr;			\
			v6->sin6_family = AF_INET6;			\
			v6->sin6_port = th->dest;			\
			v6->sin6_addr = ipv6_hdr(skb)->daddr;		\
		} else							\
			TP_STORE_ADDR_PORTS_SKB_V4(__entry, skb);	\
	} while (0)

#else

#define TP_STORE_ADDR_PORTS_SKB(__entry, skb)		\
	TP_STORE_ADDR_PORTS_SKB_V4(__entry, skb)

#endif

/*
 * tcp event with only skb
 */
DECLARE_EVENT_CLASS(tcp_event_skb,

	TP_PROTO(const struct sk_buff *skb),

	TP_ARGS(skb),

	TP_STRUCT__entry(
		__field(const void *, skbaddr)
		__array(__u8, saddr, sizeof(struct sockaddr_in6))
		__array(__u8, daddr, sizeof(struct sockaddr_in6))
	),

	TP_fast_assign(
		__entry->skbaddr = skb;

		memset(__entry->saddr, 0, sizeof(struct sockaddr_in6));
		memset(__entry->daddr, 0, sizeof(struct sockaddr_in6));

		TP_STORE_ADDR_PORTS_SKB(__entry, skb);
	),

	TP_printk("src=%pISpc dest=%pISpc", __entry->saddr, __entry->daddr)
);

DEFINE_EVENT(tcp_event_skb, tcp_bad_csum,

	TP_PROTO(const struct sk_buff *skb),

	TP_ARGS(skb)
);

#endif /* _TRACE_TCP_H */

/* This part must be outside protection */
+1 −0
Original line number Diff line number Diff line
@@ -60,3 +60,4 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(kfree_skb);
EXPORT_TRACEPOINT_SYMBOL_GPL(napi_poll);

EXPORT_TRACEPOINT_SYMBOL_GPL(tcp_send_reset);
EXPORT_TRACEPOINT_SYMBOL_GPL(tcp_bad_csum);
+1 −0
Original line number Diff line number Diff line
@@ -5885,6 +5885,7 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb)
	return;

csum_error:
	trace_tcp_bad_csum(skb);
	TCP_INC_STATS(sock_net(sk), TCP_MIB_CSUMERRORS);
	TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS);

+3 −0
Original line number Diff line number Diff line
@@ -1731,6 +1731,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
	return 0;

csum_err:
	trace_tcp_bad_csum(skb);
	TCP_INC_STATS(sock_net(sk), TCP_MIB_CSUMERRORS);
	TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS);
	goto discard;
@@ -1801,6 +1802,7 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)

	if (unlikely(tcp_checksum_complete(skb))) {
		bh_unlock_sock(sk);
		trace_tcp_bad_csum(skb);
		__TCP_INC_STATS(sock_net(sk), TCP_MIB_CSUMERRORS);
		__TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS);
		return true;
@@ -2098,6 +2100,7 @@ int tcp_v4_rcv(struct sk_buff *skb)

	if (tcp_checksum_complete(skb)) {
csum_error:
		trace_tcp_bad_csum(skb);
		__TCP_INC_STATS(net, TCP_MIB_CSUMERRORS);
bad_packet:
		__TCP_INC_STATS(net, TCP_MIB_INERRS);
+2 −0
Original line number Diff line number Diff line
@@ -1538,6 +1538,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
	kfree_skb(skb);
	return 0;
csum_err:
	trace_tcp_bad_csum(skb);
	TCP_INC_STATS(sock_net(sk), TCP_MIB_CSUMERRORS);
	TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS);
	goto discard;
@@ -1754,6 +1755,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb)

	if (tcp_checksum_complete(skb)) {
csum_error:
		trace_tcp_bad_csum(skb);
		__TCP_INC_STATS(net, TCP_MIB_CSUMERRORS);
bad_packet:
		__TCP_INC_STATS(net, TCP_MIB_INERRS);