// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2022 Hengqi Chen */ #include #include #include #include #include "tcplife.h" #define MAX_ENTRIES 10240 #define AF_INET 2 #define AF_INET6 10 const volatile bool filter_sport = false; const volatile bool filter_dport = false; const volatile __u16 target_sports[MAX_PORTS] = {}; const volatile __u16 target_dports[MAX_PORTS] = {}; const volatile pid_t target_pid = 0; const volatile __u16 target_family = 0; struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, MAX_ENTRIES); __type(key, struct sock *); __type(value, __u64); } birth SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, MAX_ENTRIES); __type(key, struct sock *); __type(value, struct ident); } idents SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); __uint(key_size, sizeof(__u32)); __uint(value_size, sizeof(__u32)); } events SEC(".maps"); SEC("tracepoint/sock/inet_sock_set_state") int inet_sock_set_state(struct trace_event_raw_inet_sock_set_state *args) { __u64 ts, *start, delta_us, rx_b, tx_b; struct ident ident = {}, *identp; __u16 sport, dport, family; struct event event = {}; struct tcp_sock *tp; struct sock *sk; bool found; __u32 pid; int i; if (BPF_CORE_READ(args, protocol) != IPPROTO_TCP) return 0; family = BPF_CORE_READ(args, family); if (target_family && family != target_family) return 0; sport = BPF_CORE_READ(args, sport); if (filter_sport) { found = false; for (i = 0; i < MAX_PORTS; i++) { if (!target_sports[i]) return 0; if (sport != target_sports[i]) continue; found = true; break; } if (!found) return 0; } dport = BPF_CORE_READ(args, dport); if (filter_dport) { found = false; for (i = 0; i < MAX_PORTS; i++) { if (!target_dports[i]) return 0; if (dport != target_dports[i]) continue; found = true; break; } if (!found) return 0; } sk = (struct sock *)BPF_CORE_READ(args, skaddr); if (BPF_CORE_READ(args, newstate) < TCP_FIN_WAIT1) { ts = bpf_ktime_get_ns(); bpf_map_update_elem(&birth, &sk, &ts, BPF_ANY); } if (BPF_CORE_READ(args, newstate) == TCP_SYN_SENT || BPF_CORE_READ(args, newstate) == TCP_LAST_ACK) { pid = bpf_get_current_pid_tgid() >> 32; if (target_pid && pid != target_pid) return 0; ident.pid = pid; bpf_get_current_comm(ident.comm, sizeof(ident.comm)); bpf_map_update_elem(&idents, &sk, &ident, BPF_ANY); } if (BPF_CORE_READ(args, newstate) != TCP_CLOSE) return 0; start = bpf_map_lookup_elem(&birth, &sk); if (!start) { bpf_map_delete_elem(&idents, &sk); return 0; } ts = bpf_ktime_get_ns(); delta_us = (ts - *start) / 1000; identp = bpf_map_lookup_elem(&idents, &sk); pid = identp ? identp->pid : bpf_get_current_pid_tgid() >> 32; if (target_pid && pid != target_pid) goto cleanup; tp = (struct tcp_sock *)sk; rx_b = BPF_CORE_READ(tp, bytes_received); tx_b = BPF_CORE_READ(tp, bytes_acked); event.ts_us = ts / 1000; event.span_us = delta_us; event.rx_b = rx_b; event.tx_b = tx_b; event.pid = pid; event.sport = sport; event.dport = dport; event.family = family; if (!identp) bpf_get_current_comm(event.comm, sizeof(event.comm)); else bpf_probe_read_kernel(event.comm, sizeof(event.comm), (void *)identp->comm); if (family == AF_INET) { bpf_probe_read_kernel(&event.saddr, sizeof(args->saddr), BPF_CORE_READ(args, saddr)); bpf_probe_read_kernel(&event.daddr, sizeof(args->daddr), BPF_CORE_READ(args, daddr)); } else { /* AF_INET6 */ bpf_probe_read_kernel(&event.saddr, sizeof(args->saddr_v6), BPF_CORE_READ(args, saddr_v6)); bpf_probe_read_kernel(&event.daddr, sizeof(args->daddr_v6), BPF_CORE_READ(args, daddr_v6)); } bpf_perf_event_output(args, &events, BPF_F_CURRENT_CPU, &event, sizeof(event)); cleanup: bpf_map_delete_elem(&birth, &sk); bpf_map_delete_elem(&idents, &sk); return 0; } char LICENSE[] SEC("license") = "GPL";