xref: /linux/tools/testing/selftests/bpf/progs/test_tcp_estats.c (revision f6d08d9d8543c8ee494b307804b28e2750ffedb9)
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_helpers.h"
40 
41 #define _(P) ({typeof(P) val = 0; bpf_probe_read(&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  */
51 typedef __u32 __bitwise __portpair;
52 typedef __u64 __bitwise __addrpair;
53 
54 struct 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 
74 struct 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 
81 struct 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 
89 struct pt_regs {
90 	long di;
91 };
92 
93 static 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  */
101 enum tcp_estats_addrtype {
102 	TCP_ESTATS_ADDRTYPE_IPV4 = 1,
103 	TCP_ESTATS_ADDRTYPE_IPV6 = 2
104 };
105 
106 enum 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 
123 struct 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  */
134 struct 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 
146 struct tcp_estats_basic_event {
147 	struct tcp_estats_event event;
148 	struct tcp_estats_conn_id conn_id;
149 };
150 
151 struct {
152 	__u32 type;
153 	__u32 max_entries;
154 	__u32 *key;
155 	struct tcp_estats_basic_event *value;
156 } ev_record_map SEC(".maps") = {
157 	.type = BPF_MAP_TYPE_HASH,
158 	.max_entries = 1024,
159 };
160 
161 struct dummy_tracepoint_args {
162 	unsigned long long pad;
163 	struct sock *sock;
164 };
165 
166 static __always_inline void tcp_estats_ev_init(struct tcp_estats_event *event,
167 					       enum tcp_estats_event_type type)
168 {
169 	event->magic = TCP_ESTATS_MAGIC;
170 	event->ts = bpf_ktime_get_ns();
171 	event->event_type = type;
172 }
173 
174 static __always_inline void unaligned_u32_set(unsigned char *to, __u8 *from)
175 {
176 	to[0] = _(from[0]);
177 	to[1] = _(from[1]);
178 	to[2] = _(from[2]);
179 	to[3] = _(from[3]);
180 }
181 
182 static __always_inline void conn_id_ipv4_init(struct tcp_estats_conn_id *conn_id,
183 					      __be32 *saddr, __be32 *daddr)
184 {
185 	conn_id->localaddressType = TCP_ESTATS_ADDRTYPE_IPV4;
186 
187 	unaligned_u32_set(conn_id->localaddress.data, (__u8 *)saddr);
188 	unaligned_u32_set(conn_id->remaddress.data, (__u8 *)daddr);
189 }
190 
191 static __always_inline void conn_id_ipv6_init(struct tcp_estats_conn_id *conn_id,
192 					      __be32 *saddr, __be32 *daddr)
193 {
194 	conn_id->localaddressType = TCP_ESTATS_ADDRTYPE_IPV6;
195 
196 	unaligned_u32_set(conn_id->localaddress.data, (__u8 *)saddr);
197 	unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32),
198 			  (__u8 *)(saddr + 1));
199 	unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32) * 2,
200 			  (__u8 *)(saddr + 2));
201 	unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32) * 3,
202 			  (__u8 *)(saddr + 3));
203 
204 	unaligned_u32_set(conn_id->remaddress.data,
205 			  (__u8 *)(daddr));
206 	unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32),
207 			  (__u8 *)(daddr + 1));
208 	unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32) * 2,
209 			  (__u8 *)(daddr + 2));
210 	unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32) * 3,
211 			  (__u8 *)(daddr + 3));
212 }
213 
214 static __always_inline void tcp_estats_conn_id_init(struct tcp_estats_conn_id *conn_id,
215 						    struct sock *sk)
216 {
217 	conn_id->localport = _(inet_sk(sk)->inet_sport);
218 	conn_id->remport = _(inet_sk(sk)->inet_dport);
219 
220 	if (_(sk->sk_family) == AF_INET6)
221 		conn_id_ipv6_init(conn_id,
222 				  sk->sk_v6_rcv_saddr.s6_addr32,
223 				  sk->sk_v6_daddr.s6_addr32);
224 	else
225 		conn_id_ipv4_init(conn_id,
226 				  &inet_sk(sk)->inet_saddr,
227 				  &inet_sk(sk)->inet_daddr);
228 }
229 
230 static __always_inline void tcp_estats_init(struct sock *sk,
231 					    struct tcp_estats_event *event,
232 					    struct tcp_estats_conn_id *conn_id,
233 					    enum tcp_estats_event_type type)
234 {
235 	tcp_estats_ev_init(event, type);
236 	tcp_estats_conn_id_init(conn_id, sk);
237 }
238 
239 static __always_inline void send_basic_event(struct sock *sk,
240 					     enum tcp_estats_event_type type)
241 {
242 	struct tcp_estats_basic_event ev;
243 	__u32 key = bpf_get_prandom_u32();
244 
245 	memset(&ev, 0, sizeof(ev));
246 	tcp_estats_init(sk, &ev.event, &ev.conn_id, type);
247 	bpf_map_update_elem(&ev_record_map, &key, &ev, BPF_ANY);
248 }
249 
250 SEC("dummy_tracepoint")
251 int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
252 {
253 	if (!arg->sock)
254 		return 0;
255 
256 	send_basic_event(arg->sock, TCP_ESTATS_TX_RESET);
257 	return 0;
258 }
259 
260 char _license[] SEC("license") = "GPL";
261 __u32 _version SEC("version") = 1; /* ignored by tracepoints, required by libbpf.a */
262