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 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 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 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 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 */ 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 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 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 */ 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 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 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 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; 256*f9b0e108SDaniel Borkmann } else if (bpf_mprog_total(entry) == bpf_mprog_max()) { 257*f9b0e108SDaniel Borkmann ret = -ERANGE; 258*f9b0e108SDaniel 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 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 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 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; 404053c8e1fSDaniel Borkmann int i, ret = 0; 405053c8e1fSDaniel Borkmann u32 id, count; 406053c8e1fSDaniel Borkmann u64 revision; 407053c8e1fSDaniel Borkmann 408053c8e1fSDaniel Borkmann if (attr->query.query_flags || attr->query.attach_flags) 409053c8e1fSDaniel Borkmann return -EINVAL; 410053c8e1fSDaniel Borkmann revision = bpf_mprog_revision(entry); 411053c8e1fSDaniel Borkmann count = bpf_mprog_total(entry); 412053c8e1fSDaniel Borkmann if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) 413053c8e1fSDaniel Borkmann return -EFAULT; 414053c8e1fSDaniel Borkmann if (copy_to_user(&uattr->query.revision, &revision, sizeof(revision))) 415053c8e1fSDaniel Borkmann return -EFAULT; 416053c8e1fSDaniel Borkmann if (copy_to_user(&uattr->query.count, &count, sizeof(count))) 417053c8e1fSDaniel Borkmann return -EFAULT; 418053c8e1fSDaniel Borkmann uprog_id = u64_to_user_ptr(attr->query.prog_ids); 419053c8e1fSDaniel Borkmann uprog_flags = u64_to_user_ptr(attr->query.prog_attach_flags); 420053c8e1fSDaniel Borkmann ulink_id = u64_to_user_ptr(attr->query.link_ids); 421053c8e1fSDaniel Borkmann ulink_flags = u64_to_user_ptr(attr->query.link_attach_flags); 422053c8e1fSDaniel Borkmann if (attr->query.count == 0 || !uprog_id || !count) 423053c8e1fSDaniel Borkmann return 0; 424053c8e1fSDaniel Borkmann if (attr->query.count < count) { 425053c8e1fSDaniel Borkmann count = attr->query.count; 426053c8e1fSDaniel Borkmann ret = -ENOSPC; 427053c8e1fSDaniel Borkmann } 428053c8e1fSDaniel Borkmann for (i = 0; i < bpf_mprog_max(); i++) { 429053c8e1fSDaniel Borkmann bpf_mprog_read(entry, i, &fp, &cp); 430053c8e1fSDaniel Borkmann prog = READ_ONCE(fp->prog); 431053c8e1fSDaniel Borkmann if (!prog) 432053c8e1fSDaniel Borkmann break; 433053c8e1fSDaniel Borkmann id = prog->aux->id; 434053c8e1fSDaniel Borkmann if (copy_to_user(uprog_id + i, &id, sizeof(id))) 435053c8e1fSDaniel Borkmann return -EFAULT; 436053c8e1fSDaniel Borkmann if (uprog_flags && 437053c8e1fSDaniel Borkmann copy_to_user(uprog_flags + i, &flags, sizeof(flags))) 438053c8e1fSDaniel Borkmann return -EFAULT; 439053c8e1fSDaniel Borkmann id = cp->link ? cp->link->id : 0; 440053c8e1fSDaniel Borkmann if (ulink_id && 441053c8e1fSDaniel Borkmann copy_to_user(ulink_id + i, &id, sizeof(id))) 442053c8e1fSDaniel Borkmann return -EFAULT; 443053c8e1fSDaniel Borkmann if (ulink_flags && 444053c8e1fSDaniel Borkmann copy_to_user(ulink_flags + i, &flags, sizeof(flags))) 445053c8e1fSDaniel Borkmann return -EFAULT; 446053c8e1fSDaniel Borkmann if (i + 1 == count) 447053c8e1fSDaniel Borkmann break; 448053c8e1fSDaniel Borkmann } 449053c8e1fSDaniel Borkmann return ret; 450053c8e1fSDaniel Borkmann } 451