github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/profile/tcprtt/tracer/bpf/tcprtt.bpf.c (about) 1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (c) 2021 Wenbo Zhang 3 // Copyright (c) 2023 The Inspektor Gadget authors 4 #include <vmlinux.h> 5 #include <bpf/bpf_helpers.h> 6 #include <bpf/bpf_core_read.h> 7 #include <bpf/bpf_tracing.h> 8 #include <bpf/bpf_endian.h> 9 #include "tcprtt.h" 10 #include <gadget/bits.bpf.h> 11 #include <gadget/maps.bpf.h> 12 13 /* Taken from kernel include/linux/socket.h. */ 14 #define AF_INET 2 /* IP version 4 */ 15 #define AF_INET6 10 /* IP version 6 */ 16 17 const volatile bool targ_laddr_hist = false; 18 const volatile bool targ_raddr_hist = false; 19 const volatile __u16 targ_sport = 0; 20 const volatile __u16 targ_dport = 0; 21 const volatile __u32 targ_saddr = 0; 22 const volatile __u32 targ_daddr = 0; 23 const volatile __u8 targ_saddr_v6[IPV6_LEN] = {}; 24 const volatile __u8 targ_daddr_v6[IPV6_LEN] = {}; 25 const volatile bool targ_ms = false; 26 27 #define MAX_ENTRIES 10240 28 29 struct { 30 __uint(type, BPF_MAP_TYPE_HASH); 31 __uint(max_entries, MAX_ENTRIES); 32 __type(key, struct hist_key); 33 __type(value, struct hist); 34 } hists SEC(".maps"); 35 36 static struct hist zero; 37 38 /* 39 * We cannot use the following: 40 * __builtin_memcmp(targ_*addr_v6, *, sizeof(targ_*addr_v6)); 41 * Indeed, by using the builtin, we would discard the volatile qualifier of 42 * targ_*addr_v6, so the compiler would optimize it and replaces the call 43 * with 0. 44 * So, using the volatile qualifier ensures this function is called at runtime. 45 */ 46 static bool inline ipv6_is_not_zero(const volatile __u8 addr[IPV6_LEN]) 47 { 48 for (int i = 0; i < IPV6_LEN; i++) 49 if (addr[i]) 50 return true; 51 return false; 52 } 53 54 static bool inline ipv6_are_different(const volatile __u8 a[IPV6_LEN], 55 const __u8 b[IPV6_LEN]) 56 { 57 for (int i = 0; i < IPV6_LEN; i++) 58 if (a[i] != b[i]) 59 return true; 60 return false; 61 } 62 63 static int handle_tcp_rcv_established(struct sock *sk) 64 { 65 const struct inet_sock *inet = (struct inet_sock *)(sk); 66 struct tcp_sock *ts; 67 struct hist *histp; 68 struct hist_key key = {}; 69 u64 slot; 70 u32 srtt; 71 72 if (targ_sport && targ_sport != BPF_CORE_READ(inet, inet_sport)) 73 return 0; 74 75 if (targ_dport && 76 targ_dport != BPF_CORE_READ(sk, __sk_common.skc_dport)) 77 return 0; 78 79 key.family = BPF_CORE_READ(sk, __sk_common.skc_family); 80 switch (key.family) { 81 case AF_INET: 82 /* If we set any of IPv6 address, we do not care about IPv4 ones. */ 83 if (ipv6_is_not_zero(targ_saddr_v6) || 84 ipv6_is_not_zero(targ_daddr_v6)) 85 return 0; 86 87 if (targ_saddr && targ_saddr != BPF_CORE_READ(inet, inet_saddr)) 88 return 0; 89 90 if (targ_daddr && 91 targ_daddr != BPF_CORE_READ(sk, __sk_common.skc_daddr)) 92 return 0; 93 94 break; 95 case AF_INET6: 96 /* 97 * Reciprocal of the above: if we set any of IPv4 address, we do not care 98 * about IPv6 ones. 99 */ 100 if (targ_saddr || targ_daddr) 101 return 0; 102 103 if (ipv6_is_not_zero(targ_saddr_v6) && 104 ipv6_are_different(targ_saddr_v6, 105 BPF_CORE_READ(inet, pinet6, 106 saddr.in6_u.u6_addr8))) 107 return 0; 108 109 if (ipv6_is_not_zero(targ_daddr_v6) && 110 ipv6_are_different( 111 targ_daddr_v6, 112 BPF_CORE_READ( 113 sk, 114 __sk_common.skc_v6_daddr.in6_u.u6_addr8))) 115 return 0; 116 117 break; 118 default: 119 return 0; 120 } 121 122 if (targ_laddr_hist) { 123 if (key.family == AF_INET6) 124 bpf_probe_read_kernel( 125 &key.addr, sizeof(key.addr), 126 BPF_CORE_READ(inet, pinet6, 127 saddr.in6_u.u6_addr8)); 128 else 129 /* 130 * It is fine to use "->" operator with bpf_probe_read_kernel() as we are 131 * using vmlinux.h which defines struct with preserve_access_index 132 * attribute, see: 133 * https://nakryiko.com/posts/bpf-core-reference-guide/#defining-own-co-re-relocatable-type-definitions 134 */ 135 bpf_probe_read_kernel(&key.addr, 136 sizeof(inet->inet_saddr), 137 &inet->inet_saddr); 138 } else if (targ_raddr_hist) { 139 if (key.family == AF_INET6) 140 bpf_probe_read_kernel( 141 &key.addr, sizeof(key.addr), 142 BPF_CORE_READ(sk, __sk_common.skc_v6_daddr.in6_u 143 .u6_addr8)); 144 else 145 bpf_probe_read_kernel(&key.addr, 146 sizeof(sk->__sk_common.skc_daddr), 147 &sk->__sk_common.skc_daddr); 148 } else { 149 key.family = 0; 150 } 151 152 histp = bpf_map_lookup_or_try_init(&hists, &key, &zero); 153 if (!histp) 154 return 0; 155 ts = (struct tcp_sock *)(sk); 156 srtt = BPF_CORE_READ(ts, srtt_us) >> 3; 157 if (targ_ms) 158 srtt /= 1000U; 159 slot = log2l(srtt); 160 if (slot >= MAX_SLOTS) 161 slot = MAX_SLOTS - 1; 162 __sync_fetch_and_add(&histp->slots[slot], 1); 163 __sync_fetch_and_add(&histp->latency, srtt); 164 __sync_fetch_and_add(&histp->cnt, 1); 165 return 0; 166 } 167 168 SEC("kprobe/tcp_rcv_established") 169 int BPF_KPROBE(ig_tcprcvest_kp, struct sock *sk) 170 { 171 return handle_tcp_rcv_established(sk); 172 } 173 174 // Enable once https://github.com/inspektor-gadget/inspektor-gadget/issues/1566 is fixed. 175 // SEC("fentry/tcp_rcv_established") 176 // int BPF_PROG(ig_tcprcvest_fe, struct sock *sk) 177 // { 178 // return handle_tcp_rcv_established(sk); 179 // } 180 181 char LICENSE[] SEC("license") = "Dual BSD/GPL";