// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2020 Facebook */ #include "bpf_iter.h" #include "bpf_tracing_net.h" #include <bpf/bpf_helpers.h> #include <bpf/bpf_endian.h> char _license[] SEC("license") = "GPL"; static int hlist_unhashed_lockless(const struct hlist_node *h) { return !(h->pprev); } static int timer_pending(const struct timer_list * timer) { return !hlist_unhashed_lockless(&timer->entry); } extern unsigned CONFIG_HZ __kconfig; #define USER_HZ 100 #define NSEC_PER_SEC 1000000000ULL static clock_t jiffies_to_clock_t(unsigned long x) { /* The implementation here tailored to a particular * setting of USER_HZ. */ u64 tick_nsec = (NSEC_PER_SEC + CONFIG_HZ/2) / CONFIG_HZ; u64 user_hz_nsec = NSEC_PER_SEC / USER_HZ; if ((tick_nsec % user_hz_nsec) == 0) { if (CONFIG_HZ < USER_HZ) return x * (USER_HZ / CONFIG_HZ); else return x / (CONFIG_HZ / USER_HZ); } return x * tick_nsec/user_hz_nsec; } static clock_t jiffies_delta_to_clock_t(long delta) { if (delta <= 0) return 0; return jiffies_to_clock_t(delta); } static long sock_i_ino(const struct sock *sk) { const struct socket *sk_socket = sk->sk_socket; const struct inode *inode; unsigned long ino; if (!sk_socket) return 0; inode = &container_of(sk_socket, struct socket_alloc, socket)->vfs_inode; bpf_probe_read_kernel(&ino, sizeof(ino), &inode->i_ino); return ino; } static bool inet_csk_in_pingpong_mode(const struct inet_connection_sock *icsk) { return icsk->icsk_ack.pingpong >= TCP_PINGPONG_THRESH; } static bool tcp_in_initial_slowstart(const struct tcp_sock *tcp) { return tcp->snd_ssthresh >= TCP_INFINITE_SSTHRESH; } static int dump_tcp6_sock(struct seq_file *seq, struct tcp6_sock *tp, uid_t uid, __u32 seq_num) { const struct inet_connection_sock *icsk; const struct fastopen_queue *fastopenq; const struct in6_addr *dest, *src; const struct inet_sock *inet; unsigned long timer_expires; const struct sock *sp; __u16 destp, srcp; int timer_active; int rx_queue; int state; icsk = &tp->tcp.inet_conn; inet = &icsk->icsk_inet; sp = &inet->sk; fastopenq = &icsk->icsk_accept_queue.fastopenq; dest = &sp->sk_v6_daddr; src = &sp->sk_v6_rcv_saddr; destp = bpf_ntohs(inet->inet_dport); srcp = bpf_ntohs(inet->inet_sport); if (icsk->icsk_pending == ICSK_TIME_RETRANS || icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT || icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) { timer_active = 1; timer_expires = icsk->icsk_timeout; } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) { timer_active = 4; timer_expires = icsk->icsk_timeout; } else if (timer_pending(&sp->sk_timer)) { timer_active = 2; timer_expires = sp->sk_timer.expires; } else { timer_active = 0; timer_expires = bpf_jiffies64(); } state = sp->sk_state; if (state == TCP_LISTEN) { rx_queue = sp->sk_ack_backlog; } else { rx_queue = tp->tcp.rcv_nxt - tp->tcp.copied_seq; if (rx_queue < 0) rx_queue = 0; } BPF_SEQ_PRINTF(seq, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X ", seq_num, src->s6_addr32[0], src->s6_addr32[1], src->s6_addr32[2], src->s6_addr32[3], srcp, dest->s6_addr32[0], dest->s6_addr32[1], dest->s6_addr32[2], dest->s6_addr32[3], destp); BPF_SEQ_PRINTF(seq, "%02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d ", state, tp->tcp.write_seq - tp->tcp.snd_una, rx_queue, timer_active, jiffies_delta_to_clock_t(timer_expires - bpf_jiffies64()), icsk->icsk_retransmits, uid, icsk->icsk_probes_out, sock_i_ino(sp), sp->sk_refcnt.refs.counter); BPF_SEQ_PRINTF(seq, "%pK %lu %lu %u %u %d\n", tp, jiffies_to_clock_t(icsk->icsk_rto), jiffies_to_clock_t(icsk->icsk_ack.ato), (icsk->icsk_ack.quick << 1) | inet_csk_in_pingpong_mode(icsk), tp->tcp.snd_cwnd, state == TCP_LISTEN ? fastopenq->max_qlen : (tcp_in_initial_slowstart(&tp->tcp) ? -1 : tp->tcp.snd_ssthresh) ); return 0; } static int dump_tw_sock(struct seq_file *seq, struct tcp_timewait_sock *ttw, uid_t uid, __u32 seq_num) { struct inet_timewait_sock *tw = &ttw->tw_sk; const struct in6_addr *dest, *src; __u16 destp, srcp; long delta; delta = tw->tw_timer.expires - bpf_jiffies64(); dest = &tw->tw_v6_daddr; src = &tw->tw_v6_rcv_saddr; destp = bpf_ntohs(tw->tw_dport); srcp = bpf_ntohs(tw->tw_sport); BPF_SEQ_PRINTF(seq, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X ", seq_num, src->s6_addr32[0], src->s6_addr32[1], src->s6_addr32[2], src->s6_addr32[3], srcp, dest->s6_addr32[0], dest->s6_addr32[1], dest->s6_addr32[2], dest->s6_addr32[3], destp); BPF_SEQ_PRINTF(seq, "%02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK\n", tw->tw_substate, 0, 0, 3, jiffies_delta_to_clock_t(delta), 0, 0, 0, 0, tw->tw_refcnt.refs.counter, tw); return 0; } static int dump_req_sock(struct seq_file *seq, struct tcp_request_sock *treq, uid_t uid, __u32 seq_num) { struct inet_request_sock *irsk = &treq->req; struct request_sock *req = &irsk->req; struct in6_addr *src, *dest; long ttd; ttd = req->rsk_timer.expires - bpf_jiffies64(); src = &irsk->ir_v6_loc_addr; dest = &irsk->ir_v6_rmt_addr; if (ttd < 0) ttd = 0; BPF_SEQ_PRINTF(seq, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X ", seq_num, src->s6_addr32[0], src->s6_addr32[1], src->s6_addr32[2], src->s6_addr32[3], irsk->ir_num, dest->s6_addr32[0], dest->s6_addr32[1], dest->s6_addr32[2], dest->s6_addr32[3], bpf_ntohs(irsk->ir_rmt_port)); BPF_SEQ_PRINTF(seq, "%02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK\n", TCP_SYN_RECV, 0, 0, 1, jiffies_to_clock_t(ttd), req->num_timeout, uid, 0, 0, 0, req); return 0; } SEC("iter/tcp") int dump_tcp6(struct bpf_iter__tcp *ctx) { struct sock_common *sk_common = ctx->sk_common; struct seq_file *seq = ctx->meta->seq; struct tcp_timewait_sock *tw; struct tcp_request_sock *req; struct tcp6_sock *tp; uid_t uid = ctx->uid; __u32 seq_num; if (sk_common == (void *)0) return 0; seq_num = ctx->meta->seq_num; if (seq_num == 0) BPF_SEQ_PRINTF(seq, " sl " "local_address " "remote_address " "st tx_queue rx_queue tr tm->when retrnsmt" " uid timeout inode\n"); if (sk_common->skc_family != AF_INET6) return 0; tp = bpf_skc_to_tcp6_sock(sk_common); if (tp) return dump_tcp6_sock(seq, tp, uid, seq_num); tw = bpf_skc_to_tcp_timewait_sock(sk_common); if (tw) return dump_tw_sock(seq, tw, uid, seq_num); req = bpf_skc_to_tcp_request_sock(sk_common); if (req) return dump_req_sock(seq, req, uid, seq_num); return 0; }