bpf: selftest: Adapt sock_fields test to use skel and global variables
[linux-2.6-block.git] / tools / testing / selftests / bpf / progs / test_sock_fields.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2019 Facebook */
3
4 #include <linux/bpf.h>
5 #include <netinet/in.h>
6 #include <stdbool.h>
7
8 #include <bpf/bpf_helpers.h>
9 #include <bpf/bpf_endian.h>
10
11 enum bpf_linum_array_idx {
12         EGRESS_LINUM_IDX,
13         INGRESS_LINUM_IDX,
14         __NR_BPF_LINUM_ARRAY_IDX,
15 };
16
17 struct {
18         __uint(type, BPF_MAP_TYPE_ARRAY);
19         __uint(max_entries, __NR_BPF_LINUM_ARRAY_IDX);
20         __type(key, __u32);
21         __type(value, __u32);
22 } linum_map SEC(".maps");
23
24 struct bpf_spinlock_cnt {
25         struct bpf_spin_lock lock;
26         __u32 cnt;
27 };
28
29 struct {
30         __uint(type, BPF_MAP_TYPE_SK_STORAGE);
31         __uint(map_flags, BPF_F_NO_PREALLOC);
32         __type(key, int);
33         __type(value, struct bpf_spinlock_cnt);
34 } sk_pkt_out_cnt SEC(".maps");
35
36 struct {
37         __uint(type, BPF_MAP_TYPE_SK_STORAGE);
38         __uint(map_flags, BPF_F_NO_PREALLOC);
39         __type(key, int);
40         __type(value, struct bpf_spinlock_cnt);
41 } sk_pkt_out_cnt10 SEC(".maps");
42
43 struct bpf_tcp_sock listen_tp = {};
44 struct sockaddr_in6 srv_sa6 = {};
45 struct bpf_tcp_sock cli_tp = {};
46 struct bpf_tcp_sock srv_tp = {};
47 struct bpf_sock listen_sk = {};
48 struct bpf_sock srv_sk = {};
49 struct bpf_sock cli_sk = {};
50
51 static bool is_loopback6(__u32 *a6)
52 {
53         return !a6[0] && !a6[1] && !a6[2] && a6[3] == bpf_htonl(1);
54 }
55
56 static void skcpy(struct bpf_sock *dst,
57                   const struct bpf_sock *src)
58 {
59         dst->bound_dev_if = src->bound_dev_if;
60         dst->family = src->family;
61         dst->type = src->type;
62         dst->protocol = src->protocol;
63         dst->mark = src->mark;
64         dst->priority = src->priority;
65         dst->src_ip4 = src->src_ip4;
66         dst->src_ip6[0] = src->src_ip6[0];
67         dst->src_ip6[1] = src->src_ip6[1];
68         dst->src_ip6[2] = src->src_ip6[2];
69         dst->src_ip6[3] = src->src_ip6[3];
70         dst->src_port = src->src_port;
71         dst->dst_ip4 = src->dst_ip4;
72         dst->dst_ip6[0] = src->dst_ip6[0];
73         dst->dst_ip6[1] = src->dst_ip6[1];
74         dst->dst_ip6[2] = src->dst_ip6[2];
75         dst->dst_ip6[3] = src->dst_ip6[3];
76         dst->dst_port = src->dst_port;
77         dst->state = src->state;
78 }
79
80 static void tpcpy(struct bpf_tcp_sock *dst,
81                   const struct bpf_tcp_sock *src)
82 {
83         dst->snd_cwnd = src->snd_cwnd;
84         dst->srtt_us = src->srtt_us;
85         dst->rtt_min = src->rtt_min;
86         dst->snd_ssthresh = src->snd_ssthresh;
87         dst->rcv_nxt = src->rcv_nxt;
88         dst->snd_nxt = src->snd_nxt;
89         dst->snd_una = src->snd_una;
90         dst->mss_cache = src->mss_cache;
91         dst->ecn_flags = src->ecn_flags;
92         dst->rate_delivered = src->rate_delivered;
93         dst->rate_interval_us = src->rate_interval_us;
94         dst->packets_out = src->packets_out;
95         dst->retrans_out = src->retrans_out;
96         dst->total_retrans = src->total_retrans;
97         dst->segs_in = src->segs_in;
98         dst->data_segs_in = src->data_segs_in;
99         dst->segs_out = src->segs_out;
100         dst->data_segs_out = src->data_segs_out;
101         dst->lost_out = src->lost_out;
102         dst->sacked_out = src->sacked_out;
103         dst->bytes_received = src->bytes_received;
104         dst->bytes_acked = src->bytes_acked;
105 }
106
107 /* Always return CG_OK so that no pkt will be filtered out */
108 #define CG_OK 1
109
110 #define RET_LOG() ({                                            \
111         linum = __LINE__;                                       \
112         bpf_map_update_elem(&linum_map, &linum_idx, &linum, BPF_NOEXIST);       \
113         return CG_OK;                                           \
114 })
115
116 SEC("cgroup_skb/egress")
117 int egress_read_sock_fields(struct __sk_buff *skb)
118 {
119         struct bpf_spinlock_cnt cli_cnt_init = { .lock = 0, .cnt = 0xeB9F };
120         struct bpf_spinlock_cnt *pkt_out_cnt, *pkt_out_cnt10;
121         struct bpf_tcp_sock *tp, *tp_ret;
122         struct bpf_sock *sk, *sk_ret;
123         __u32 linum, linum_idx;
124
125         linum_idx = EGRESS_LINUM_IDX;
126
127         sk = skb->sk;
128         if (!sk)
129                 RET_LOG();
130
131         /* Not the testing egress traffic or
132          * TCP_LISTEN (10) socket will be copied at the ingress side.
133          */
134         if (sk->family != AF_INET6 || !is_loopback6(sk->src_ip6) ||
135             sk->state == 10)
136                 return CG_OK;
137
138         if (sk->src_port == bpf_ntohs(srv_sa6.sin6_port)) {
139                 /* Server socket */
140                 sk_ret = &srv_sk;
141                 tp_ret = &srv_tp;
142         } else if (sk->dst_port == srv_sa6.sin6_port) {
143                 /* Client socket */
144                 sk_ret = &cli_sk;
145                 tp_ret = &cli_tp;
146         } else {
147                 /* Not the testing egress traffic */
148                 return CG_OK;
149         }
150
151         /* It must be a fullsock for cgroup_skb/egress prog */
152         sk = bpf_sk_fullsock(sk);
153         if (!sk)
154                 RET_LOG();
155
156         /* Not the testing egress traffic */
157         if (sk->protocol != IPPROTO_TCP)
158                 return CG_OK;
159
160         tp = bpf_tcp_sock(sk);
161         if (!tp)
162                 RET_LOG();
163
164         skcpy(sk_ret, sk);
165         tpcpy(tp_ret, tp);
166
167         if (sk_ret == &srv_sk) {
168                 /* The userspace has created it for srv sk */
169                 pkt_out_cnt = bpf_sk_storage_get(&sk_pkt_out_cnt, sk, 0, 0);
170                 pkt_out_cnt10 = bpf_sk_storage_get(&sk_pkt_out_cnt10, sk,
171                                                    0, 0);
172         } else {
173                 pkt_out_cnt = bpf_sk_storage_get(&sk_pkt_out_cnt, sk,
174                                                  &cli_cnt_init,
175                                                  BPF_SK_STORAGE_GET_F_CREATE);
176                 pkt_out_cnt10 = bpf_sk_storage_get(&sk_pkt_out_cnt10,
177                                                    sk, &cli_cnt_init,
178                                                    BPF_SK_STORAGE_GET_F_CREATE);
179         }
180
181         if (!pkt_out_cnt || !pkt_out_cnt10)
182                 RET_LOG();
183
184         /* Even both cnt and cnt10 have lock defined in their BTF,
185          * intentionally one cnt takes lock while one does not
186          * as a test for the spinlock support in BPF_MAP_TYPE_SK_STORAGE.
187          */
188         pkt_out_cnt->cnt += 1;
189         bpf_spin_lock(&pkt_out_cnt10->lock);
190         pkt_out_cnt10->cnt += 10;
191         bpf_spin_unlock(&pkt_out_cnt10->lock);
192
193         return CG_OK;
194 }
195
196 SEC("cgroup_skb/ingress")
197 int ingress_read_sock_fields(struct __sk_buff *skb)
198 {
199         struct bpf_tcp_sock *tp;
200         __u32 linum, linum_idx;
201         struct bpf_sock *sk;
202
203         linum_idx = INGRESS_LINUM_IDX;
204
205         sk = skb->sk;
206         if (!sk)
207                 RET_LOG();
208
209         /* Not the testing ingress traffic to the server */
210         if (sk->family != AF_INET6 || !is_loopback6(sk->src_ip6) ||
211             sk->src_port != bpf_ntohs(srv_sa6.sin6_port))
212                 return CG_OK;
213
214         /* Only interested in TCP_LISTEN */
215         if (sk->state != 10)
216                 return CG_OK;
217
218         /* It must be a fullsock for cgroup_skb/ingress prog */
219         sk = bpf_sk_fullsock(sk);
220         if (!sk)
221                 RET_LOG();
222
223         tp = bpf_tcp_sock(sk);
224         if (!tp)
225                 RET_LOG();
226
227         skcpy(&listen_sk, sk);
228         tpcpy(&listen_tp, tp);
229
230         return CG_OK;
231 }
232
233 char _license[] SEC("license") = "GPL";