1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2023 Isovalent */ 3 4 #include <linux/bpf.h> 5 #include <linux/bpf_mprog.h> 6 7 static int bpf_mprog_link(struct bpf_tuple *tuple, 8 u32 id_or_fd, u32 flags, 9 enum bpf_prog_type type) 10 { 11 struct bpf_link *link = ERR_PTR(-EINVAL); 12 bool id = flags & BPF_F_ID; 13 14 if (id) 15 link = bpf_link_by_id(id_or_fd); 16 else if (id_or_fd) 17 link = bpf_link_get_from_fd(id_or_fd); 18 if (IS_ERR(link)) 19 return PTR_ERR(link); 20 if (type && link->prog->type != type) { 21 bpf_link_put(link); 22 return -EINVAL; 23 } 24 25 tuple->link = link; 26 tuple->prog = link->prog; 27 return 0; 28 } 29 30 static int bpf_mprog_prog(struct bpf_tuple *tuple, 31 u32 id_or_fd, u32 flags, 32 enum bpf_prog_type type) 33 { 34 struct bpf_prog *prog = ERR_PTR(-EINVAL); 35 bool id = flags & BPF_F_ID; 36 37 if (id) 38 prog = bpf_prog_by_id(id_or_fd); 39 else if (id_or_fd) 40 prog = bpf_prog_get(id_or_fd); 41 if (IS_ERR(prog)) 42 return PTR_ERR(prog); 43 if (type && prog->type != type) { 44 bpf_prog_put(prog); 45 return -EINVAL; 46 } 47 48 tuple->link = NULL; 49 tuple->prog = prog; 50 return 0; 51 } 52 53 static int bpf_mprog_tuple_relative(struct bpf_tuple *tuple, 54 u32 id_or_fd, u32 flags, 55 enum bpf_prog_type type) 56 { 57 bool link = flags & BPF_F_LINK; 58 bool id = flags & BPF_F_ID; 59 60 memset(tuple, 0, sizeof(*tuple)); 61 if (link) 62 return bpf_mprog_link(tuple, id_or_fd, flags, type); 63 /* If no relevant flag is set and no id_or_fd was passed, then 64 * tuple link/prog is just NULLed. This is the case when before/ 65 * after selects first/last position without passing fd. 66 */ 67 if (!id && !id_or_fd) 68 return 0; 69 return bpf_mprog_prog(tuple, id_or_fd, flags, type); 70 } 71 72 static void bpf_mprog_tuple_put(struct bpf_tuple *tuple) 73 { 74 if (tuple->link) 75 bpf_link_put(tuple->link); 76 else if (tuple->prog) 77 bpf_prog_put(tuple->prog); 78 } 79 80 /* The bpf_mprog_{replace,delete}() operate on exact idx position with the 81 * one exception that for deletion we support delete from front/back. In 82 * case of front idx is -1, in case of back idx is bpf_mprog_total(entry). 83 * Adjustment to first and last entry is trivial. The bpf_mprog_insert() 84 * we have to deal with the following cases: 85 * 86 * idx + before: 87 * 88 * Insert P4 before P3: idx for old array is 1, idx for new array is 2, 89 * hence we adjust target idx for the new array, so that memmove copies 90 * P1 and P2 to the new entry, and we insert P4 into idx 2. Inserting 91 * before P1 would have old idx -1 and new idx 0. 92 * 93 * +--+--+--+ +--+--+--+--+ +--+--+--+--+ 94 * |P1|P2|P3| ==> |P1|P2| |P3| ==> |P1|P2|P4|P3| 95 * +--+--+--+ +--+--+--+--+ +--+--+--+--+ 96 * 97 * idx + after: 98 * 99 * Insert P4 after P2: idx for old array is 2, idx for new array is 2. 100 * Again, memmove copies P1 and P2 to the new entry, and we insert P4 101 * into idx 2. Inserting after P3 would have both old/new idx at 4 aka 102 * bpf_mprog_total(entry). 103 * 104 * +--+--+--+ +--+--+--+--+ +--+--+--+--+ 105 * |P1|P2|P3| ==> |P1|P2| |P3| ==> |P1|P2|P4|P3| 106 * +--+--+--+ +--+--+--+--+ +--+--+--+--+ 107 */ 108 static int bpf_mprog_replace(struct bpf_mprog_entry *entry, 109 struct bpf_mprog_entry **entry_new, 110 struct bpf_tuple *ntuple, int idx) 111 { 112 struct bpf_mprog_fp *fp; 113 struct bpf_mprog_cp *cp; 114 struct bpf_prog *oprog; 115 116 bpf_mprog_read(entry, idx, &fp, &cp); 117 oprog = READ_ONCE(fp->prog); 118 bpf_mprog_write(fp, cp, ntuple); 119 if (!ntuple->link) { 120 WARN_ON_ONCE(cp->link); 121 bpf_prog_put(oprog); 122 } 123 *entry_new = entry; 124 return 0; 125 } 126 127 static int bpf_mprog_insert(struct bpf_mprog_entry *entry, 128 struct bpf_mprog_entry **entry_new, 129 struct bpf_tuple *ntuple, int idx, u32 flags) 130 { 131 int total = bpf_mprog_total(entry); 132 struct bpf_mprog_entry *peer; 133 struct bpf_mprog_fp *fp; 134 struct bpf_mprog_cp *cp; 135 136 peer = bpf_mprog_peer(entry); 137 bpf_mprog_entry_copy(peer, entry); 138 if (idx == total) 139 goto insert; 140 else if (flags & BPF_F_BEFORE) 141 idx += 1; 142 bpf_mprog_entry_grow(peer, idx); 143 insert: 144 bpf_mprog_read(peer, idx, &fp, &cp); 145 bpf_mprog_write(fp, cp, ntuple); 146 bpf_mprog_inc(peer); 147 *entry_new = peer; 148 return 0; 149 } 150 151 static int bpf_mprog_delete(struct bpf_mprog_entry *entry, 152 struct bpf_mprog_entry **entry_new, 153 struct bpf_tuple *dtuple, int idx) 154 { 155 int total = bpf_mprog_total(entry); 156 struct bpf_mprog_entry *peer; 157 158 peer = bpf_mprog_peer(entry); 159 bpf_mprog_entry_copy(peer, entry); 160 if (idx == -1) 161 idx = 0; 162 else if (idx == total) 163 idx = total - 1; 164 bpf_mprog_entry_shrink(peer, idx); 165 bpf_mprog_dec(peer); 166 bpf_mprog_mark_for_release(peer, dtuple); 167 *entry_new = peer; 168 return 0; 169 } 170 171 /* In bpf_mprog_pos_*() we evaluate the target position for the BPF 172 * program/link that needs to be replaced, inserted or deleted for 173 * each "rule" independently. If all rules agree on that position 174 * or existing element, then enact replacement, addition or deletion. 175 * If this is not the case, then the request cannot be satisfied and 176 * we bail out with an error. 177 */ 178 static int bpf_mprog_pos_exact(struct bpf_mprog_entry *entry, 179 struct bpf_tuple *tuple) 180 { 181 struct bpf_mprog_fp *fp; 182 struct bpf_mprog_cp *cp; 183 int i; 184 185 for (i = 0; i < bpf_mprog_total(entry); i++) { 186 bpf_mprog_read(entry, i, &fp, &cp); 187 if (tuple->prog == READ_ONCE(fp->prog)) 188 return tuple->link == cp->link ? i : -EBUSY; 189 } 190 return -ENOENT; 191 } 192 193 static int bpf_mprog_pos_before(struct bpf_mprog_entry *entry, 194 struct bpf_tuple *tuple) 195 { 196 struct bpf_mprog_fp *fp; 197 struct bpf_mprog_cp *cp; 198 int i; 199 200 for (i = 0; i < bpf_mprog_total(entry); i++) { 201 bpf_mprog_read(entry, i, &fp, &cp); 202 if (tuple->prog == READ_ONCE(fp->prog) && 203 (!tuple->link || tuple->link == cp->link)) 204 return i - 1; 205 } 206 return tuple->prog ? -ENOENT : -1; 207 } 208 209 static int bpf_mprog_pos_after(struct bpf_mprog_entry *entry, 210 struct bpf_tuple *tuple) 211 { 212 struct bpf_mprog_fp *fp; 213 struct bpf_mprog_cp *cp; 214 int i; 215 216 for (i = 0; i < bpf_mprog_total(entry); i++) { 217 bpf_mprog_read(entry, i, &fp, &cp); 218 if (tuple->prog == READ_ONCE(fp->prog) && 219 (!tuple->link || tuple->link == cp->link)) 220 return i + 1; 221 } 222 return tuple->prog ? -ENOENT : bpf_mprog_total(entry); 223 } 224 225 int bpf_mprog_attach(struct bpf_mprog_entry *entry, 226 struct bpf_mprog_entry **entry_new, 227 struct bpf_prog *prog_new, struct bpf_link *link, 228 struct bpf_prog *prog_old, 229 u32 flags, u32 id_or_fd, u64 revision) 230 { 231 struct bpf_tuple rtuple, ntuple = { 232 .prog = prog_new, 233 .link = link, 234 }, otuple = { 235 .prog = prog_old, 236 .link = link, 237 }; 238 int ret, idx = -ERANGE, tidx; 239 240 if (revision && revision != bpf_mprog_revision(entry)) 241 return -ESTALE; 242 if (bpf_mprog_exists(entry, prog_new)) 243 return -EEXIST; 244 ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd, 245 flags & ~BPF_F_REPLACE, 246 prog_new->type); 247 if (ret) 248 return ret; 249 if (flags & BPF_F_REPLACE) { 250 tidx = bpf_mprog_pos_exact(entry, &otuple); 251 if (tidx < 0) { 252 ret = tidx; 253 goto out; 254 } 255 idx = tidx; 256 } else if (bpf_mprog_total(entry) == bpf_mprog_max()) { 257 ret = -ERANGE; 258 goto out; 259 } 260 if (flags & BPF_F_BEFORE) { 261 tidx = bpf_mprog_pos_before(entry, &rtuple); 262 if (tidx < -1 || (idx >= -1 && tidx != idx)) { 263 ret = tidx < -1 ? tidx : -ERANGE; 264 goto out; 265 } 266 idx = tidx; 267 } 268 if (flags & BPF_F_AFTER) { 269 tidx = bpf_mprog_pos_after(entry, &rtuple); 270 if (tidx < -1 || (idx >= -1 && tidx != idx)) { 271 ret = tidx < 0 ? tidx : -ERANGE; 272 goto out; 273 } 274 idx = tidx; 275 } 276 if (idx < -1) { 277 if (rtuple.prog || flags) { 278 ret = -EINVAL; 279 goto out; 280 } 281 idx = bpf_mprog_total(entry); 282 flags = BPF_F_AFTER; 283 } 284 if (idx >= bpf_mprog_max()) { 285 ret = -ERANGE; 286 goto out; 287 } 288 if (flags & BPF_F_REPLACE) 289 ret = bpf_mprog_replace(entry, entry_new, &ntuple, idx); 290 else 291 ret = bpf_mprog_insert(entry, entry_new, &ntuple, idx, flags); 292 out: 293 bpf_mprog_tuple_put(&rtuple); 294 return ret; 295 } 296 297 static int bpf_mprog_fetch(struct bpf_mprog_entry *entry, 298 struct bpf_tuple *tuple, int idx) 299 { 300 int total = bpf_mprog_total(entry); 301 struct bpf_mprog_cp *cp; 302 struct bpf_mprog_fp *fp; 303 struct bpf_prog *prog; 304 struct bpf_link *link; 305 306 if (idx == -1) 307 idx = 0; 308 else if (idx == total) 309 idx = total - 1; 310 bpf_mprog_read(entry, idx, &fp, &cp); 311 prog = READ_ONCE(fp->prog); 312 link = cp->link; 313 /* The deletion request can either be without filled tuple in which 314 * case it gets populated here based on idx, or with filled tuple 315 * where the only thing we end up doing is the WARN_ON_ONCE() assert. 316 * If we hit a BPF link at the given index, it must not be removed 317 * from opts path. 318 */ 319 if (link && !tuple->link) 320 return -EBUSY; 321 WARN_ON_ONCE(tuple->prog && tuple->prog != prog); 322 WARN_ON_ONCE(tuple->link && tuple->link != link); 323 tuple->prog = prog; 324 tuple->link = link; 325 return 0; 326 } 327 328 int bpf_mprog_detach(struct bpf_mprog_entry *entry, 329 struct bpf_mprog_entry **entry_new, 330 struct bpf_prog *prog, struct bpf_link *link, 331 u32 flags, u32 id_or_fd, u64 revision) 332 { 333 struct bpf_tuple rtuple, dtuple = { 334 .prog = prog, 335 .link = link, 336 }; 337 int ret, idx = -ERANGE, tidx; 338 339 if (flags & BPF_F_REPLACE) 340 return -EINVAL; 341 if (revision && revision != bpf_mprog_revision(entry)) 342 return -ESTALE; 343 if (!bpf_mprog_total(entry)) 344 return -ENOENT; 345 ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd, flags, 346 prog ? prog->type : 347 BPF_PROG_TYPE_UNSPEC); 348 if (ret) 349 return ret; 350 if (dtuple.prog) { 351 tidx = bpf_mprog_pos_exact(entry, &dtuple); 352 if (tidx < 0) { 353 ret = tidx; 354 goto out; 355 } 356 idx = tidx; 357 } 358 if (flags & BPF_F_BEFORE) { 359 tidx = bpf_mprog_pos_before(entry, &rtuple); 360 if (tidx < -1 || (idx >= -1 && tidx != idx)) { 361 ret = tidx < -1 ? tidx : -ERANGE; 362 goto out; 363 } 364 idx = tidx; 365 } 366 if (flags & BPF_F_AFTER) { 367 tidx = bpf_mprog_pos_after(entry, &rtuple); 368 if (tidx < -1 || (idx >= -1 && tidx != idx)) { 369 ret = tidx < 0 ? tidx : -ERANGE; 370 goto out; 371 } 372 idx = tidx; 373 } 374 if (idx < -1) { 375 if (rtuple.prog || flags) { 376 ret = -EINVAL; 377 goto out; 378 } 379 idx = bpf_mprog_total(entry); 380 flags = BPF_F_AFTER; 381 } 382 if (idx >= bpf_mprog_max()) { 383 ret = -ERANGE; 384 goto out; 385 } 386 ret = bpf_mprog_fetch(entry, &dtuple, idx); 387 if (ret) 388 goto out; 389 ret = bpf_mprog_delete(entry, entry_new, &dtuple, idx); 390 out: 391 bpf_mprog_tuple_put(&rtuple); 392 return ret; 393 } 394 395 int bpf_mprog_query(const union bpf_attr *attr, union bpf_attr __user *uattr, 396 struct bpf_mprog_entry *entry) 397 { 398 u32 __user *uprog_flags, *ulink_flags; 399 u32 __user *uprog_id, *ulink_id; 400 struct bpf_mprog_fp *fp; 401 struct bpf_mprog_cp *cp; 402 struct bpf_prog *prog; 403 const u32 flags = 0; 404 u32 id, count = 0; 405 u64 revision = 1; 406 int i, ret = 0; 407 408 if (attr->query.query_flags || attr->query.attach_flags) 409 return -EINVAL; 410 if (entry) { 411 revision = bpf_mprog_revision(entry); 412 count = bpf_mprog_total(entry); 413 } 414 if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) 415 return -EFAULT; 416 if (copy_to_user(&uattr->query.revision, &revision, sizeof(revision))) 417 return -EFAULT; 418 if (copy_to_user(&uattr->query.count, &count, sizeof(count))) 419 return -EFAULT; 420 uprog_id = u64_to_user_ptr(attr->query.prog_ids); 421 uprog_flags = u64_to_user_ptr(attr->query.prog_attach_flags); 422 ulink_id = u64_to_user_ptr(attr->query.link_ids); 423 ulink_flags = u64_to_user_ptr(attr->query.link_attach_flags); 424 if (attr->query.count == 0 || !uprog_id || !count) 425 return 0; 426 if (attr->query.count < count) { 427 count = attr->query.count; 428 ret = -ENOSPC; 429 } 430 for (i = 0; i < bpf_mprog_max(); i++) { 431 bpf_mprog_read(entry, i, &fp, &cp); 432 prog = READ_ONCE(fp->prog); 433 if (!prog) 434 break; 435 id = prog->aux->id; 436 if (copy_to_user(uprog_id + i, &id, sizeof(id))) 437 return -EFAULT; 438 if (uprog_flags && 439 copy_to_user(uprog_flags + i, &flags, sizeof(flags))) 440 return -EFAULT; 441 id = cp->link ? cp->link->id : 0; 442 if (ulink_id && 443 copy_to_user(ulink_id + i, &id, sizeof(id))) 444 return -EFAULT; 445 if (ulink_flags && 446 copy_to_user(ulink_flags + i, &flags, sizeof(flags))) 447 return -EFAULT; 448 if (i + 1 == count) 449 break; 450 } 451 return ret; 452 } 453
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.