Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0
2 : /*
3 : * To speed up listener socket lookup, create an array to store all sockets
4 : * listening on the same port. This allows a decision to be made after finding
5 : * the first socket. An optional BPF program can also be configured for
6 : * selecting the socket index from the array of available sockets.
7 : */
8 :
9 : #include <net/sock_reuseport.h>
10 : #include <linux/bpf.h>
11 : #include <linux/idr.h>
12 : #include <linux/filter.h>
13 : #include <linux/rcupdate.h>
14 :
15 : #define INIT_SOCKS 128
16 :
17 : DEFINE_SPINLOCK(reuseport_lock);
18 :
19 : static DEFINE_IDA(reuseport_ida);
20 :
21 0 : static struct sock_reuseport *__reuseport_alloc(unsigned int max_socks)
22 : {
23 0 : unsigned int size = sizeof(struct sock_reuseport) +
24 : sizeof(struct sock *) * max_socks;
25 0 : struct sock_reuseport *reuse = kzalloc(size, GFP_ATOMIC);
26 :
27 0 : if (!reuse)
28 : return NULL;
29 :
30 0 : reuse->max_socks = max_socks;
31 :
32 0 : RCU_INIT_POINTER(reuse->prog, NULL);
33 0 : return reuse;
34 : }
35 :
36 0 : int reuseport_alloc(struct sock *sk, bool bind_inany)
37 : {
38 0 : struct sock_reuseport *reuse;
39 0 : int id, ret = 0;
40 :
41 : /* bh lock used since this function call may precede hlist lock in
42 : * soft irq of receive path or setsockopt from process context
43 : */
44 0 : spin_lock_bh(&reuseport_lock);
45 :
46 : /* Allocation attempts can occur concurrently via the setsockopt path
47 : * and the bind/hash path. Nothing to do when we lose the race.
48 : */
49 0 : reuse = rcu_dereference_protected(sk->sk_reuseport_cb,
50 : lockdep_is_held(&reuseport_lock));
51 0 : if (reuse) {
52 : /* Only set reuse->bind_inany if the bind_inany is true.
53 : * Otherwise, it will overwrite the reuse->bind_inany
54 : * which was set by the bind/hash path.
55 : */
56 0 : if (bind_inany)
57 0 : reuse->bind_inany = bind_inany;
58 0 : goto out;
59 : }
60 :
61 0 : reuse = __reuseport_alloc(INIT_SOCKS);
62 0 : if (!reuse) {
63 0 : ret = -ENOMEM;
64 0 : goto out;
65 : }
66 :
67 0 : id = ida_alloc(&reuseport_ida, GFP_ATOMIC);
68 0 : if (id < 0) {
69 0 : kfree(reuse);
70 0 : ret = id;
71 0 : goto out;
72 : }
73 :
74 0 : reuse->reuseport_id = id;
75 0 : reuse->socks[0] = sk;
76 0 : reuse->num_socks = 1;
77 0 : reuse->bind_inany = bind_inany;
78 0 : rcu_assign_pointer(sk->sk_reuseport_cb, reuse);
79 :
80 0 : out:
81 0 : spin_unlock_bh(&reuseport_lock);
82 :
83 0 : return ret;
84 : }
85 : EXPORT_SYMBOL(reuseport_alloc);
86 :
87 0 : static struct sock_reuseport *reuseport_grow(struct sock_reuseport *reuse)
88 : {
89 0 : struct sock_reuseport *more_reuse;
90 0 : u32 more_socks_size, i;
91 :
92 0 : more_socks_size = reuse->max_socks * 2U;
93 0 : if (more_socks_size > U16_MAX)
94 : return NULL;
95 :
96 0 : more_reuse = __reuseport_alloc(more_socks_size);
97 0 : if (!more_reuse)
98 : return NULL;
99 :
100 0 : more_reuse->num_socks = reuse->num_socks;
101 0 : more_reuse->prog = reuse->prog;
102 0 : more_reuse->reuseport_id = reuse->reuseport_id;
103 0 : more_reuse->bind_inany = reuse->bind_inany;
104 0 : more_reuse->has_conns = reuse->has_conns;
105 :
106 0 : memcpy(more_reuse->socks, reuse->socks,
107 0 : reuse->num_socks * sizeof(struct sock *));
108 0 : more_reuse->synq_overflow_ts = READ_ONCE(reuse->synq_overflow_ts);
109 :
110 0 : for (i = 0; i < reuse->num_socks; ++i)
111 0 : rcu_assign_pointer(reuse->socks[i]->sk_reuseport_cb,
112 : more_reuse);
113 :
114 : /* Note: we use kfree_rcu here instead of reuseport_free_rcu so
115 : * that reuse and more_reuse can temporarily share a reference
116 : * to prog.
117 : */
118 0 : kfree_rcu(reuse, rcu);
119 : return more_reuse;
120 : }
121 :
122 0 : static void reuseport_free_rcu(struct rcu_head *head)
123 : {
124 0 : struct sock_reuseport *reuse;
125 :
126 0 : reuse = container_of(head, struct sock_reuseport, rcu);
127 0 : sk_reuseport_prog_free(rcu_dereference_protected(reuse->prog, 1));
128 0 : ida_free(&reuseport_ida, reuse->reuseport_id);
129 0 : kfree(reuse);
130 0 : }
131 :
132 : /**
133 : * reuseport_add_sock - Add a socket to the reuseport group of another.
134 : * @sk: New socket to add to the group.
135 : * @sk2: Socket belonging to the existing reuseport group.
136 : * @bind_inany: Whether or not the group is bound to a local INANY address.
137 : *
138 : * May return ENOMEM and not add socket to group under memory pressure.
139 : */
140 0 : int reuseport_add_sock(struct sock *sk, struct sock *sk2, bool bind_inany)
141 : {
142 0 : struct sock_reuseport *old_reuse, *reuse;
143 :
144 0 : if (!rcu_access_pointer(sk2->sk_reuseport_cb)) {
145 0 : int err = reuseport_alloc(sk2, bind_inany);
146 :
147 0 : if (err)
148 : return err;
149 : }
150 :
151 0 : spin_lock_bh(&reuseport_lock);
152 0 : reuse = rcu_dereference_protected(sk2->sk_reuseport_cb,
153 : lockdep_is_held(&reuseport_lock));
154 0 : old_reuse = rcu_dereference_protected(sk->sk_reuseport_cb,
155 : lockdep_is_held(&reuseport_lock));
156 0 : if (old_reuse && old_reuse->num_socks != 1) {
157 0 : spin_unlock_bh(&reuseport_lock);
158 0 : return -EBUSY;
159 : }
160 :
161 0 : if (reuse->num_socks == reuse->max_socks) {
162 0 : reuse = reuseport_grow(reuse);
163 0 : if (!reuse) {
164 0 : spin_unlock_bh(&reuseport_lock);
165 0 : return -ENOMEM;
166 : }
167 : }
168 :
169 0 : reuse->socks[reuse->num_socks] = sk;
170 : /* paired with smp_rmb() in reuseport_select_sock() */
171 0 : smp_wmb();
172 0 : reuse->num_socks++;
173 0 : rcu_assign_pointer(sk->sk_reuseport_cb, reuse);
174 :
175 0 : spin_unlock_bh(&reuseport_lock);
176 :
177 0 : if (old_reuse)
178 0 : call_rcu(&old_reuse->rcu, reuseport_free_rcu);
179 : return 0;
180 : }
181 : EXPORT_SYMBOL(reuseport_add_sock);
182 :
183 0 : void reuseport_detach_sock(struct sock *sk)
184 : {
185 0 : struct sock_reuseport *reuse;
186 0 : int i;
187 :
188 0 : spin_lock_bh(&reuseport_lock);
189 0 : reuse = rcu_dereference_protected(sk->sk_reuseport_cb,
190 : lockdep_is_held(&reuseport_lock));
191 :
192 : /* Notify the bpf side. The sk may be added to a sockarray
193 : * map. If so, sockarray logic will remove it from the map.
194 : *
195 : * Other bpf map types that work with reuseport, like sockmap,
196 : * don't need an explicit callback from here. They override sk
197 : * unhash/close ops to remove the sk from the map before we
198 : * get to this point.
199 : */
200 0 : bpf_sk_reuseport_detach(sk);
201 :
202 0 : rcu_assign_pointer(sk->sk_reuseport_cb, NULL);
203 :
204 0 : for (i = 0; i < reuse->num_socks; i++) {
205 0 : if (reuse->socks[i] == sk) {
206 0 : reuse->socks[i] = reuse->socks[reuse->num_socks - 1];
207 0 : reuse->num_socks--;
208 0 : if (reuse->num_socks == 0)
209 0 : call_rcu(&reuse->rcu, reuseport_free_rcu);
210 : break;
211 : }
212 : }
213 0 : spin_unlock_bh(&reuseport_lock);
214 0 : }
215 : EXPORT_SYMBOL(reuseport_detach_sock);
216 :
217 0 : static struct sock *run_bpf_filter(struct sock_reuseport *reuse, u16 socks,
218 : struct bpf_prog *prog, struct sk_buff *skb,
219 : int hdr_len)
220 : {
221 0 : struct sk_buff *nskb = NULL;
222 0 : u32 index;
223 :
224 0 : if (skb_shared(skb)) {
225 0 : nskb = skb_clone(skb, GFP_ATOMIC);
226 0 : if (!nskb)
227 : return NULL;
228 : skb = nskb;
229 : }
230 :
231 : /* temporarily advance data past protocol header */
232 0 : if (!pskb_pull(skb, hdr_len)) {
233 0 : kfree_skb(nskb);
234 0 : return NULL;
235 : }
236 0 : index = bpf_prog_run_save_cb(prog, skb);
237 0 : __skb_push(skb, hdr_len);
238 :
239 0 : consume_skb(nskb);
240 :
241 0 : if (index >= socks)
242 : return NULL;
243 :
244 0 : return reuse->socks[index];
245 : }
246 :
247 : /**
248 : * reuseport_select_sock - Select a socket from an SO_REUSEPORT group.
249 : * @sk: First socket in the group.
250 : * @hash: When no BPF filter is available, use this hash to select.
251 : * @skb: skb to run through BPF filter.
252 : * @hdr_len: BPF filter expects skb data pointer at payload data. If
253 : * the skb does not yet point at the payload, this parameter represents
254 : * how far the pointer needs to advance to reach the payload.
255 : * Returns a socket that should receive the packet (or NULL on error).
256 : */
257 0 : struct sock *reuseport_select_sock(struct sock *sk,
258 : u32 hash,
259 : struct sk_buff *skb,
260 : int hdr_len)
261 : {
262 0 : struct sock_reuseport *reuse;
263 0 : struct bpf_prog *prog;
264 0 : struct sock *sk2 = NULL;
265 0 : u16 socks;
266 :
267 0 : rcu_read_lock();
268 0 : reuse = rcu_dereference(sk->sk_reuseport_cb);
269 :
270 : /* if memory allocation failed or add call is not yet complete */
271 0 : if (!reuse)
272 0 : goto out;
273 :
274 0 : prog = rcu_dereference(reuse->prog);
275 0 : socks = READ_ONCE(reuse->num_socks);
276 0 : if (likely(socks)) {
277 : /* paired with smp_wmb() in reuseport_add_sock() */
278 0 : smp_rmb();
279 :
280 0 : if (!prog || !skb)
281 0 : goto select_by_hash;
282 :
283 0 : if (prog->type == BPF_PROG_TYPE_SK_REUSEPORT)
284 0 : sk2 = bpf_run_sk_reuseport(reuse, sk, prog, skb, hash);
285 : else
286 0 : sk2 = run_bpf_filter(reuse, socks, prog, skb, hdr_len);
287 :
288 0 : select_by_hash:
289 : /* no bpf or invalid bpf result: fall back to hash usage */
290 0 : if (!sk2) {
291 0 : int i, j;
292 :
293 0 : i = j = reciprocal_scale(hash, socks);
294 0 : while (reuse->socks[i]->sk_state == TCP_ESTABLISHED) {
295 0 : i++;
296 0 : if (i >= socks)
297 0 : i = 0;
298 0 : if (i == j)
299 0 : goto out;
300 : }
301 : sk2 = reuse->socks[i];
302 : }
303 : }
304 :
305 0 : out:
306 0 : rcu_read_unlock();
307 0 : return sk2;
308 : }
309 : EXPORT_SYMBOL(reuseport_select_sock);
310 :
311 0 : int reuseport_attach_prog(struct sock *sk, struct bpf_prog *prog)
312 : {
313 0 : struct sock_reuseport *reuse;
314 0 : struct bpf_prog *old_prog;
315 :
316 0 : if (sk_unhashed(sk) && sk->sk_reuseport) {
317 0 : int err = reuseport_alloc(sk, false);
318 :
319 0 : if (err)
320 : return err;
321 0 : } else if (!rcu_access_pointer(sk->sk_reuseport_cb)) {
322 : /* The socket wasn't bound with SO_REUSEPORT */
323 : return -EINVAL;
324 : }
325 :
326 0 : spin_lock_bh(&reuseport_lock);
327 0 : reuse = rcu_dereference_protected(sk->sk_reuseport_cb,
328 : lockdep_is_held(&reuseport_lock));
329 0 : old_prog = rcu_dereference_protected(reuse->prog,
330 : lockdep_is_held(&reuseport_lock));
331 0 : rcu_assign_pointer(reuse->prog, prog);
332 0 : spin_unlock_bh(&reuseport_lock);
333 :
334 0 : sk_reuseport_prog_free(old_prog);
335 0 : return 0;
336 : }
337 : EXPORT_SYMBOL(reuseport_attach_prog);
338 :
339 0 : int reuseport_detach_prog(struct sock *sk)
340 : {
341 0 : struct sock_reuseport *reuse;
342 0 : struct bpf_prog *old_prog;
343 :
344 0 : if (!rcu_access_pointer(sk->sk_reuseport_cb))
345 0 : return sk->sk_reuseport ? -ENOENT : -EINVAL;
346 :
347 0 : old_prog = NULL;
348 0 : spin_lock_bh(&reuseport_lock);
349 0 : reuse = rcu_dereference_protected(sk->sk_reuseport_cb,
350 : lockdep_is_held(&reuseport_lock));
351 0 : old_prog = rcu_replace_pointer(reuse->prog, old_prog,
352 : lockdep_is_held(&reuseport_lock));
353 0 : spin_unlock_bh(&reuseport_lock);
354 :
355 0 : if (!old_prog)
356 : return -ENOENT;
357 :
358 0 : sk_reuseport_prog_free(old_prog);
359 0 : return 0;
360 : }
361 : EXPORT_SYMBOL(reuseport_detach_prog);
|