Skip to content

Commit

Permalink
net/tcp: Add tcp-md5 and tcp-ao tracepoints
Browse files Browse the repository at this point in the history
Instead of forcing userspace to parse dmesg (that's what currently is
happening, at least in codebase of my current company), provide a better
way, that can be enabled/disabled in runtime.

Currently, there are already tcp events, add hashing related ones there,
too. Rasdaemon currently exercises net_dev_xmit_timeout,
devlink_health_report, but it'll be trivial to teach it to deal with
failed hashes. Otherwise, BGP may trace/log them itself. Especially
exciting for possible investigations is key rotation (RNext_key
requests).

Suggested-by: Jakub Kicinski <[email protected]>
Signed-off-by: Dmitry Safonov <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
0x7f454c46 authored and davem330 committed Jun 12, 2024
1 parent 811efc0 commit 96be3dc
Show file tree
Hide file tree
Showing 5 changed files with 355 additions and 2 deletions.
317 changes: 317 additions & 0 deletions include/trace/events/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,323 @@ TRACE_EVENT(tcp_cong_state_set,
__entry->cong_state)
);

DECLARE_EVENT_CLASS(tcp_hash_event,

TP_PROTO(const struct sock *sk, const struct sk_buff *skb),

TP_ARGS(sk, skb),

TP_STRUCT__entry(
__field(__u64, net_cookie)
__field(const void *, skbaddr)
__field(const void *, skaddr)
__field(int, state)

/* sockaddr_in6 is always bigger than sockaddr_in */
__array(__u8, saddr, sizeof(struct sockaddr_in6))
__array(__u8, daddr, sizeof(struct sockaddr_in6))
__field(int, l3index)

__field(__u16, sport)
__field(__u16, dport)
__field(__u16, family)

__field(bool, fin)
__field(bool, syn)
__field(bool, rst)
__field(bool, psh)
__field(bool, ack)
),

TP_fast_assign(
const struct tcphdr *th = (const struct tcphdr *)skb->data;

__entry->net_cookie = sock_net(sk)->net_cookie;
__entry->skbaddr = skb;
__entry->skaddr = sk;
__entry->state = sk->sk_state;

memset(__entry->saddr, 0, sizeof(struct sockaddr_in6));
memset(__entry->daddr, 0, sizeof(struct sockaddr_in6));
TP_STORE_ADDR_PORTS_SKB(skb, th, __entry->saddr, __entry->daddr);
__entry->l3index = inet_sdif(skb) ? inet_iif(skb) : 0;

/* For filtering use */
__entry->sport = ntohs(th->source);
__entry->dport = ntohs(th->dest);
__entry->family = sk->sk_family;

__entry->fin = th->fin;
__entry->syn = th->syn;
__entry->rst = th->rst;
__entry->psh = th->psh;
__entry->ack = th->ack;
),

TP_printk("net=%llu state=%s family=%s src=%pISpc dest=%pISpc L3index=%d [%c%c%c%c%c]",
__entry->net_cookie,
show_tcp_state_name(__entry->state),
show_family_name(__entry->family),
__entry->saddr, __entry->daddr,
__entry->l3index,
__entry->fin ? 'F' : ' ',
__entry->syn ? 'S' : ' ',
__entry->rst ? 'R' : ' ',
__entry->psh ? 'P' : ' ',
__entry->ack ? '.' : ' ')
);

DEFINE_EVENT(tcp_hash_event, tcp_hash_bad_header,

TP_PROTO(const struct sock *sk, const struct sk_buff *skb),
TP_ARGS(sk, skb)
);

DEFINE_EVENT(tcp_hash_event, tcp_hash_md5_required,

TP_PROTO(const struct sock *sk, const struct sk_buff *skb),
TP_ARGS(sk, skb)
);

DEFINE_EVENT(tcp_hash_event, tcp_hash_md5_unexpected,

TP_PROTO(const struct sock *sk, const struct sk_buff *skb),
TP_ARGS(sk, skb)
);

DEFINE_EVENT(tcp_hash_event, tcp_hash_md5_mismatch,

TP_PROTO(const struct sock *sk, const struct sk_buff *skb),
TP_ARGS(sk, skb)
);

DEFINE_EVENT(tcp_hash_event, tcp_hash_ao_required,

TP_PROTO(const struct sock *sk, const struct sk_buff *skb),
TP_ARGS(sk, skb)
);

DECLARE_EVENT_CLASS(tcp_ao_event,

TP_PROTO(const struct sock *sk, const struct sk_buff *skb,
const __u8 keyid, const __u8 rnext, const __u8 maclen),

TP_ARGS(sk, skb, keyid, rnext, maclen),

TP_STRUCT__entry(
__field(__u64, net_cookie)
__field(const void *, skbaddr)
__field(const void *, skaddr)
__field(int, state)

/* sockaddr_in6 is always bigger than sockaddr_in */
__array(__u8, saddr, sizeof(struct sockaddr_in6))
__array(__u8, daddr, sizeof(struct sockaddr_in6))
__field(int, l3index)

__field(__u16, sport)
__field(__u16, dport)
__field(__u16, family)

__field(bool, fin)
__field(bool, syn)
__field(bool, rst)
__field(bool, psh)
__field(bool, ack)

__field(__u8, keyid)
__field(__u8, rnext)
__field(__u8, maclen)
),

TP_fast_assign(
const struct tcphdr *th = (const struct tcphdr *)skb->data;

__entry->net_cookie = sock_net(sk)->net_cookie;
__entry->skbaddr = skb;
__entry->skaddr = sk;
__entry->state = sk->sk_state;

memset(__entry->saddr, 0, sizeof(struct sockaddr_in6));
memset(__entry->daddr, 0, sizeof(struct sockaddr_in6));
TP_STORE_ADDR_PORTS_SKB(skb, th, __entry->saddr, __entry->daddr);
__entry->l3index = inet_sdif(skb) ? inet_iif(skb) : 0;

/* For filtering use */
__entry->sport = ntohs(th->source);
__entry->dport = ntohs(th->dest);
__entry->family = sk->sk_family;

__entry->fin = th->fin;
__entry->syn = th->syn;
__entry->rst = th->rst;
__entry->psh = th->psh;
__entry->ack = th->ack;

__entry->keyid = keyid;
__entry->rnext = rnext;
__entry->maclen = maclen;
),

TP_printk("net=%llu state=%s family=%s src=%pISpc dest=%pISpc L3index=%d [%c%c%c%c%c] keyid=%u rnext=%u maclen=%u",
__entry->net_cookie,
show_tcp_state_name(__entry->state),
show_family_name(__entry->family),
__entry->saddr, __entry->daddr,
__entry->l3index,
__entry->fin ? 'F' : ' ',
__entry->syn ? 'S' : ' ',
__entry->rst ? 'R' : ' ',
__entry->psh ? 'P' : ' ',
__entry->ack ? '.' : ' ',
__entry->keyid, __entry->rnext, __entry->maclen)
);

DEFINE_EVENT(tcp_ao_event, tcp_ao_handshake_failure,
TP_PROTO(const struct sock *sk, const struct sk_buff *skb,
const __u8 keyid, const __u8 rnext, const __u8 maclen),
TP_ARGS(sk, skb, keyid, rnext, maclen)
);

DEFINE_EVENT(tcp_ao_event, tcp_ao_wrong_maclen,
TP_PROTO(const struct sock *sk, const struct sk_buff *skb,
const __u8 keyid, const __u8 rnext, const __u8 maclen),
TP_ARGS(sk, skb, keyid, rnext, maclen)
);

DEFINE_EVENT(tcp_ao_event, tcp_ao_mismatch,
TP_PROTO(const struct sock *sk, const struct sk_buff *skb,
const __u8 keyid, const __u8 rnext, const __u8 maclen),
TP_ARGS(sk, skb, keyid, rnext, maclen)
);

DEFINE_EVENT(tcp_ao_event, tcp_ao_key_not_found,
TP_PROTO(const struct sock *sk, const struct sk_buff *skb,
const __u8 keyid, const __u8 rnext, const __u8 maclen),
TP_ARGS(sk, skb, keyid, rnext, maclen)
);

DEFINE_EVENT(tcp_ao_event, tcp_ao_rnext_request,
TP_PROTO(const struct sock *sk, const struct sk_buff *skb,
const __u8 keyid, const __u8 rnext, const __u8 maclen),
TP_ARGS(sk, skb, keyid, rnext, maclen)
);

DECLARE_EVENT_CLASS(tcp_ao_event_sk,

TP_PROTO(const struct sock *sk, const __u8 keyid, const __u8 rnext),

TP_ARGS(sk, keyid, rnext),

TP_STRUCT__entry(
__field(__u64, net_cookie)
__field(const void *, skaddr)
__field(int, state)

/* sockaddr_in6 is always bigger than sockaddr_in */
__array(__u8, saddr, sizeof(struct sockaddr_in6))
__array(__u8, daddr, sizeof(struct sockaddr_in6))

__field(__u16, sport)
__field(__u16, dport)
__field(__u16, family)

__field(__u8, keyid)
__field(__u8, rnext)
),

TP_fast_assign(
const struct inet_sock *inet = inet_sk(sk);

__entry->net_cookie = sock_net(sk)->net_cookie;
__entry->skaddr = sk;
__entry->state = sk->sk_state;

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

/* For filtering use */
__entry->sport = ntohs(inet->inet_sport);
__entry->dport = ntohs(inet->inet_dport);
__entry->family = sk->sk_family;

__entry->keyid = keyid;
__entry->rnext = rnext;
),

TP_printk("net=%llu state=%s family=%s src=%pISpc dest=%pISpc keyid=%u rnext=%u",
__entry->net_cookie,
show_tcp_state_name(__entry->state),
show_family_name(__entry->family),
__entry->saddr, __entry->daddr,
__entry->keyid, __entry->rnext)
);

DEFINE_EVENT(tcp_ao_event_sk, tcp_ao_synack_no_key,
TP_PROTO(const struct sock *sk, const __u8 keyid, const __u8 rnext),
TP_ARGS(sk, keyid, rnext)
);

DECLARE_EVENT_CLASS(tcp_ao_event_sne,

TP_PROTO(const struct sock *sk, __u32 new_sne),

TP_ARGS(sk, new_sne),

TP_STRUCT__entry(
__field(__u64, net_cookie)
__field(const void *, skaddr)
__field(int, state)

/* sockaddr_in6 is always bigger than sockaddr_in */
__array(__u8, saddr, sizeof(struct sockaddr_in6))
__array(__u8, daddr, sizeof(struct sockaddr_in6))

__field(__u16, sport)
__field(__u16, dport)
__field(__u16, family)

__field(__u32, new_sne)
),

TP_fast_assign(
const struct inet_sock *inet = inet_sk(sk);

__entry->net_cookie = sock_net(sk)->net_cookie;
__entry->skaddr = sk;
__entry->state = sk->sk_state;

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

/* For filtering use */
__entry->sport = ntohs(inet->inet_sport);
__entry->dport = ntohs(inet->inet_dport);
__entry->family = sk->sk_family;

__entry->new_sne = new_sne;
),

TP_printk("net=%llu state=%s family=%s src=%pISpc dest=%pISpc sne=%u",
__entry->net_cookie,
show_tcp_state_name(__entry->state),
show_family_name(__entry->family),
__entry->saddr, __entry->daddr,
__entry->new_sne)
);

DEFINE_EVENT(tcp_ao_event_sne, tcp_ao_snd_sne_update,
TP_PROTO(const struct sock *sk, __u32 new_sne),
TP_ARGS(sk, new_sne)
);

DEFINE_EVENT(tcp_ao_event_sne, tcp_ao_rcv_sne_update,
TP_PROTO(const struct sock *sk, __u32 new_sne),
TP_ARGS(sk, new_sne)
);

#endif /* _TRACE_TCP_H */

/* This part must be outside protection */
Expand Down
17 changes: 17 additions & 0 deletions net/ipv4/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@
#include <asm/ioctls.h>
#include <net/busy_poll.h>
#include <net/hotdata.h>
#include <trace/events/tcp.h>
#include <net/rps.h>

/* Track pending CMSGs. */
Expand Down Expand Up @@ -4484,6 +4485,7 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
if (!key && hash_location) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED);
tcp_hash_fail("Unexpected MD5 Hash found", family, skb, "");
trace_tcp_hash_md5_unexpected(sk, skb);
return SKB_DROP_REASON_TCP_MD5UNEXPECTED;
}

Expand Down Expand Up @@ -4513,6 +4515,7 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
l3index);
}
}
trace_tcp_hash_md5_mismatch(sk, skb);
return SKB_DROP_REASON_TCP_MD5FAILURE;
}
return SKB_NOT_DROPPED_YET;
Expand Down Expand Up @@ -4544,15 +4547,27 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req,
if (tcp_parse_auth_options(th, &md5_location, &aoh)) {
tcp_hash_fail("TCP segment has incorrect auth options set",
family, skb, "");
trace_tcp_hash_bad_header(sk, skb);
return SKB_DROP_REASON_TCP_AUTH_HDR;
}

if (req) {
if (tcp_rsk_used_ao(req) != !!aoh) {
u8 keyid, rnext, maclen;

if (aoh) {
keyid = aoh->keyid;
rnext = aoh->rnext_keyid;
maclen = tcp_ao_hdr_maclen(aoh);
} else {
keyid = rnext = maclen = 0;
}

NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD);
tcp_hash_fail("TCP connection can't start/end using TCP-AO",
family, skb, "%s",
!aoh ? "missing AO" : "AO signed");
trace_tcp_ao_handshake_failure(sk, skb, keyid, rnext, maclen);
return SKB_DROP_REASON_TCP_AOFAILURE;
}
}
Expand All @@ -4572,12 +4587,14 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req,
if (tcp_ao_required(sk, saddr, family, l3index, true)) {
tcp_hash_fail("AO hash is required, but not found",
family, skb, "L3 index %d", l3index);
trace_tcp_hash_ao_required(sk, skb);
return SKB_DROP_REASON_TCP_AONOTFOUND;
}
if (unlikely(tcp_md5_do_lookup(sk, l3index, saddr, family))) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND);
tcp_hash_fail("MD5 Hash not found",
family, skb, "L3 index %d", l3index);
trace_tcp_hash_md5_required(sk, skb);
return SKB_DROP_REASON_TCP_MD5NOTFOUND;
}
return SKB_NOT_DROPPED_YET;
Expand Down
Loading

0 comments on commit 96be3dc

Please sign in to comment.