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 | */ |
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 | __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 | |
158 | struct dummy_tracepoint_args { |
159 | unsigned long long pad; |
160 | struct sock *sock; |
161 | }; |
162 | |
163 | static __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 | |
171 | static __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 | |
179 | static __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(to: conn_id->localaddress.data, from: (__u8 *)saddr); |
185 | unaligned_u32_set(to: conn_id->remaddress.data, from: (__u8 *)daddr); |
186 | } |
187 | |
188 | static __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(to: conn_id->localaddress.data, from: (__u8 *)saddr); |
194 | unaligned_u32_set(to: conn_id->localaddress.data + sizeof(__u32), |
195 | from: (__u8 *)(saddr + 1)); |
196 | unaligned_u32_set(to: conn_id->localaddress.data + sizeof(__u32) * 2, |
197 | from: (__u8 *)(saddr + 2)); |
198 | unaligned_u32_set(to: conn_id->localaddress.data + sizeof(__u32) * 3, |
199 | from: (__u8 *)(saddr + 3)); |
200 | |
201 | unaligned_u32_set(to: conn_id->remaddress.data, |
202 | from: (__u8 *)(daddr)); |
203 | unaligned_u32_set(to: conn_id->remaddress.data + sizeof(__u32), |
204 | from: (__u8 *)(daddr + 1)); |
205 | unaligned_u32_set(to: conn_id->remaddress.data + sizeof(__u32) * 2, |
206 | from: (__u8 *)(daddr + 2)); |
207 | unaligned_u32_set(to: conn_id->remaddress.data + sizeof(__u32) * 3, |
208 | from: (__u8 *)(daddr + 3)); |
209 | } |
210 | |
211 | static __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 | saddr: sk->sk_v6_rcv_saddr.s6_addr32, |
220 | daddr: sk->sk_v6_daddr.s6_addr32); |
221 | else |
222 | conn_id_ipv4_init(conn_id, |
223 | saddr: &inet_sk(sk)->inet_saddr, |
224 | daddr: &inet_sk(sk)->inet_daddr); |
225 | } |
226 | |
227 | static __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 | |
236 | static __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, event: &ev.event, conn_id: &ev.conn_id, type); |
244 | bpf_map_update_elem(&ev_record_map, &key, &ev, BPF_ANY); |
245 | } |
246 | |
247 | SEC("tp/dummy/tracepoint" ) |
248 | int _dummy_tracepoint(struct dummy_tracepoint_args *arg) |
249 | { |
250 | if (!arg->sock) |
251 | return 0; |
252 | |
253 | send_basic_event(sk: arg->sock, type: TCP_ESTATS_TX_RESET); |
254 | return 0; |
255 | } |
256 | |
257 | char _license[] SEC("license" ) = "GPL" ; |
258 | |