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