tcp: add tracepoint for checksum errors
authorJakub Kicinski <kuba@kernel.org>
Fri, 14 May 2021 20:04:25 +0000 (13:04 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 14 May 2021 22:26:03 +0000 (15:26 -0700)
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: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/trace/events/tcp.h
net/core/net-traces.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv6/tcp_ipv6.c

index ba94857eea11ee5299e40a7d07fb0f4fe79d2ca4..521059d8dc0a67087527e8a9aa2338e81d86bd37 100644 (file)
@@ -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 */
index 283ddb2dbc7d3c2080a4252e99692cb235929b20..c40cd8dd75c7f989b007f989a3bf0c13b9b7dc2f 100644 (file)
@@ -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);
index 4cf4dd532d1c65bba417a66ba6b7783491b6380a..cd52ce0a2a85059f27c24bb8685db900b1721c40 100644 (file)
@@ -5885,6 +5885,7 @@ step5:
        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);
 
index 312184cead57ed889a1bc73484df43112b20fe76..4f5b68a90be96bdc4be0753007a0fcea50044fd3 100644 (file)
@@ -1731,6 +1731,7 @@ discard:
        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 @@ no_tcp_socket:
 
        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);
index 5f47c0b6e3de8e0d59a9f3184b16b6cf6c6a9316..4435fa342e7aa756bd426ffe051ade7d84ac5523 100644 (file)
@@ -1538,6 +1538,7 @@ discard:
        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 @@ no_tcp_socket:
 
        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);