| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright (c) 2023 Isovalent */
- #include <linux/bpf.h>
- #include <linux/bpf_mprog.h>
- static int bpf_mprog_link(struct bpf_tuple *tuple,
- u32 id_or_fd, u32 flags,
- enum bpf_prog_type type)
- {
- struct bpf_link *link = ERR_PTR(-EINVAL);
- bool id = flags & BPF_F_ID;
- if (id)
- link = bpf_link_by_id(id_or_fd);
- else if (id_or_fd)
- link = bpf_link_get_from_fd(id_or_fd);
- if (IS_ERR(link))
- return PTR_ERR(link);
- if (type && link->prog->type != type) {
- bpf_link_put(link);
- return -EINVAL;
- }
- tuple->link = link;
- tuple->prog = link->prog;
- return 0;
- }
- static int bpf_mprog_prog(struct bpf_tuple *tuple,
- u32 id_or_fd, u32 flags,
- enum bpf_prog_type type)
- {
- struct bpf_prog *prog = ERR_PTR(-EINVAL);
- bool id = flags & BPF_F_ID;
- if (id)
- prog = bpf_prog_by_id(id_or_fd);
- else if (id_or_fd)
- prog = bpf_prog_get(id_or_fd);
- if (IS_ERR(prog))
- return PTR_ERR(prog);
- if (type && prog->type != type) {
- bpf_prog_put(prog);
- return -EINVAL;
- }
- tuple->link = NULL;
- tuple->prog = prog;
- return 0;
- }
- static int bpf_mprog_tuple_relative(struct bpf_tuple *tuple,
- u32 id_or_fd, u32 flags,
- enum bpf_prog_type type)
- {
- bool link = flags & BPF_F_LINK;
- bool id = flags & BPF_F_ID;
- memset(tuple, 0, sizeof(*tuple));
- if (link)
- return bpf_mprog_link(tuple, id_or_fd, flags, type);
- /* If no relevant flag is set and no id_or_fd was passed, then
- * tuple link/prog is just NULLed. This is the case when before/
- * after selects first/last position without passing fd.
- */
- if (!id && !id_or_fd)
- return 0;
- return bpf_mprog_prog(tuple, id_or_fd, flags, type);
- }
- static void bpf_mprog_tuple_put(struct bpf_tuple *tuple)
- {
- if (tuple->link)
- bpf_link_put(tuple->link);
- else if (tuple->prog)
- bpf_prog_put(tuple->prog);
- }
- /* The bpf_mprog_{replace,delete}() operate on exact idx position with the
- * one exception that for deletion we support delete from front/back. In
- * case of front idx is -1, in case of back idx is bpf_mprog_total(entry).
- * Adjustment to first and last entry is trivial. The bpf_mprog_insert()
- * we have to deal with the following cases:
- *
- * idx + before:
- *
- * Insert P4 before P3: idx for old array is 1, idx for new array is 2,
- * hence we adjust target idx for the new array, so that memmove copies
- * P1 and P2 to the new entry, and we insert P4 into idx 2. Inserting
- * before P1 would have old idx -1 and new idx 0.
- *
- * +--+--+--+ +--+--+--+--+ +--+--+--+--+
- * |P1|P2|P3| ==> |P1|P2| |P3| ==> |P1|P2|P4|P3|
- * +--+--+--+ +--+--+--+--+ +--+--+--+--+
- *
- * idx + after:
- *
- * Insert P4 after P2: idx for old array is 2, idx for new array is 2.
- * Again, memmove copies P1 and P2 to the new entry, and we insert P4
- * into idx 2. Inserting after P3 would have both old/new idx at 4 aka
- * bpf_mprog_total(entry).
- *
- * +--+--+--+ +--+--+--+--+ +--+--+--+--+
- * |P1|P2|P3| ==> |P1|P2| |P3| ==> |P1|P2|P4|P3|
- * +--+--+--+ +--+--+--+--+ +--+--+--+--+
- */
- static int bpf_mprog_replace(struct bpf_mprog_entry *entry,
- struct bpf_mprog_entry **entry_new,
- struct bpf_tuple *ntuple, int idx)
- {
- struct bpf_mprog_fp *fp;
- struct bpf_mprog_cp *cp;
- struct bpf_prog *oprog;
- bpf_mprog_read(entry, idx, &fp, &cp);
- oprog = READ_ONCE(fp->prog);
- bpf_mprog_write(fp, cp, ntuple);
- if (!ntuple->link) {
- WARN_ON_ONCE(cp->link);
- bpf_prog_put(oprog);
- }
- *entry_new = entry;
- return 0;
- }
- static int bpf_mprog_insert(struct bpf_mprog_entry *entry,
- struct bpf_mprog_entry **entry_new,
- struct bpf_tuple *ntuple, int idx, u32 flags)
- {
- int total = bpf_mprog_total(entry);
- struct bpf_mprog_entry *peer;
- struct bpf_mprog_fp *fp;
- struct bpf_mprog_cp *cp;
- peer = bpf_mprog_peer(entry);
- bpf_mprog_entry_copy(peer, entry);
- if (idx == total)
- goto insert;
- else if (flags & BPF_F_BEFORE)
- idx += 1;
- bpf_mprog_entry_grow(peer, idx);
- insert:
- bpf_mprog_read(peer, idx, &fp, &cp);
- bpf_mprog_write(fp, cp, ntuple);
- bpf_mprog_inc(peer);
- *entry_new = peer;
- return 0;
- }
- static int bpf_mprog_delete(struct bpf_mprog_entry *entry,
- struct bpf_mprog_entry **entry_new,
- struct bpf_tuple *dtuple, int idx)
- {
- int total = bpf_mprog_total(entry);
- struct bpf_mprog_entry *peer;
- peer = bpf_mprog_peer(entry);
- bpf_mprog_entry_copy(peer, entry);
- if (idx == -1)
- idx = 0;
- else if (idx == total)
- idx = total - 1;
- bpf_mprog_entry_shrink(peer, idx);
- bpf_mprog_dec(peer);
- bpf_mprog_mark_for_release(peer, dtuple);
- *entry_new = peer;
- return 0;
- }
- /* In bpf_mprog_pos_*() we evaluate the target position for the BPF
- * program/link that needs to be replaced, inserted or deleted for
- * each "rule" independently. If all rules agree on that position
- * or existing element, then enact replacement, addition or deletion.
- * If this is not the case, then the request cannot be satisfied and
- * we bail out with an error.
- */
- static int bpf_mprog_pos_exact(struct bpf_mprog_entry *entry,
- struct bpf_tuple *tuple)
- {
- struct bpf_mprog_fp *fp;
- struct bpf_mprog_cp *cp;
- int i;
- for (i = 0; i < bpf_mprog_total(entry); i++) {
- bpf_mprog_read(entry, i, &fp, &cp);
- if (tuple->prog == READ_ONCE(fp->prog))
- return tuple->link == cp->link ? i : -EBUSY;
- }
- return -ENOENT;
- }
- static int bpf_mprog_pos_before(struct bpf_mprog_entry *entry,
- struct bpf_tuple *tuple)
- {
- struct bpf_mprog_fp *fp;
- struct bpf_mprog_cp *cp;
- int i;
- for (i = 0; i < bpf_mprog_total(entry); i++) {
- bpf_mprog_read(entry, i, &fp, &cp);
- if (tuple->prog == READ_ONCE(fp->prog) &&
- (!tuple->link || tuple->link == cp->link))
- return i - 1;
- }
- return tuple->prog ? -ENOENT : -1;
- }
- static int bpf_mprog_pos_after(struct bpf_mprog_entry *entry,
- struct bpf_tuple *tuple)
- {
- struct bpf_mprog_fp *fp;
- struct bpf_mprog_cp *cp;
- int i;
- for (i = 0; i < bpf_mprog_total(entry); i++) {
- bpf_mprog_read(entry, i, &fp, &cp);
- if (tuple->prog == READ_ONCE(fp->prog) &&
- (!tuple->link || tuple->link == cp->link))
- return i + 1;
- }
- return tuple->prog ? -ENOENT : bpf_mprog_total(entry);
- }
- int 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)
- {
- struct bpf_tuple rtuple, ntuple = {
- .prog = prog_new,
- .link = link,
- }, otuple = {
- .prog = prog_old,
- .link = link,
- };
- int ret, idx = -ERANGE, tidx;
- if (revision && revision != bpf_mprog_revision(entry))
- return -ESTALE;
- if (bpf_mprog_exists(entry, prog_new))
- return -EEXIST;
- ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd,
- flags & ~BPF_F_REPLACE,
- prog_new->type);
- if (ret)
- return ret;
- if (flags & BPF_F_REPLACE) {
- tidx = bpf_mprog_pos_exact(entry, &otuple);
- if (tidx < 0) {
- ret = tidx;
- goto out;
- }
- idx = tidx;
- } else if (bpf_mprog_total(entry) == bpf_mprog_max()) {
- ret = -ERANGE;
- goto out;
- }
- if (flags & BPF_F_BEFORE) {
- tidx = bpf_mprog_pos_before(entry, &rtuple);
- if (tidx < -1 || (idx >= -1 && tidx != idx)) {
- ret = tidx < -1 ? tidx : -ERANGE;
- goto out;
- }
- idx = tidx;
- }
- if (flags & BPF_F_AFTER) {
- tidx = bpf_mprog_pos_after(entry, &rtuple);
- if (tidx < -1 || (idx >= -1 && tidx != idx)) {
- ret = tidx < 0 ? tidx : -ERANGE;
- goto out;
- }
- idx = tidx;
- }
- if (idx < -1) {
- if (rtuple.prog || flags) {
- ret = -EINVAL;
- goto out;
- }
- idx = bpf_mprog_total(entry);
- flags = BPF_F_AFTER;
- }
- if (idx >= bpf_mprog_max()) {
- ret = -ERANGE;
- goto out;
- }
- if (flags & BPF_F_REPLACE)
- ret = bpf_mprog_replace(entry, entry_new, &ntuple, idx);
- else
- ret = bpf_mprog_insert(entry, entry_new, &ntuple, idx, flags);
- out:
- bpf_mprog_tuple_put(&rtuple);
- return ret;
- }
- static int bpf_mprog_fetch(struct bpf_mprog_entry *entry,
- struct bpf_tuple *tuple, int idx)
- {
- int total = bpf_mprog_total(entry);
- struct bpf_mprog_cp *cp;
- struct bpf_mprog_fp *fp;
- struct bpf_prog *prog;
- struct bpf_link *link;
- if (idx == -1)
- idx = 0;
- else if (idx == total)
- idx = total - 1;
- bpf_mprog_read(entry, idx, &fp, &cp);
- prog = READ_ONCE(fp->prog);
- link = cp->link;
- /* The deletion request can either be without filled tuple in which
- * case it gets populated here based on idx, or with filled tuple
- * where the only thing we end up doing is the WARN_ON_ONCE() assert.
- * If we hit a BPF link at the given index, it must not be removed
- * from opts path.
- */
- if (link && !tuple->link)
- return -EBUSY;
- WARN_ON_ONCE(tuple->prog && tuple->prog != prog);
- WARN_ON_ONCE(tuple->link && tuple->link != link);
- tuple->prog = prog;
- tuple->link = link;
- return 0;
- }
- int 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)
- {
- struct bpf_tuple rtuple, dtuple = {
- .prog = prog,
- .link = link,
- };
- int ret, idx = -ERANGE, tidx;
- if (flags & BPF_F_REPLACE)
- return -EINVAL;
- if (revision && revision != bpf_mprog_revision(entry))
- return -ESTALE;
- if (!bpf_mprog_total(entry))
- return -ENOENT;
- ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd, flags,
- prog ? prog->type :
- BPF_PROG_TYPE_UNSPEC);
- if (ret)
- return ret;
- if (dtuple.prog) {
- tidx = bpf_mprog_pos_exact(entry, &dtuple);
- if (tidx < 0) {
- ret = tidx;
- goto out;
- }
- idx = tidx;
- }
- if (flags & BPF_F_BEFORE) {
- tidx = bpf_mprog_pos_before(entry, &rtuple);
- if (tidx < -1 || (idx >= -1 && tidx != idx)) {
- ret = tidx < -1 ? tidx : -ERANGE;
- goto out;
- }
- idx = tidx;
- }
- if (flags & BPF_F_AFTER) {
- tidx = bpf_mprog_pos_after(entry, &rtuple);
- if (tidx < -1 || (idx >= -1 && tidx != idx)) {
- ret = tidx < 0 ? tidx : -ERANGE;
- goto out;
- }
- idx = tidx;
- }
- if (idx < -1) {
- if (rtuple.prog || flags) {
- ret = -EINVAL;
- goto out;
- }
- idx = bpf_mprog_total(entry);
- flags = BPF_F_AFTER;
- }
- if (idx >= bpf_mprog_max()) {
- ret = -ERANGE;
- goto out;
- }
- ret = bpf_mprog_fetch(entry, &dtuple, idx);
- if (ret)
- goto out;
- ret = bpf_mprog_delete(entry, entry_new, &dtuple, idx);
- out:
- bpf_mprog_tuple_put(&rtuple);
- return ret;
- }
- int bpf_mprog_query(const union bpf_attr *attr, union bpf_attr __user *uattr,
- struct bpf_mprog_entry *entry)
- {
- u32 __user *uprog_flags, *ulink_flags;
- u32 __user *uprog_id, *ulink_id;
- struct bpf_mprog_fp *fp;
- struct bpf_mprog_cp *cp;
- struct bpf_prog *prog;
- const u32 flags = 0;
- u32 id, count = 0;
- u64 revision = 1;
- int i, ret = 0;
- if (attr->query.query_flags || attr->query.attach_flags)
- return -EINVAL;
- if (entry) {
- revision = bpf_mprog_revision(entry);
- count = bpf_mprog_total(entry);
- }
- if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags)))
- return -EFAULT;
- if (copy_to_user(&uattr->query.revision, &revision, sizeof(revision)))
- return -EFAULT;
- if (copy_to_user(&uattr->query.count, &count, sizeof(count)))
- return -EFAULT;
- uprog_id = u64_to_user_ptr(attr->query.prog_ids);
- uprog_flags = u64_to_user_ptr(attr->query.prog_attach_flags);
- ulink_id = u64_to_user_ptr(attr->query.link_ids);
- ulink_flags = u64_to_user_ptr(attr->query.link_attach_flags);
- if (attr->query.count == 0 || !uprog_id || !count)
- return 0;
- if (attr->query.count < count) {
- count = attr->query.count;
- ret = -ENOSPC;
- }
- for (i = 0; i < bpf_mprog_max(); i++) {
- bpf_mprog_read(entry, i, &fp, &cp);
- prog = READ_ONCE(fp->prog);
- if (!prog)
- break;
- id = prog->aux->id;
- if (copy_to_user(uprog_id + i, &id, sizeof(id)))
- return -EFAULT;
- if (uprog_flags &&
- copy_to_user(uprog_flags + i, &flags, sizeof(flags)))
- return -EFAULT;
- id = cp->link ? cp->link->id : 0;
- if (ulink_id &&
- copy_to_user(ulink_id + i, &id, sizeof(id)))
- return -EFAULT;
- if (ulink_flags &&
- copy_to_user(ulink_flags + i, &flags, sizeof(flags)))
- return -EFAULT;
- if (i + 1 == count)
- break;
- }
- return ret;
- }
|