test_tcp_estats.c (7137B)
1/* Copyright (c) 2017 Facebook 2 * 3 * This program is free software; you can redistribute it and/or 4 * modify it under the terms of version 2 of the GNU General Public 5 * License as published by the Free Software Foundation. 6 */ 7 8/* This program shows clang/llvm is able to generate code pattern 9 * like: 10 * _tcp_send_active_reset: 11 * 0: bf 16 00 00 00 00 00 00 r6 = r1 12 * ...... 13 * 335: b7 01 00 00 0f 00 00 00 r1 = 15 14 * 336: 05 00 48 00 00 00 00 00 goto 72 15 * 16 * LBB0_3: 17 * 337: b7 01 00 00 01 00 00 00 r1 = 1 18 * 338: 63 1a d0 ff 00 00 00 00 *(u32 *)(r10 - 48) = r1 19 * 408: b7 01 00 00 03 00 00 00 r1 = 3 20 * 21 * LBB0_4: 22 * 409: 71 a2 fe ff 00 00 00 00 r2 = *(u8 *)(r10 - 2) 23 * 410: bf a7 00 00 00 00 00 00 r7 = r10 24 * 411: 07 07 00 00 b8 ff ff ff r7 += -72 25 * 412: bf 73 00 00 00 00 00 00 r3 = r7 26 * 413: 0f 13 00 00 00 00 00 00 r3 += r1 27 * 414: 73 23 2d 00 00 00 00 00 *(u8 *)(r3 + 45) = r2 28 * 29 * From the above code snippet, the code generated by the compiler 30 * is reasonable. The "r1" is assigned to different values in basic 31 * blocks "_tcp_send_active_reset" and "LBB0_3", and used in "LBB0_4". 32 * The verifier should be able to handle such code patterns. 33 */ 34#include <string.h> 35#include <linux/bpf.h> 36#include <linux/ipv6.h> 37#include <linux/version.h> 38#include <sys/socket.h> 39#include <bpf/bpf_helpers.h> 40 41#define _(P) ({typeof(P) val = 0; bpf_probe_read_kernel(&val, sizeof(val), &P); val;}) 42#define TCP_ESTATS_MAGIC 0xBAADBEEF 43 44/* This test case needs "sock" and "pt_regs" data structure. 45 * Recursively, "sock" needs "sock_common" and "inet_sock". 46 * However, this is a unit test case only for 47 * verifier purpose without bpf program execution. 48 * We can safely mock much simpler data structures, basically 49 * only taking the necessary fields from kernel headers. 50 */ 51typedef __u32 __bitwise __portpair; 52typedef __u64 __bitwise __addrpair; 53 54struct sock_common { 55 unsigned short skc_family; 56 union { 57 __addrpair skc_addrpair; 58 struct { 59 __be32 skc_daddr; 60 __be32 skc_rcv_saddr; 61 }; 62 }; 63 union { 64 __portpair skc_portpair; 65 struct { 66 __be16 skc_dport; 67 __u16 skc_num; 68 }; 69 }; 70 struct in6_addr skc_v6_daddr; 71 struct in6_addr skc_v6_rcv_saddr; 72}; 73 74struct sock { 75 struct sock_common __sk_common; 76#define sk_family __sk_common.skc_family 77#define sk_v6_daddr __sk_common.skc_v6_daddr 78#define sk_v6_rcv_saddr __sk_common.skc_v6_rcv_saddr 79}; 80 81struct inet_sock { 82 struct sock sk; 83#define inet_daddr sk.__sk_common.skc_daddr 84#define inet_dport sk.__sk_common.skc_dport 85 __be32 inet_saddr; 86 __be16 inet_sport; 87}; 88 89struct pt_regs { 90 long di; 91}; 92 93static inline struct inet_sock *inet_sk(const struct sock *sk) 94{ 95 return (struct inet_sock *)sk; 96} 97 98/* Define various data structures for state recording. 99 * Some fields are not used due to test simplification. 100 */ 101enum tcp_estats_addrtype { 102 TCP_ESTATS_ADDRTYPE_IPV4 = 1, 103 TCP_ESTATS_ADDRTYPE_IPV6 = 2 104}; 105 106enum tcp_estats_event_type { 107 TCP_ESTATS_ESTABLISH, 108 TCP_ESTATS_PERIODIC, 109 TCP_ESTATS_TIMEOUT, 110 TCP_ESTATS_RETRANSMIT_TIMEOUT, 111 TCP_ESTATS_RETRANSMIT_OTHER, 112 TCP_ESTATS_SYN_RETRANSMIT, 113 TCP_ESTATS_SYNACK_RETRANSMIT, 114 TCP_ESTATS_TERM, 115 TCP_ESTATS_TX_RESET, 116 TCP_ESTATS_RX_RESET, 117 TCP_ESTATS_WRITE_TIMEOUT, 118 TCP_ESTATS_CONN_TIMEOUT, 119 TCP_ESTATS_ACK_LATENCY, 120 TCP_ESTATS_NEVENTS, 121}; 122 123struct tcp_estats_event { 124 int pid; 125 int cpu; 126 unsigned long ts; 127 unsigned int magic; 128 enum tcp_estats_event_type event_type; 129}; 130 131/* The below data structure is packed in order for 132 * llvm compiler to generate expected code. 133 */ 134struct tcp_estats_conn_id { 135 unsigned int localaddressType; 136 struct { 137 unsigned char data[16]; 138 } localaddress; 139 struct { 140 unsigned char data[16]; 141 } remaddress; 142 unsigned short localport; 143 unsigned short remport; 144} __attribute__((__packed__)); 145 146struct tcp_estats_basic_event { 147 struct tcp_estats_event event; 148 struct tcp_estats_conn_id conn_id; 149}; 150 151struct { 152 __uint(type, BPF_MAP_TYPE_HASH); 153 __uint(max_entries, 1024); 154 __type(key, __u32); 155 __type(value, struct tcp_estats_basic_event); 156} ev_record_map SEC(".maps"); 157 158struct dummy_tracepoint_args { 159 unsigned long long pad; 160 struct sock *sock; 161}; 162 163static __always_inline void tcp_estats_ev_init(struct tcp_estats_event *event, 164 enum tcp_estats_event_type type) 165{ 166 event->magic = TCP_ESTATS_MAGIC; 167 event->ts = bpf_ktime_get_ns(); 168 event->event_type = type; 169} 170 171static __always_inline void unaligned_u32_set(unsigned char *to, __u8 *from) 172{ 173 to[0] = _(from[0]); 174 to[1] = _(from[1]); 175 to[2] = _(from[2]); 176 to[3] = _(from[3]); 177} 178 179static __always_inline void conn_id_ipv4_init(struct tcp_estats_conn_id *conn_id, 180 __be32 *saddr, __be32 *daddr) 181{ 182 conn_id->localaddressType = TCP_ESTATS_ADDRTYPE_IPV4; 183 184 unaligned_u32_set(conn_id->localaddress.data, (__u8 *)saddr); 185 unaligned_u32_set(conn_id->remaddress.data, (__u8 *)daddr); 186} 187 188static __always_inline void conn_id_ipv6_init(struct tcp_estats_conn_id *conn_id, 189 __be32 *saddr, __be32 *daddr) 190{ 191 conn_id->localaddressType = TCP_ESTATS_ADDRTYPE_IPV6; 192 193 unaligned_u32_set(conn_id->localaddress.data, (__u8 *)saddr); 194 unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32), 195 (__u8 *)(saddr + 1)); 196 unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32) * 2, 197 (__u8 *)(saddr + 2)); 198 unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32) * 3, 199 (__u8 *)(saddr + 3)); 200 201 unaligned_u32_set(conn_id->remaddress.data, 202 (__u8 *)(daddr)); 203 unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32), 204 (__u8 *)(daddr + 1)); 205 unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32) * 2, 206 (__u8 *)(daddr + 2)); 207 unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32) * 3, 208 (__u8 *)(daddr + 3)); 209} 210 211static __always_inline void tcp_estats_conn_id_init(struct tcp_estats_conn_id *conn_id, 212 struct sock *sk) 213{ 214 conn_id->localport = _(inet_sk(sk)->inet_sport); 215 conn_id->remport = _(inet_sk(sk)->inet_dport); 216 217 if (_(sk->sk_family) == AF_INET6) 218 conn_id_ipv6_init(conn_id, 219 sk->sk_v6_rcv_saddr.s6_addr32, 220 sk->sk_v6_daddr.s6_addr32); 221 else 222 conn_id_ipv4_init(conn_id, 223 &inet_sk(sk)->inet_saddr, 224 &inet_sk(sk)->inet_daddr); 225} 226 227static __always_inline void tcp_estats_init(struct sock *sk, 228 struct tcp_estats_event *event, 229 struct tcp_estats_conn_id *conn_id, 230 enum tcp_estats_event_type type) 231{ 232 tcp_estats_ev_init(event, type); 233 tcp_estats_conn_id_init(conn_id, sk); 234} 235 236static __always_inline void send_basic_event(struct sock *sk, 237 enum tcp_estats_event_type type) 238{ 239 struct tcp_estats_basic_event ev; 240 __u32 key = bpf_get_prandom_u32(); 241 242 memset(&ev, 0, sizeof(ev)); 243 tcp_estats_init(sk, &ev.event, &ev.conn_id, type); 244 bpf_map_update_elem(&ev_record_map, &key, &ev, BPF_ANY); 245} 246 247SEC("tp/dummy/tracepoint") 248int _dummy_tracepoint(struct dummy_tracepoint_args *arg) 249{ 250 if (!arg->sock) 251 return 0; 252 253 send_basic_event(arg->sock, TCP_ESTATS_TX_RESET); 254 return 0; 255} 256 257char _license[] SEC("license") = "GPL";