xref: /linux/kernel/bpf/mprog.c (revision edfa9af0)
1053c8e1fSDaniel Borkmann // SPDX-License-Identifier: GPL-2.0
2053c8e1fSDaniel Borkmann /* Copyright (c) 2023 Isovalent */
3053c8e1fSDaniel Borkmann 
4053c8e1fSDaniel Borkmann #include <linux/bpf.h>
5053c8e1fSDaniel Borkmann #include <linux/bpf_mprog.h>
6053c8e1fSDaniel Borkmann 
bpf_mprog_link(struct bpf_tuple * tuple,u32 id_or_fd,u32 flags,enum bpf_prog_type type)7053c8e1fSDaniel Borkmann static int bpf_mprog_link(struct bpf_tuple *tuple,
8053c8e1fSDaniel Borkmann 			  u32 id_or_fd, u32 flags,
9053c8e1fSDaniel Borkmann 			  enum bpf_prog_type type)
10053c8e1fSDaniel Borkmann {
11053c8e1fSDaniel Borkmann 	struct bpf_link *link = ERR_PTR(-EINVAL);
12053c8e1fSDaniel Borkmann 	bool id = flags & BPF_F_ID;
13053c8e1fSDaniel Borkmann 
14053c8e1fSDaniel Borkmann 	if (id)
15053c8e1fSDaniel Borkmann 		link = bpf_link_by_id(id_or_fd);
16053c8e1fSDaniel Borkmann 	else if (id_or_fd)
17053c8e1fSDaniel Borkmann 		link = bpf_link_get_from_fd(id_or_fd);
18053c8e1fSDaniel Borkmann 	if (IS_ERR(link))
19053c8e1fSDaniel Borkmann 		return PTR_ERR(link);
20053c8e1fSDaniel Borkmann 	if (type && link->prog->type != type) {
21053c8e1fSDaniel Borkmann 		bpf_link_put(link);
22053c8e1fSDaniel Borkmann 		return -EINVAL;
23053c8e1fSDaniel Borkmann 	}
24053c8e1fSDaniel Borkmann 
25053c8e1fSDaniel Borkmann 	tuple->link = link;
26053c8e1fSDaniel Borkmann 	tuple->prog = link->prog;
27053c8e1fSDaniel Borkmann 	return 0;
28053c8e1fSDaniel Borkmann }
29053c8e1fSDaniel Borkmann 
bpf_mprog_prog(struct bpf_tuple * tuple,u32 id_or_fd,u32 flags,enum bpf_prog_type type)30053c8e1fSDaniel Borkmann static int bpf_mprog_prog(struct bpf_tuple *tuple,
31053c8e1fSDaniel Borkmann 			  u32 id_or_fd, u32 flags,
32053c8e1fSDaniel Borkmann 			  enum bpf_prog_type type)
33053c8e1fSDaniel Borkmann {
34053c8e1fSDaniel Borkmann 	struct bpf_prog *prog = ERR_PTR(-EINVAL);
35053c8e1fSDaniel Borkmann 	bool id = flags & BPF_F_ID;
36053c8e1fSDaniel Borkmann 
37053c8e1fSDaniel Borkmann 	if (id)
38053c8e1fSDaniel Borkmann 		prog = bpf_prog_by_id(id_or_fd);
39053c8e1fSDaniel Borkmann 	else if (id_or_fd)
40053c8e1fSDaniel Borkmann 		prog = bpf_prog_get(id_or_fd);
41053c8e1fSDaniel Borkmann 	if (IS_ERR(prog))
42053c8e1fSDaniel Borkmann 		return PTR_ERR(prog);
43053c8e1fSDaniel Borkmann 	if (type && prog->type != type) {
44053c8e1fSDaniel Borkmann 		bpf_prog_put(prog);
45053c8e1fSDaniel Borkmann 		return -EINVAL;
46053c8e1fSDaniel Borkmann 	}
47053c8e1fSDaniel Borkmann 
48053c8e1fSDaniel Borkmann 	tuple->link = NULL;
49053c8e1fSDaniel Borkmann 	tuple->prog = prog;
50053c8e1fSDaniel Borkmann 	return 0;
51053c8e1fSDaniel Borkmann }
52053c8e1fSDaniel Borkmann 
bpf_mprog_tuple_relative(struct bpf_tuple * tuple,u32 id_or_fd,u32 flags,enum bpf_prog_type type)53053c8e1fSDaniel Borkmann static int bpf_mprog_tuple_relative(struct bpf_tuple *tuple,
54053c8e1fSDaniel Borkmann 				    u32 id_or_fd, u32 flags,
55053c8e1fSDaniel Borkmann 				    enum bpf_prog_type type)
56053c8e1fSDaniel Borkmann {
57053c8e1fSDaniel Borkmann 	bool link = flags & BPF_F_LINK;
58053c8e1fSDaniel Borkmann 	bool id = flags & BPF_F_ID;
59053c8e1fSDaniel Borkmann 
60053c8e1fSDaniel Borkmann 	memset(tuple, 0, sizeof(*tuple));
61053c8e1fSDaniel Borkmann 	if (link)
62053c8e1fSDaniel Borkmann 		return bpf_mprog_link(tuple, id_or_fd, flags, type);
63053c8e1fSDaniel Borkmann 	/* If no relevant flag is set and no id_or_fd was passed, then
64053c8e1fSDaniel Borkmann 	 * tuple link/prog is just NULLed. This is the case when before/
65053c8e1fSDaniel Borkmann 	 * after selects first/last position without passing fd.
66053c8e1fSDaniel Borkmann 	 */
67053c8e1fSDaniel Borkmann 	if (!id && !id_or_fd)
68053c8e1fSDaniel Borkmann 		return 0;
69053c8e1fSDaniel Borkmann 	return bpf_mprog_prog(tuple, id_or_fd, flags, type);
70053c8e1fSDaniel Borkmann }
71053c8e1fSDaniel Borkmann 
bpf_mprog_tuple_put(struct bpf_tuple * tuple)72053c8e1fSDaniel Borkmann static void bpf_mprog_tuple_put(struct bpf_tuple *tuple)
73053c8e1fSDaniel Borkmann {
74053c8e1fSDaniel Borkmann 	if (tuple->link)
75053c8e1fSDaniel Borkmann 		bpf_link_put(tuple->link);
76053c8e1fSDaniel Borkmann 	else if (tuple->prog)
77053c8e1fSDaniel Borkmann 		bpf_prog_put(tuple->prog);
78053c8e1fSDaniel Borkmann }
79053c8e1fSDaniel Borkmann 
80053c8e1fSDaniel Borkmann /* The bpf_mprog_{replace,delete}() operate on exact idx position with the
81053c8e1fSDaniel Borkmann  * one exception that for deletion we support delete from front/back. In
82053c8e1fSDaniel Borkmann  * case of front idx is -1, in case of back idx is bpf_mprog_total(entry).
83053c8e1fSDaniel Borkmann  * Adjustment to first and last entry is trivial. The bpf_mprog_insert()
84053c8e1fSDaniel Borkmann  * we have to deal with the following cases:
85053c8e1fSDaniel Borkmann  *
86053c8e1fSDaniel Borkmann  * idx + before:
87053c8e1fSDaniel Borkmann  *
88053c8e1fSDaniel Borkmann  * Insert P4 before P3: idx for old array is 1, idx for new array is 2,
89053c8e1fSDaniel Borkmann  * hence we adjust target idx for the new array, so that memmove copies
90053c8e1fSDaniel Borkmann  * P1 and P2 to the new entry, and we insert P4 into idx 2. Inserting
91053c8e1fSDaniel Borkmann  * before P1 would have old idx -1 and new idx 0.
92053c8e1fSDaniel Borkmann  *
93053c8e1fSDaniel Borkmann  * +--+--+--+     +--+--+--+--+     +--+--+--+--+
94053c8e1fSDaniel Borkmann  * |P1|P2|P3| ==> |P1|P2|  |P3| ==> |P1|P2|P4|P3|
95053c8e1fSDaniel Borkmann  * +--+--+--+     +--+--+--+--+     +--+--+--+--+
96053c8e1fSDaniel Borkmann  *
97053c8e1fSDaniel Borkmann  * idx + after:
98053c8e1fSDaniel Borkmann  *
99053c8e1fSDaniel Borkmann  * Insert P4 after P2: idx for old array is 2, idx for new array is 2.
100053c8e1fSDaniel Borkmann  * Again, memmove copies P1 and P2 to the new entry, and we insert P4
101053c8e1fSDaniel Borkmann  * into idx 2. Inserting after P3 would have both old/new idx at 4 aka
102053c8e1fSDaniel Borkmann  * bpf_mprog_total(entry).
103053c8e1fSDaniel Borkmann  *
104053c8e1fSDaniel Borkmann  * +--+--+--+     +--+--+--+--+     +--+--+--+--+
105053c8e1fSDaniel Borkmann  * |P1|P2|P3| ==> |P1|P2|  |P3| ==> |P1|P2|P4|P3|
106053c8e1fSDaniel Borkmann  * +--+--+--+     +--+--+--+--+     +--+--+--+--+
107053c8e1fSDaniel Borkmann  */
bpf_mprog_replace(struct bpf_mprog_entry * entry,struct bpf_mprog_entry ** entry_new,struct bpf_tuple * ntuple,int idx)108053c8e1fSDaniel Borkmann static int bpf_mprog_replace(struct bpf_mprog_entry *entry,
109053c8e1fSDaniel Borkmann 			     struct bpf_mprog_entry **entry_new,
110053c8e1fSDaniel Borkmann 			     struct bpf_tuple *ntuple, int idx)
111053c8e1fSDaniel Borkmann {
112053c8e1fSDaniel Borkmann 	struct bpf_mprog_fp *fp;
113053c8e1fSDaniel Borkmann 	struct bpf_mprog_cp *cp;
114053c8e1fSDaniel Borkmann 	struct bpf_prog *oprog;
115053c8e1fSDaniel Borkmann 
116053c8e1fSDaniel Borkmann 	bpf_mprog_read(entry, idx, &fp, &cp);
117053c8e1fSDaniel Borkmann 	oprog = READ_ONCE(fp->prog);
118053c8e1fSDaniel Borkmann 	bpf_mprog_write(fp, cp, ntuple);
119053c8e1fSDaniel Borkmann 	if (!ntuple->link) {
120053c8e1fSDaniel Borkmann 		WARN_ON_ONCE(cp->link);
121053c8e1fSDaniel Borkmann 		bpf_prog_put(oprog);
122053c8e1fSDaniel Borkmann 	}
123053c8e1fSDaniel Borkmann 	*entry_new = entry;
124053c8e1fSDaniel Borkmann 	return 0;
125053c8e1fSDaniel Borkmann }
126053c8e1fSDaniel Borkmann 
bpf_mprog_insert(struct bpf_mprog_entry * entry,struct bpf_mprog_entry ** entry_new,struct bpf_tuple * ntuple,int idx,u32 flags)127053c8e1fSDaniel Borkmann static int bpf_mprog_insert(struct bpf_mprog_entry *entry,
128053c8e1fSDaniel Borkmann 			    struct bpf_mprog_entry **entry_new,
129053c8e1fSDaniel Borkmann 			    struct bpf_tuple *ntuple, int idx, u32 flags)
130053c8e1fSDaniel Borkmann {
131053c8e1fSDaniel Borkmann 	int total = bpf_mprog_total(entry);
132053c8e1fSDaniel Borkmann 	struct bpf_mprog_entry *peer;
133053c8e1fSDaniel Borkmann 	struct bpf_mprog_fp *fp;
134053c8e1fSDaniel Borkmann 	struct bpf_mprog_cp *cp;
135053c8e1fSDaniel Borkmann 
136053c8e1fSDaniel Borkmann 	peer = bpf_mprog_peer(entry);
137053c8e1fSDaniel Borkmann 	bpf_mprog_entry_copy(peer, entry);
138053c8e1fSDaniel Borkmann 	if (idx == total)
139053c8e1fSDaniel Borkmann 		goto insert;
140053c8e1fSDaniel Borkmann 	else if (flags & BPF_F_BEFORE)
141053c8e1fSDaniel Borkmann 		idx += 1;
142053c8e1fSDaniel Borkmann 	bpf_mprog_entry_grow(peer, idx);
143053c8e1fSDaniel Borkmann insert:
144053c8e1fSDaniel Borkmann 	bpf_mprog_read(peer, idx, &fp, &cp);
145053c8e1fSDaniel Borkmann 	bpf_mprog_write(fp, cp, ntuple);
146053c8e1fSDaniel Borkmann 	bpf_mprog_inc(peer);
147053c8e1fSDaniel Borkmann 	*entry_new = peer;
148053c8e1fSDaniel Borkmann 	return 0;
149053c8e1fSDaniel Borkmann }
150053c8e1fSDaniel Borkmann 
bpf_mprog_delete(struct bpf_mprog_entry * entry,struct bpf_mprog_entry ** entry_new,struct bpf_tuple * dtuple,int idx)151053c8e1fSDaniel Borkmann static int bpf_mprog_delete(struct bpf_mprog_entry *entry,
152053c8e1fSDaniel Borkmann 			    struct bpf_mprog_entry **entry_new,
153053c8e1fSDaniel Borkmann 			    struct bpf_tuple *dtuple, int idx)
154053c8e1fSDaniel Borkmann {
155053c8e1fSDaniel Borkmann 	int total = bpf_mprog_total(entry);
156053c8e1fSDaniel Borkmann 	struct bpf_mprog_entry *peer;
157053c8e1fSDaniel Borkmann 
158053c8e1fSDaniel Borkmann 	peer = bpf_mprog_peer(entry);
159053c8e1fSDaniel Borkmann 	bpf_mprog_entry_copy(peer, entry);
160053c8e1fSDaniel Borkmann 	if (idx == -1)
161053c8e1fSDaniel Borkmann 		idx = 0;
162053c8e1fSDaniel Borkmann 	else if (idx == total)
163053c8e1fSDaniel Borkmann 		idx = total - 1;
164053c8e1fSDaniel Borkmann 	bpf_mprog_entry_shrink(peer, idx);
165053c8e1fSDaniel Borkmann 	bpf_mprog_dec(peer);
166053c8e1fSDaniel Borkmann 	bpf_mprog_mark_for_release(peer, dtuple);
167053c8e1fSDaniel Borkmann 	*entry_new = peer;
168053c8e1fSDaniel Borkmann 	return 0;
169053c8e1fSDaniel Borkmann }
170053c8e1fSDaniel Borkmann 
171053c8e1fSDaniel Borkmann /* In bpf_mprog_pos_*() we evaluate the target position for the BPF
172053c8e1fSDaniel Borkmann  * program/link that needs to be replaced, inserted or deleted for
173053c8e1fSDaniel Borkmann  * each "rule" independently. If all rules agree on that position
174053c8e1fSDaniel Borkmann  * or existing element, then enact replacement, addition or deletion.
175053c8e1fSDaniel Borkmann  * If this is not the case, then the request cannot be satisfied and
176053c8e1fSDaniel Borkmann  * we bail out with an error.
177053c8e1fSDaniel Borkmann  */
bpf_mprog_pos_exact(struct bpf_mprog_entry * entry,struct bpf_tuple * tuple)178053c8e1fSDaniel Borkmann static int bpf_mprog_pos_exact(struct bpf_mprog_entry *entry,
179053c8e1fSDaniel Borkmann 			       struct bpf_tuple *tuple)
180053c8e1fSDaniel Borkmann {
181053c8e1fSDaniel Borkmann 	struct bpf_mprog_fp *fp;
182053c8e1fSDaniel Borkmann 	struct bpf_mprog_cp *cp;
183053c8e1fSDaniel Borkmann 	int i;
184053c8e1fSDaniel Borkmann 
185053c8e1fSDaniel Borkmann 	for (i = 0; i < bpf_mprog_total(entry); i++) {
186053c8e1fSDaniel Borkmann 		bpf_mprog_read(entry, i, &fp, &cp);
187053c8e1fSDaniel Borkmann 		if (tuple->prog == READ_ONCE(fp->prog))
188053c8e1fSDaniel Borkmann 			return tuple->link == cp->link ? i : -EBUSY;
189053c8e1fSDaniel Borkmann 	}
190053c8e1fSDaniel Borkmann 	return -ENOENT;
191053c8e1fSDaniel Borkmann }
192053c8e1fSDaniel Borkmann 
bpf_mprog_pos_before(struct bpf_mprog_entry * entry,struct bpf_tuple * tuple)193053c8e1fSDaniel Borkmann static int bpf_mprog_pos_before(struct bpf_mprog_entry *entry,
194053c8e1fSDaniel Borkmann 				struct bpf_tuple *tuple)
195053c8e1fSDaniel Borkmann {
196053c8e1fSDaniel Borkmann 	struct bpf_mprog_fp *fp;
197053c8e1fSDaniel Borkmann 	struct bpf_mprog_cp *cp;
198053c8e1fSDaniel Borkmann 	int i;
199053c8e1fSDaniel Borkmann 
200053c8e1fSDaniel Borkmann 	for (i = 0; i < bpf_mprog_total(entry); i++) {
201053c8e1fSDaniel Borkmann 		bpf_mprog_read(entry, i, &fp, &cp);
202053c8e1fSDaniel Borkmann 		if (tuple->prog == READ_ONCE(fp->prog) &&
203053c8e1fSDaniel Borkmann 		    (!tuple->link || tuple->link == cp->link))
204053c8e1fSDaniel Borkmann 			return i - 1;
205053c8e1fSDaniel Borkmann 	}
206053c8e1fSDaniel Borkmann 	return tuple->prog ? -ENOENT : -1;
207053c8e1fSDaniel Borkmann }
208053c8e1fSDaniel Borkmann 
bpf_mprog_pos_after(struct bpf_mprog_entry * entry,struct bpf_tuple * tuple)209053c8e1fSDaniel Borkmann static int bpf_mprog_pos_after(struct bpf_mprog_entry *entry,
210053c8e1fSDaniel Borkmann 			       struct bpf_tuple *tuple)
211053c8e1fSDaniel Borkmann {
212053c8e1fSDaniel Borkmann 	struct bpf_mprog_fp *fp;
213053c8e1fSDaniel Borkmann 	struct bpf_mprog_cp *cp;
214053c8e1fSDaniel Borkmann 	int i;
215053c8e1fSDaniel Borkmann 
216053c8e1fSDaniel Borkmann 	for (i = 0; i < bpf_mprog_total(entry); i++) {
217053c8e1fSDaniel Borkmann 		bpf_mprog_read(entry, i, &fp, &cp);
218053c8e1fSDaniel Borkmann 		if (tuple->prog == READ_ONCE(fp->prog) &&
219053c8e1fSDaniel Borkmann 		    (!tuple->link || tuple->link == cp->link))
220053c8e1fSDaniel Borkmann 			return i + 1;
221053c8e1fSDaniel Borkmann 	}
222053c8e1fSDaniel Borkmann 	return tuple->prog ? -ENOENT : bpf_mprog_total(entry);
223053c8e1fSDaniel Borkmann }
224053c8e1fSDaniel Borkmann 
bpf_mprog_attach(struct bpf_mprog_entry * entry,struct bpf_mprog_entry ** entry_new,struct bpf_prog * prog_new,struct bpf_link * link,struct bpf_prog * prog_old,u32 flags,u32 id_or_fd,u64 revision)225053c8e1fSDaniel Borkmann int bpf_mprog_attach(struct bpf_mprog_entry *entry,
226053c8e1fSDaniel Borkmann 		     struct bpf_mprog_entry **entry_new,
227053c8e1fSDaniel Borkmann 		     struct bpf_prog *prog_new, struct bpf_link *link,
228053c8e1fSDaniel Borkmann 		     struct bpf_prog *prog_old,
229053c8e1fSDaniel Borkmann 		     u32 flags, u32 id_or_fd, u64 revision)
230053c8e1fSDaniel Borkmann {
231053c8e1fSDaniel Borkmann 	struct bpf_tuple rtuple, ntuple = {
232053c8e1fSDaniel Borkmann 		.prog = prog_new,
233053c8e1fSDaniel Borkmann 		.link = link,
234053c8e1fSDaniel Borkmann 	}, otuple = {
235053c8e1fSDaniel Borkmann 		.prog = prog_old,
236053c8e1fSDaniel Borkmann 		.link = link,
237053c8e1fSDaniel Borkmann 	};
238053c8e1fSDaniel Borkmann 	int ret, idx = -ERANGE, tidx;
239053c8e1fSDaniel Borkmann 
240053c8e1fSDaniel Borkmann 	if (revision && revision != bpf_mprog_revision(entry))
241053c8e1fSDaniel Borkmann 		return -ESTALE;
242053c8e1fSDaniel Borkmann 	if (bpf_mprog_exists(entry, prog_new))
243053c8e1fSDaniel Borkmann 		return -EEXIST;
244053c8e1fSDaniel Borkmann 	ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd,
245053c8e1fSDaniel Borkmann 				       flags & ~BPF_F_REPLACE,
246053c8e1fSDaniel Borkmann 				       prog_new->type);
247053c8e1fSDaniel Borkmann 	if (ret)
248053c8e1fSDaniel Borkmann 		return ret;
249053c8e1fSDaniel Borkmann 	if (flags & BPF_F_REPLACE) {
250053c8e1fSDaniel Borkmann 		tidx = bpf_mprog_pos_exact(entry, &otuple);
251053c8e1fSDaniel Borkmann 		if (tidx < 0) {
252053c8e1fSDaniel Borkmann 			ret = tidx;
253053c8e1fSDaniel Borkmann 			goto out;
254053c8e1fSDaniel Borkmann 		}
255053c8e1fSDaniel Borkmann 		idx = tidx;
256f9b0e108SDaniel Borkmann 	} else if (bpf_mprog_total(entry) == bpf_mprog_max()) {
257f9b0e108SDaniel Borkmann 		ret = -ERANGE;
258f9b0e108SDaniel Borkmann 		goto out;
259053c8e1fSDaniel Borkmann 	}
260053c8e1fSDaniel Borkmann 	if (flags & BPF_F_BEFORE) {
261053c8e1fSDaniel Borkmann 		tidx = bpf_mprog_pos_before(entry, &rtuple);
262053c8e1fSDaniel Borkmann 		if (tidx < -1 || (idx >= -1 && tidx != idx)) {
263053c8e1fSDaniel Borkmann 			ret = tidx < -1 ? tidx : -ERANGE;
264053c8e1fSDaniel Borkmann 			goto out;
265053c8e1fSDaniel Borkmann 		}
266053c8e1fSDaniel Borkmann 		idx = tidx;
267053c8e1fSDaniel Borkmann 	}
268053c8e1fSDaniel Borkmann 	if (flags & BPF_F_AFTER) {
269053c8e1fSDaniel Borkmann 		tidx = bpf_mprog_pos_after(entry, &rtuple);
270053c8e1fSDaniel Borkmann 		if (tidx < -1 || (idx >= -1 && tidx != idx)) {
271053c8e1fSDaniel Borkmann 			ret = tidx < 0 ? tidx : -ERANGE;
272053c8e1fSDaniel Borkmann 			goto out;
273053c8e1fSDaniel Borkmann 		}
274053c8e1fSDaniel Borkmann 		idx = tidx;
275053c8e1fSDaniel Borkmann 	}
276053c8e1fSDaniel Borkmann 	if (idx < -1) {
277053c8e1fSDaniel Borkmann 		if (rtuple.prog || flags) {
278053c8e1fSDaniel Borkmann 			ret = -EINVAL;
279053c8e1fSDaniel Borkmann 			goto out;
280053c8e1fSDaniel Borkmann 		}
281053c8e1fSDaniel Borkmann 		idx = bpf_mprog_total(entry);
282053c8e1fSDaniel Borkmann 		flags = BPF_F_AFTER;
283053c8e1fSDaniel Borkmann 	}
284053c8e1fSDaniel Borkmann 	if (idx >= bpf_mprog_max()) {
285053c8e1fSDaniel Borkmann 		ret = -ERANGE;
286053c8e1fSDaniel Borkmann 		goto out;
287053c8e1fSDaniel Borkmann 	}
288053c8e1fSDaniel Borkmann 	if (flags & BPF_F_REPLACE)
289053c8e1fSDaniel Borkmann 		ret = bpf_mprog_replace(entry, entry_new, &ntuple, idx);
290053c8e1fSDaniel Borkmann 	else
291053c8e1fSDaniel Borkmann 		ret = bpf_mprog_insert(entry, entry_new, &ntuple, idx, flags);
292053c8e1fSDaniel Borkmann out:
293053c8e1fSDaniel Borkmann 	bpf_mprog_tuple_put(&rtuple);
294053c8e1fSDaniel Borkmann 	return ret;
295053c8e1fSDaniel Borkmann }
296053c8e1fSDaniel Borkmann 
bpf_mprog_fetch(struct bpf_mprog_entry * entry,struct bpf_tuple * tuple,int idx)297053c8e1fSDaniel Borkmann static int bpf_mprog_fetch(struct bpf_mprog_entry *entry,
298053c8e1fSDaniel Borkmann 			   struct bpf_tuple *tuple, int idx)
299053c8e1fSDaniel Borkmann {
300053c8e1fSDaniel Borkmann 	int total = bpf_mprog_total(entry);
301053c8e1fSDaniel Borkmann 	struct bpf_mprog_cp *cp;
302053c8e1fSDaniel Borkmann 	struct bpf_mprog_fp *fp;
303053c8e1fSDaniel Borkmann 	struct bpf_prog *prog;
304053c8e1fSDaniel Borkmann 	struct bpf_link *link;
305053c8e1fSDaniel Borkmann 
306053c8e1fSDaniel Borkmann 	if (idx == -1)
307053c8e1fSDaniel Borkmann 		idx = 0;
308053c8e1fSDaniel Borkmann 	else if (idx == total)
309053c8e1fSDaniel Borkmann 		idx = total - 1;
310053c8e1fSDaniel Borkmann 	bpf_mprog_read(entry, idx, &fp, &cp);
311053c8e1fSDaniel Borkmann 	prog = READ_ONCE(fp->prog);
312053c8e1fSDaniel Borkmann 	link = cp->link;
313053c8e1fSDaniel Borkmann 	/* The deletion request can either be without filled tuple in which
314053c8e1fSDaniel Borkmann 	 * case it gets populated here based on idx, or with filled tuple
315053c8e1fSDaniel Borkmann 	 * where the only thing we end up doing is the WARN_ON_ONCE() assert.
316053c8e1fSDaniel Borkmann 	 * If we hit a BPF link at the given index, it must not be removed
317053c8e1fSDaniel Borkmann 	 * from opts path.
318053c8e1fSDaniel Borkmann 	 */
319053c8e1fSDaniel Borkmann 	if (link && !tuple->link)
320053c8e1fSDaniel Borkmann 		return -EBUSY;
321053c8e1fSDaniel Borkmann 	WARN_ON_ONCE(tuple->prog && tuple->prog != prog);
322053c8e1fSDaniel Borkmann 	WARN_ON_ONCE(tuple->link && tuple->link != link);
323053c8e1fSDaniel Borkmann 	tuple->prog = prog;
324053c8e1fSDaniel Borkmann 	tuple->link = link;
325053c8e1fSDaniel Borkmann 	return 0;
326053c8e1fSDaniel Borkmann }
327053c8e1fSDaniel Borkmann 
bpf_mprog_detach(struct bpf_mprog_entry * entry,struct bpf_mprog_entry ** entry_new,struct bpf_prog * prog,struct bpf_link * link,u32 flags,u32 id_or_fd,u64 revision)328053c8e1fSDaniel Borkmann int bpf_mprog_detach(struct bpf_mprog_entry *entry,
329053c8e1fSDaniel Borkmann 		     struct bpf_mprog_entry **entry_new,
330053c8e1fSDaniel Borkmann 		     struct bpf_prog *prog, struct bpf_link *link,
331053c8e1fSDaniel Borkmann 		     u32 flags, u32 id_or_fd, u64 revision)
332053c8e1fSDaniel Borkmann {
333053c8e1fSDaniel Borkmann 	struct bpf_tuple rtuple, dtuple = {
334053c8e1fSDaniel Borkmann 		.prog = prog,
335053c8e1fSDaniel Borkmann 		.link = link,
336053c8e1fSDaniel Borkmann 	};
337053c8e1fSDaniel Borkmann 	int ret, idx = -ERANGE, tidx;
338053c8e1fSDaniel Borkmann 
339053c8e1fSDaniel Borkmann 	if (flags & BPF_F_REPLACE)
340053c8e1fSDaniel Borkmann 		return -EINVAL;
341053c8e1fSDaniel Borkmann 	if (revision && revision != bpf_mprog_revision(entry))
342053c8e1fSDaniel Borkmann 		return -ESTALE;
343d210f973SDaniel Borkmann 	if (!bpf_mprog_total(entry))
344d210f973SDaniel Borkmann 		return -ENOENT;
345053c8e1fSDaniel Borkmann 	ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd, flags,
346053c8e1fSDaniel Borkmann 				       prog ? prog->type :
347053c8e1fSDaniel Borkmann 				       BPF_PROG_TYPE_UNSPEC);
348053c8e1fSDaniel Borkmann 	if (ret)
349053c8e1fSDaniel Borkmann 		return ret;
350053c8e1fSDaniel Borkmann 	if (dtuple.prog) {
351053c8e1fSDaniel Borkmann 		tidx = bpf_mprog_pos_exact(entry, &dtuple);
352053c8e1fSDaniel Borkmann 		if (tidx < 0) {
353053c8e1fSDaniel Borkmann 			ret = tidx;
354053c8e1fSDaniel Borkmann 			goto out;
355053c8e1fSDaniel Borkmann 		}
356053c8e1fSDaniel Borkmann 		idx = tidx;
357053c8e1fSDaniel Borkmann 	}
358053c8e1fSDaniel Borkmann 	if (flags & BPF_F_BEFORE) {
359053c8e1fSDaniel Borkmann 		tidx = bpf_mprog_pos_before(entry, &rtuple);
360053c8e1fSDaniel Borkmann 		if (tidx < -1 || (idx >= -1 && tidx != idx)) {
361053c8e1fSDaniel Borkmann 			ret = tidx < -1 ? tidx : -ERANGE;
362053c8e1fSDaniel Borkmann 			goto out;
363053c8e1fSDaniel Borkmann 		}
364053c8e1fSDaniel Borkmann 		idx = tidx;
365053c8e1fSDaniel Borkmann 	}
366053c8e1fSDaniel Borkmann 	if (flags & BPF_F_AFTER) {
367053c8e1fSDaniel Borkmann 		tidx = bpf_mprog_pos_after(entry, &rtuple);
368053c8e1fSDaniel Borkmann 		if (tidx < -1 || (idx >= -1 && tidx != idx)) {
369053c8e1fSDaniel Borkmann 			ret = tidx < 0 ? tidx : -ERANGE;
370053c8e1fSDaniel Borkmann 			goto out;
371053c8e1fSDaniel Borkmann 		}
372053c8e1fSDaniel Borkmann 		idx = tidx;
373053c8e1fSDaniel Borkmann 	}
374053c8e1fSDaniel Borkmann 	if (idx < -1) {
375053c8e1fSDaniel Borkmann 		if (rtuple.prog || flags) {
376053c8e1fSDaniel Borkmann 			ret = -EINVAL;
377053c8e1fSDaniel Borkmann 			goto out;
378053c8e1fSDaniel Borkmann 		}
379053c8e1fSDaniel Borkmann 		idx = bpf_mprog_total(entry);
380053c8e1fSDaniel Borkmann 		flags = BPF_F_AFTER;
381053c8e1fSDaniel Borkmann 	}
382053c8e1fSDaniel Borkmann 	if (idx >= bpf_mprog_max()) {
383053c8e1fSDaniel Borkmann 		ret = -ERANGE;
384053c8e1fSDaniel Borkmann 		goto out;
385053c8e1fSDaniel Borkmann 	}
386053c8e1fSDaniel Borkmann 	ret = bpf_mprog_fetch(entry, &dtuple, idx);
387053c8e1fSDaniel Borkmann 	if (ret)
388053c8e1fSDaniel Borkmann 		goto out;
389053c8e1fSDaniel Borkmann 	ret = bpf_mprog_delete(entry, entry_new, &dtuple, idx);
390053c8e1fSDaniel Borkmann out:
391053c8e1fSDaniel Borkmann 	bpf_mprog_tuple_put(&rtuple);
392053c8e1fSDaniel Borkmann 	return ret;
393053c8e1fSDaniel Borkmann }
394053c8e1fSDaniel Borkmann 
bpf_mprog_query(const union bpf_attr * attr,union bpf_attr __user * uattr,struct bpf_mprog_entry * entry)395053c8e1fSDaniel Borkmann int bpf_mprog_query(const union bpf_attr *attr, union bpf_attr __user *uattr,
396053c8e1fSDaniel Borkmann 		    struct bpf_mprog_entry *entry)
397053c8e1fSDaniel Borkmann {
398053c8e1fSDaniel Borkmann 	u32 __user *uprog_flags, *ulink_flags;
399053c8e1fSDaniel Borkmann 	u32 __user *uprog_id, *ulink_id;
400053c8e1fSDaniel Borkmann 	struct bpf_mprog_fp *fp;
401053c8e1fSDaniel Borkmann 	struct bpf_mprog_cp *cp;
402053c8e1fSDaniel Borkmann 	struct bpf_prog *prog;
403053c8e1fSDaniel Borkmann 	const u32 flags = 0;
404*edfa9af0SDaniel Borkmann 	u32 id, count = 0;
405*edfa9af0SDaniel Borkmann 	u64 revision = 1;
406053c8e1fSDaniel Borkmann 	int i, ret = 0;
407053c8e1fSDaniel Borkmann 
408053c8e1fSDaniel Borkmann 	if (attr->query.query_flags || attr->query.attach_flags)
409053c8e1fSDaniel Borkmann 		return -EINVAL;
410*edfa9af0SDaniel Borkmann 	if (entry) {
411053c8e1fSDaniel Borkmann 		revision = bpf_mprog_revision(entry);
412053c8e1fSDaniel Borkmann 		count = bpf_mprog_total(entry);
413*edfa9af0SDaniel Borkmann 	}
414053c8e1fSDaniel Borkmann 	if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags)))
415053c8e1fSDaniel Borkmann 		return -EFAULT;
416053c8e1fSDaniel Borkmann 	if (copy_to_user(&uattr->query.revision, &revision, sizeof(revision)))
417053c8e1fSDaniel Borkmann 		return -EFAULT;
418053c8e1fSDaniel Borkmann 	if (copy_to_user(&uattr->query.count, &count, sizeof(count)))
419053c8e1fSDaniel Borkmann 		return -EFAULT;
420053c8e1fSDaniel Borkmann 	uprog_id = u64_to_user_ptr(attr->query.prog_ids);
421053c8e1fSDaniel Borkmann 	uprog_flags = u64_to_user_ptr(attr->query.prog_attach_flags);
422053c8e1fSDaniel Borkmann 	ulink_id = u64_to_user_ptr(attr->query.link_ids);
423053c8e1fSDaniel Borkmann 	ulink_flags = u64_to_user_ptr(attr->query.link_attach_flags);
424053c8e1fSDaniel Borkmann 	if (attr->query.count == 0 || !uprog_id || !count)
425053c8e1fSDaniel Borkmann 		return 0;
426053c8e1fSDaniel Borkmann 	if (attr->query.count < count) {
427053c8e1fSDaniel Borkmann 		count = attr->query.count;
428053c8e1fSDaniel Borkmann 		ret = -ENOSPC;
429053c8e1fSDaniel Borkmann 	}
430053c8e1fSDaniel Borkmann 	for (i = 0; i < bpf_mprog_max(); i++) {
431053c8e1fSDaniel Borkmann 		bpf_mprog_read(entry, i, &fp, &cp);
432053c8e1fSDaniel Borkmann 		prog = READ_ONCE(fp->prog);
433053c8e1fSDaniel Borkmann 		if (!prog)
434053c8e1fSDaniel Borkmann 			break;
435053c8e1fSDaniel Borkmann 		id = prog->aux->id;
436053c8e1fSDaniel Borkmann 		if (copy_to_user(uprog_id + i, &id, sizeof(id)))
437053c8e1fSDaniel Borkmann 			return -EFAULT;
438053c8e1fSDaniel Borkmann 		if (uprog_flags &&
439053c8e1fSDaniel Borkmann 		    copy_to_user(uprog_flags + i, &flags, sizeof(flags)))
440053c8e1fSDaniel Borkmann 			return -EFAULT;
441053c8e1fSDaniel Borkmann 		id = cp->link ? cp->link->id : 0;
442053c8e1fSDaniel Borkmann 		if (ulink_id &&
443053c8e1fSDaniel Borkmann 		    copy_to_user(ulink_id + i, &id, sizeof(id)))
444053c8e1fSDaniel Borkmann 			return -EFAULT;
445053c8e1fSDaniel Borkmann 		if (ulink_flags &&
446053c8e1fSDaniel Borkmann 		    copy_to_user(ulink_flags + i, &flags, sizeof(flags)))
447053c8e1fSDaniel Borkmann 			return -EFAULT;
448053c8e1fSDaniel Borkmann 		if (i + 1 == count)
449053c8e1fSDaniel Borkmann 			break;
450053c8e1fSDaniel Borkmann 	}
451053c8e1fSDaniel Borkmann 	return ret;
452053c8e1fSDaniel Borkmann }
453