From patchwork Mon Jun 17 18:01:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanislav Fomichev X-Patchwork-Id: 1117247 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: incoming-bpf@patchwork.ozlabs.org Delivered-To: patchwork-incoming-bpf@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=bpf-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="LOoRZR/x"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 45SJtN1TzMz9sNm for ; Tue, 18 Jun 2019 04:01:16 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726200AbfFQSBP (ORCPT ); Mon, 17 Jun 2019 14:01:15 -0400 Received: from mail-oi1-f202.google.com ([209.85.167.202]:54926 "EHLO mail-oi1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726005AbfFQSBP (ORCPT ); Mon, 17 Jun 2019 14:01:15 -0400 Received: by mail-oi1-f202.google.com with SMTP id w123so3830221oie.21 for ; Mon, 17 Jun 2019 11:01:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=LHtAu5yhp2yyhz6Lnk7y4xvzh0FtUxjXp2dvzucLTYQ=; b=LOoRZR/xMEMAcVO/rdbY22ZuV3sYiv/amn4a60MpSExbAmeT4kYkqp1FSFuQ92AHlQ H1z6BT5yAd6HPGgigZYVKOviM6g+m+IASJ8IacYJtGbF0ee7Lc2zsbgNo0KVzj+SD7m7 xMn54KFkMBmy2X3l4jQvw1v23bxvqi5vYkCDLTvyZORsrHsjVwlrpbce+R1TKV3trnAz BV2Y60/46k3Q30iuUtCNbXZJS7y5PbvkOVA6WzQG9sc3fuxWN1iii82FX3s7k3aLcPuP xM0pzSSPT0U98kbz55IjUA3dnO93+PYVdbZJA7paZuo8Wr8vLVUemB53me28mtRJQbNd rkfw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=LHtAu5yhp2yyhz6Lnk7y4xvzh0FtUxjXp2dvzucLTYQ=; b=Z/NdTcGPTiLjofbLjdt9neQw/UCbjU9zQF+6UYbDqt2V8ey+KdCLTsrCGfHZBZabsN 0znvEqqopUCvnYIfBiUPXW9z0G23Nh5kfBUSXfkUTbUcovnmbPN5FliJ+AgP97c/WwH4 TSVKdxdZPUaWegrMkuwthIW8AMfMEFxpWQKHaceEknEt5UcxppCjpHZwNyIobgNt84tU J8kwC3c5OvvoicRR6VD7TJQTLPnHoYBpqVYn6hqRWiknRuD6hIR7HNMkGHwxgL5mmK/s O94GhArCZdyG+cEGlWSNhG9CVCoJDE4dkGF4kVhCLEE09ZKxGPOXxVC2OWNIS2PqPyo9 x4DA== X-Gm-Message-State: APjAAAWxm3/+46vrUqhQ+rRTzDeGT8MUJGvfCkBxxF6gJgm7seBVzIH2 W88xGAJeM8qaFXdb1CeFjuYAw8A= X-Google-Smtp-Source: APXvYqyoPcRb9+WnFjIF47/feMaEJJIP89zOrd1sN/3jPLOblvpmVBknnQ0f6WPU3jagU6MoRAQLPgE= X-Received: by 2002:aca:494c:: with SMTP id w73mr128960oia.31.1560794474329; Mon, 17 Jun 2019 11:01:14 -0700 (PDT) Date: Mon, 17 Jun 2019 11:01:01 -0700 In-Reply-To: <20190617180109.34950-1-sdf@google.com> Message-Id: <20190617180109.34950-2-sdf@google.com> Mime-Version: 1.0 References: <20190617180109.34950-1-sdf@google.com> X-Mailer: git-send-email 2.22.0.410.gd8fdbe21b5-goog Subject: [PATCH bpf-next v6 1/9] bpf: implement getsockopt and setsockopt hooks From: Stanislav Fomichev To: netdev@vger.kernel.org, bpf@vger.kernel.org Cc: davem@davemloft.net, ast@kernel.org, daniel@iogearbox.net, Stanislav Fomichev , Martin Lau Sender: bpf-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org Implement new BPF_PROG_TYPE_CGROUP_SOCKOPT program type and BPF_CGROUP_{G,S}ETSOCKOPT cgroup hooks. BPF_CGROUP_SETSOCKOPT get a read-only view of the setsockopt arguments. BPF_CGROUP_GETSOCKOPT can modify the supplied buffer. Both of them reuse existing PTR_TO_PACKET{,_END} infrastructure. The buffer memory is pre-allocated (because I don't think there is a precedent for working with __user memory from bpf). This might be slow to do for each {s,g}etsockopt call, that's why I've added __cgroup_bpf_prog_array_is_empty that exits early if there is nothing attached to a cgroup. Note, however, that there is a race between __cgroup_bpf_prog_array_is_empty and BPF_PROG_RUN_ARRAY where cgroup program layout might have changed; this should not be a problem because in general there is a race between multiple calls to {s,g}etsocktop and user adding/removing bpf progs from a cgroup. The return code of the BPF program is handled as follows: * 0: EPERM * 1: success, execute kernel {s,g}etsockopt path after BPF prog exits * 2: success, do _not_ execute kernel {s,g}etsockopt path after BPF prog exits Note that if 0 or 2 is returned from BPF program, no further BPF program in the cgroup hierarchy is executed. This is in contrast with any existing per-cgroup BPF attach_type. v6: * rework cgroup chaining; stop as soon as bpf program returns 0 or 2; see patch with the documentation for the details * drop Andrii's and Martin's Acked-by (not sure they are comfortable with the new state of things) v5: * skip copy_to_user() and put_user() when ret == 0 (Martin Lau) v4: * don't export bpf_sk_fullsock helper (Martin Lau) * size != sizeof(__u64) for uapi pointers (Martin Lau) * offsetof instead of bpf_ctx_range when checking ctx access (Martin Lau) v3: * typos in BPF_PROG_CGROUP_SOCKOPT_RUN_ARRAY comments (Andrii Nakryiko) * reverse christmas tree in BPF_PROG_CGROUP_SOCKOPT_RUN_ARRAY (Andrii Nakryiko) * use __bpf_md_ptr instead of __u32 for optval{,_end} (Martin Lau) * use BPF_FIELD_SIZEOF() for consistency (Martin Lau) * new CG_SOCKOPT_ACCESS macro to wrap repeated parts v2: * moved bpf_sockopt_kern fields around to remove a hole (Martin Lau) * aligned bpf_sockopt_kern->buf to 8 bytes (Martin Lau) * bpf_prog_array_is_empty instead of bpf_prog_array_length (Martin Lau) * added [0,2] return code check to verifier (Martin Lau) * dropped unused buf[64] from the stack (Martin Lau) * use PTR_TO_SOCKET for bpf_sockopt->sk (Martin Lau) * dropped bpf_target_off from ctx rewrites (Martin Lau) * use return code for kernel bypass (Martin Lau & Andrii Nakryiko) Cc: Martin Lau Signed-off-by: Stanislav Fomichev --- include/linux/bpf-cgroup.h | 29 +++++ include/linux/bpf.h | 46 +++++++ include/linux/bpf_types.h | 1 + include/linux/filter.h | 13 ++ include/uapi/linux/bpf.h | 13 ++ kernel/bpf/cgroup.c | 260 +++++++++++++++++++++++++++++++++++++ kernel/bpf/core.c | 9 ++ kernel/bpf/syscall.c | 19 +++ kernel/bpf/verifier.c | 15 +++ net/core/filter.c | 2 +- net/socket.c | 18 +++ 11 files changed, 424 insertions(+), 1 deletion(-) diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index b631ee75762d..406f1ba82531 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -124,6 +124,13 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, loff_t *ppos, void **new_buf, enum bpf_attach_type type); +int __cgroup_bpf_run_filter_setsockopt(struct sock *sock, int level, + int optname, char __user *optval, + unsigned int optlen); +int __cgroup_bpf_run_filter_getsockopt(struct sock *sock, int level, + int optname, char __user *optval, + int __user *optlen); + static inline enum bpf_cgroup_storage_type cgroup_storage_type( struct bpf_map *map) { @@ -280,6 +287,26 @@ int bpf_percpu_cgroup_storage_update(struct bpf_map *map, void *key, __ret; \ }) +#define BPF_CGROUP_RUN_PROG_SETSOCKOPT(sock, level, optname, optval, optlen) \ +({ \ + int __ret = 0; \ + if (cgroup_bpf_enabled) \ + __ret = __cgroup_bpf_run_filter_setsockopt(sock, level, \ + optname, optval, \ + optlen); \ + __ret; \ +}) + +#define BPF_CGROUP_RUN_PROG_GETSOCKOPT(sock, level, optname, optval, optlen) \ +({ \ + int __ret = 0; \ + if (cgroup_bpf_enabled) \ + __ret = __cgroup_bpf_run_filter_getsockopt(sock, level, \ + optname, optval, \ + optlen); \ + __ret; \ +}) + int cgroup_bpf_prog_attach(const union bpf_attr *attr, enum bpf_prog_type ptype, struct bpf_prog *prog); int cgroup_bpf_prog_detach(const union bpf_attr *attr, @@ -349,6 +376,8 @@ static inline int bpf_percpu_cgroup_storage_update(struct bpf_map *map, #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) ({ 0; }) #define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type,major,minor,access) ({ 0; }) #define BPF_CGROUP_RUN_PROG_SYSCTL(head,table,write,buf,count,pos,nbuf) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_GETSOCKOPT(sock, level, optname, optval, optlen) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_SETSOCKOPT(sock, level, optname, optval, optlen) ({ 0; }) #define for_each_cgroup_storage_type(stype) for (; false; ) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index b15fb5fcb741..70d681dde253 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -521,6 +521,7 @@ struct bpf_prog_array { struct bpf_prog_array *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags); void bpf_prog_array_free(struct bpf_prog_array *progs); int bpf_prog_array_length(struct bpf_prog_array *progs); +bool bpf_prog_array_is_empty(struct bpf_prog_array *array); int bpf_prog_array_copy_to_user(struct bpf_prog_array *progs, __u32 __user *prog_ids, u32 cnt); @@ -607,6 +608,50 @@ _out: \ _ret; \ }) +/* To be used by BPF_PROG_TYPE_CGROUP_SOCKOPT program type. + * + * Expected BPF program return values are: + * 0: return -EPERM to the userspace + * 1: sockopt was not handled by BPF, kernel or next BPF prog should do it + * 2: sockopt was handled by BPF, kernel should _not_ do it and return + * to the userspace instead + * + * Note that as soon as BPF program returns 0 or 2 no further + * BPF program is executed and control is returned to the + * userspace. + * + * The macro itself returns: + * 0: sockopt was not handled by BPF, kernel should do it + * 1: sockopt was handled by BPF, kernel should _not_ do it + * -EPERM: return error back to userspace + */ +#define BPF_PROG_CGROUP_SOCKOPT_RUN_ARRAY(array, ctx, func) \ + ({ \ + struct bpf_prog_array_item *_item; \ + struct bpf_prog_array *_array; \ + struct bpf_prog *_prog; \ + u32 _success = 1; \ + u32 _bypass = 0; \ + u32 ret; \ + preempt_disable(); \ + rcu_read_lock(); \ + _array = rcu_dereference(array); \ + _item = &_array->items[0]; \ + while ((_prog = READ_ONCE(_item->prog))) { \ + bpf_cgroup_storage_set(_item->cgroup_storage); \ + ret = func(_prog, ctx); \ + _success &= (ret > 0); \ + _bypass |= (ret == 2); \ + if (ret != 1) \ + break; \ + _item++; \ + } \ + rcu_read_unlock(); \ + preempt_enable(); \ + ret = _success ? _bypass : -EPERM; \ + ret; \ + }) + #define BPF_PROG_RUN_ARRAY(array, ctx, func) \ __BPF_PROG_RUN_ARRAY(array, ctx, func, false) @@ -1055,6 +1100,7 @@ extern const struct bpf_func_proto bpf_spin_unlock_proto; extern const struct bpf_func_proto bpf_get_local_storage_proto; extern const struct bpf_func_proto bpf_strtol_proto; extern const struct bpf_func_proto bpf_strtoul_proto; +extern const struct bpf_func_proto bpf_tcp_sock_proto; /* Shared helpers among cBPF and eBPF. */ void bpf_user_rnd_init_once(void); diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h index 5a9975678d6f..eec5aeeeaf92 100644 --- a/include/linux/bpf_types.h +++ b/include/linux/bpf_types.h @@ -30,6 +30,7 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, raw_tracepoint_writable) #ifdef CONFIG_CGROUP_BPF BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev) BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SYSCTL, cg_sysctl) +BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCKOPT, cg_sockopt) #endif #ifdef CONFIG_BPF_LIRC_MODE2 BPF_PROG_TYPE(BPF_PROG_TYPE_LIRC_MODE2, lirc_mode2) diff --git a/include/linux/filter.h b/include/linux/filter.h index 43b45d6db36d..6e64d01e4e36 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -1199,4 +1199,17 @@ struct bpf_sysctl_kern { u64 tmp_reg; }; +struct bpf_sockopt_kern { + struct sock *sk; + u8 *optval; + u8 *optval_end; + s32 level; + s32 optname; + u32 optlen; + + /* Small on-stack optval buffer to avoid small allocations. + */ + u8 buf[64] __aligned(8); +}; + #endif /* __LINUX_FILTER_H__ */ diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index d0a23476f887..1cc40a2b6bef 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -170,6 +170,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_FLOW_DISSECTOR, BPF_PROG_TYPE_CGROUP_SYSCTL, BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, + BPF_PROG_TYPE_CGROUP_SOCKOPT, }; enum bpf_attach_type { @@ -192,6 +193,8 @@ enum bpf_attach_type { BPF_LIRC_MODE2, BPF_FLOW_DISSECTOR, BPF_CGROUP_SYSCTL, + BPF_CGROUP_GETSOCKOPT, + BPF_CGROUP_SETSOCKOPT, __MAX_BPF_ATTACH_TYPE }; @@ -3539,4 +3542,14 @@ struct bpf_sysctl { */ }; +struct bpf_sockopt { + __bpf_md_ptr(struct bpf_sock *, sk); + __bpf_md_ptr(void *, optval); + __bpf_md_ptr(void *, optval_end); + + __s32 level; + __s32 optname; + __u32 optlen; +}; + #endif /* _UAPI__LINUX_BPF_H__ */ diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 1b65ab0df457..9085a218a1a8 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -18,6 +18,7 @@ #include #include #include +#include DEFINE_STATIC_KEY_FALSE(cgroup_bpf_enabled_key); EXPORT_SYMBOL(cgroup_bpf_enabled_key); @@ -924,6 +925,140 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, } EXPORT_SYMBOL(__cgroup_bpf_run_filter_sysctl); +static bool __cgroup_bpf_prog_array_is_empty(struct cgroup *cgrp, + enum bpf_attach_type attach_type) +{ + struct bpf_prog_array *prog_array; + bool empty; + + rcu_read_lock(); + prog_array = rcu_dereference(cgrp->bpf.effective[attach_type]); + empty = bpf_prog_array_is_empty(prog_array); + rcu_read_unlock(); + + return empty; +} + +static int sockopt_alloc_buf(struct bpf_sockopt_kern *ctx, int max_optlen) +{ + if (unlikely(max_optlen > PAGE_SIZE)) + return -EINVAL; + + if (likely(max_optlen <= sizeof(ctx->buf))) { + ctx->optval = ctx->buf; + } else { + ctx->optval = kzalloc(max_optlen, GFP_USER); + if (!ctx->optval) + return -ENOMEM; + } + + ctx->optval_end = ctx->optval + max_optlen; + ctx->optlen = max_optlen; + + return 0; +} + +static void sockopt_free_buf(struct bpf_sockopt_kern *ctx) +{ + if (unlikely(ctx->optval != ctx->buf)) + kfree(ctx->optval); +} + +int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int level, + int optname, char __user *optval, + unsigned int optlen) +{ + struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); + struct bpf_sockopt_kern ctx = { + .sk = sk, + .level = level, + .optname = optname, + }; + int ret; + + /* Opportunistic check to see whether we have any BPF program + * attached to the hook so we don't waste time allocating + * memory and locking the socket. + */ + if (__cgroup_bpf_prog_array_is_empty(cgrp, BPF_CGROUP_SETSOCKOPT)) + return 0; + + ret = sockopt_alloc_buf(&ctx, optlen); + if (ret) + return ret; + + if (copy_from_user(ctx.optval, optval, optlen) != 0) { + sockopt_free_buf(&ctx); + return -EFAULT; + } + + lock_sock(sk); + ret = BPF_PROG_CGROUP_SOCKOPT_RUN_ARRAY( + cgrp->bpf.effective[BPF_CGROUP_SETSOCKOPT], + &ctx, BPF_PROG_RUN); + release_sock(sk); + + sockopt_free_buf(&ctx); + + return ret; +} +EXPORT_SYMBOL(__cgroup_bpf_run_filter_setsockopt); + +int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, + int optname, char __user *optval, + int __user *optlen) +{ + struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); + struct bpf_sockopt_kern ctx = { + .sk = sk, + .level = level, + .optname = optname, + }; + int max_optlen; + int ret; + + /* Opportunistic check to see whether we have any BPF program + * attached to the hook so we don't waste time allocating + * memory and locking the socket. + */ + if (__cgroup_bpf_prog_array_is_empty(cgrp, BPF_CGROUP_GETSOCKOPT)) + return 0; + + if (get_user(max_optlen, optlen)) + return -EFAULT; + + ret = sockopt_alloc_buf(&ctx, max_optlen); + if (ret) + return ret; + + lock_sock(sk); + ret = BPF_PROG_CGROUP_SOCKOPT_RUN_ARRAY( + cgrp->bpf.effective[BPF_CGROUP_GETSOCKOPT], + &ctx, BPF_PROG_RUN); + release_sock(sk); + + if (ret < 0) + goto out; + + if (ctx.optlen > max_optlen) { + ret = -EFAULT; + goto out; + } + + if (ret) { + if (copy_to_user(optval, ctx.optval, ctx.optlen) || + put_user(ctx.optlen, optlen)) { + ret = -EFAULT; + goto out; + } + } + +out: + sockopt_free_buf(&ctx); + return ret; +} +EXPORT_SYMBOL(__cgroup_bpf_run_filter_getsockopt); + static ssize_t sysctl_cpy_dir(const struct ctl_dir *dir, char **bufp, size_t *lenp) { @@ -1184,3 +1319,128 @@ const struct bpf_verifier_ops cg_sysctl_verifier_ops = { const struct bpf_prog_ops cg_sysctl_prog_ops = { }; + +static const struct bpf_func_proto * +cg_sockopt_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) +{ + switch (func_id) { + case BPF_FUNC_sk_storage_get: + return &bpf_sk_storage_get_proto; + case BPF_FUNC_sk_storage_delete: + return &bpf_sk_storage_delete_proto; +#ifdef CONFIG_INET + case BPF_FUNC_tcp_sock: + return &bpf_tcp_sock_proto; +#endif + default: + return cgroup_base_func_proto(func_id, prog); + } +} + +static bool cg_sockopt_is_valid_access(int off, int size, + enum bpf_access_type type, + const struct bpf_prog *prog, + struct bpf_insn_access_aux *info) +{ + const int size_default = sizeof(__u32); + + if (off < 0 || off >= sizeof(struct bpf_sockopt)) + return false; + + if (off % size != 0) + return false; + + if (type == BPF_WRITE) { + switch (off) { + case offsetof(struct bpf_sockopt, optlen): + if (size != size_default) + return false; + return prog->expected_attach_type == + BPF_CGROUP_GETSOCKOPT; + default: + return false; + } + } + + switch (off) { + case offsetof(struct bpf_sockopt, sk): + if (size != sizeof(__u64)) + return false; + info->reg_type = PTR_TO_SOCKET; + break; + case offsetof(struct bpf_sockopt, optval): + if (size != sizeof(__u64)) + return false; + info->reg_type = PTR_TO_PACKET; + break; + case offsetof(struct bpf_sockopt, optval_end): + if (size != sizeof(__u64)) + return false; + info->reg_type = PTR_TO_PACKET_END; + break; + default: + if (size != size_default) + return false; + break; + } + return true; +} + +#define CG_SOCKOPT_ACCESS_FIELD(T, F) \ + T(BPF_FIELD_SIZEOF(struct bpf_sockopt_kern, F), \ + si->dst_reg, si->src_reg, \ + offsetof(struct bpf_sockopt_kern, F)) + +static u32 cg_sockopt_convert_ctx_access(enum bpf_access_type type, + const struct bpf_insn *si, + struct bpf_insn *insn_buf, + struct bpf_prog *prog, + u32 *target_size) +{ + struct bpf_insn *insn = insn_buf; + + switch (si->off) { + case offsetof(struct bpf_sockopt, sk): + *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, sk); + break; + case offsetof(struct bpf_sockopt, level): + *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, level); + break; + case offsetof(struct bpf_sockopt, optname): + *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, optname); + break; + case offsetof(struct bpf_sockopt, optlen): + if (type == BPF_WRITE) + *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_STX_MEM, optlen); + else + *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, optlen); + break; + case offsetof(struct bpf_sockopt, optval): + *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, optval); + break; + case offsetof(struct bpf_sockopt, optval_end): + *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, optval_end); + break; + } + + return insn - insn_buf; +} + +static int cg_sockopt_get_prologue(struct bpf_insn *insn_buf, + bool direct_write, + const struct bpf_prog *prog) +{ + /* Nothing to do for sockopt argument. The data is kzalloc'ated. + */ + return 0; +} + +const struct bpf_verifier_ops cg_sockopt_verifier_ops = { + .get_func_proto = cg_sockopt_func_proto, + .is_valid_access = cg_sockopt_is_valid_access, + .convert_ctx_access = cg_sockopt_convert_ctx_access, + .gen_prologue = cg_sockopt_get_prologue, +}; + +const struct bpf_prog_ops cg_sockopt_prog_ops = { +}; diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 33fb292f2e30..e9152ebd66bc 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1813,6 +1813,15 @@ int bpf_prog_array_length(struct bpf_prog_array *array) return cnt; } +bool bpf_prog_array_is_empty(struct bpf_prog_array *array) +{ + struct bpf_prog_array_item *item; + + for (item = array->items; item->prog; item++) + if (item->prog != &dummy_bpf_prog.prog) + return false; + return true; +} static bool bpf_prog_array_copy_core(struct bpf_prog_array *array, u32 *prog_ids, diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 4c53cbd3329d..4ad2b5f1905f 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1596,6 +1596,14 @@ bpf_prog_load_check_attach_type(enum bpf_prog_type prog_type, default: return -EINVAL; } + case BPF_PROG_TYPE_CGROUP_SOCKOPT: + switch (expected_attach_type) { + case BPF_CGROUP_SETSOCKOPT: + case BPF_CGROUP_GETSOCKOPT: + return 0; + default: + return -EINVAL; + } default: return 0; } @@ -1846,6 +1854,7 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog, switch (prog->type) { case BPF_PROG_TYPE_CGROUP_SOCK: case BPF_PROG_TYPE_CGROUP_SOCK_ADDR: + case BPF_PROG_TYPE_CGROUP_SOCKOPT: return attach_type == prog->expected_attach_type ? 0 : -EINVAL; case BPF_PROG_TYPE_CGROUP_SKB: return prog->enforce_expected_attach_type && @@ -1916,6 +1925,10 @@ static int bpf_prog_attach(const union bpf_attr *attr) case BPF_CGROUP_SYSCTL: ptype = BPF_PROG_TYPE_CGROUP_SYSCTL; break; + case BPF_CGROUP_GETSOCKOPT: + case BPF_CGROUP_SETSOCKOPT: + ptype = BPF_PROG_TYPE_CGROUP_SOCKOPT; + break; default: return -EINVAL; } @@ -1997,6 +2010,10 @@ static int bpf_prog_detach(const union bpf_attr *attr) case BPF_CGROUP_SYSCTL: ptype = BPF_PROG_TYPE_CGROUP_SYSCTL; break; + case BPF_CGROUP_GETSOCKOPT: + case BPF_CGROUP_SETSOCKOPT: + ptype = BPF_PROG_TYPE_CGROUP_SOCKOPT; + break; default: return -EINVAL; } @@ -2031,6 +2048,8 @@ static int bpf_prog_query(const union bpf_attr *attr, case BPF_CGROUP_SOCK_OPS: case BPF_CGROUP_DEVICE: case BPF_CGROUP_SYSCTL: + case BPF_CGROUP_GETSOCKOPT: + case BPF_CGROUP_SETSOCKOPT: break; case BPF_LIRC_MODE2: return lirc_prog_query(attr, uattr); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 8d1786357a09..f03b00b4c1b7 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1720,6 +1720,18 @@ static bool may_access_direct_pkt_data(struct bpf_verifier_env *env, env->seen_direct_write = true; return true; + + case BPF_PROG_TYPE_CGROUP_SOCKOPT: + if (t == BPF_WRITE) { + if (env->prog->expected_attach_type == + BPF_CGROUP_GETSOCKOPT) { + env->seen_direct_write = true; + return true; + } + return false; + } + return true; + default: return false; } @@ -5541,6 +5553,9 @@ static int check_return_code(struct bpf_verifier_env *env) case BPF_PROG_TYPE_CGROUP_DEVICE: case BPF_PROG_TYPE_CGROUP_SYSCTL: break; + case BPF_PROG_TYPE_CGROUP_SOCKOPT: + range = tnum_range(0, 2); + break; default: return 0; } diff --git a/net/core/filter.c b/net/core/filter.c index 8c18f2781afa..e2ad8144cf6e 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -5636,7 +5636,7 @@ BPF_CALL_1(bpf_tcp_sock, struct sock *, sk) return (unsigned long)NULL; } -static const struct bpf_func_proto bpf_tcp_sock_proto = { +const struct bpf_func_proto bpf_tcp_sock_proto = { .func = bpf_tcp_sock, .gpl_only = false, .ret_type = RET_PTR_TO_TCP_SOCK_OR_NULL, diff --git a/net/socket.c b/net/socket.c index 72372dc5dd70..e8654f1f70e6 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2069,6 +2069,15 @@ static int __sys_setsockopt(int fd, int level, int optname, if (err) goto out_put; + err = BPF_CGROUP_RUN_PROG_SETSOCKOPT(sock->sk, level, optname, + optval, optlen); + if (err < 0) { + goto out_put; + } else if (err > 0) { + err = 0; + goto out_put; + } + if (level == SOL_SOCKET) err = sock_setsockopt(sock, level, optname, optval, @@ -2106,6 +2115,15 @@ static int __sys_getsockopt(int fd, int level, int optname, if (err) goto out_put; + err = BPF_CGROUP_RUN_PROG_GETSOCKOPT(sock->sk, level, optname, + optval, optlen); + if (err < 0) { + goto out_put; + } else if (err > 0) { + err = 0; + goto out_put; + } + if (level == SOL_SOCKET) err = sock_getsockopt(sock, level, optname, optval, From patchwork Mon Jun 17 18:01:02 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanislav Fomichev X-Patchwork-Id: 1117249 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="cIa5vIno"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 45SJtR0Vnfz9sDX for ; Tue, 18 Jun 2019 04:01:19 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726605AbfFQSBS (ORCPT ); Mon, 17 Jun 2019 14:01:18 -0400 Received: from mail-yw1-f73.google.com ([209.85.161.73]:34396 "EHLO mail-yw1-f73.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726048AbfFQSBS (ORCPT ); Mon, 17 Jun 2019 14:01:18 -0400 Received: by mail-yw1-f73.google.com with SMTP id q8so12968224ywc.1 for ; Mon, 17 Jun 2019 11:01:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=U184OPt6PLUipr5CRRliK9xhGJgLNV6CYr7jB9LM2eg=; b=cIa5vInokoz5ShMinNVzCNRjnTQx9MNiflzS4JTAfb3OC43jYw5dsE0PCJCZeH1PJC I/tiAbdRG2zoicSuaFyFdBn5DG+tU3TBRzDYGbxT79ZYx5MqKCq4HnxLGx3IUAwcXrG2 4JF9esyxDQdzZKf37iJ0sr/dtYUr4OQ7TKgrn8vOWTJMH/r4PuGyEGGrBQaeHhSl34H3 TVRm6hXjDo9m25Ko24OK+O91tLS1BXG+PSfqUS4DbUh8OhrW+/IRCLtx58sjwoJAsDFR nRjyNcSzinrvM00ztTwklEGC5SkSSGTwkX3Q/ijIdWlvEO1yoyFzxk217NOqccVu7dv6 NK+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=U184OPt6PLUipr5CRRliK9xhGJgLNV6CYr7jB9LM2eg=; b=DiGzxzyRItUKrHA+E3MZ3f8Dj1TkpiDBasCoMLFNlxE3xOa3wDZkUJufys/YyObvIf KGbl9eSAqBnam+8+la46t1SkOtkF0QJHHCyotG9wzuCkN4AJVySGIHwmmqsmiyf9xRst zWMFOg/rnrJL64fxGgKJ4XolckDE0sexSfQ8//1q+olled3PnshGeR+XK1kNYfVyRJA6 W4pO0v6h/EuTzsF8459mWNdeXs0xQvRUdZ5LeTM7wdjMOI9C+ieVn11IfPqg3/OVssMk jrqOJuqodqqFzfeokqCN2kY45jqOdJ+i/j96rkBe7nPGxUuLGvyeuyAr+0zxsUCmJnl+ /zbg== X-Gm-Message-State: APjAAAWHbGBRgZORfcgqYsWQuPVxlke0h7g5Xsd3MZCBmnV6Dz+yC4f5 vNDcDdpIdPmU3R5dvZsRLwXdqwTrexofeIB2HTm5P5mPoS5w40WKfqb5V4e5egYQnPv7U8vwvdy Waf1a7czKO+5HHN1/vA3K5XLgZlwdEIeJZe5xmwyBc5wS2TRpcSdDzQ== X-Google-Smtp-Source: APXvYqz4nftkEQUPmxowXiWBQ0IroOAfxnPY22p3cFBiugN1245bx5zIivMEnckIFLErD+caCJ2iJbE= X-Received: by 2002:a81:98cc:: with SMTP id p195mr7453338ywg.155.1560794476785; Mon, 17 Jun 2019 11:01:16 -0700 (PDT) Date: Mon, 17 Jun 2019 11:01:02 -0700 In-Reply-To: <20190617180109.34950-1-sdf@google.com> Message-Id: <20190617180109.34950-3-sdf@google.com> Mime-Version: 1.0 References: <20190617180109.34950-1-sdf@google.com> X-Mailer: git-send-email 2.22.0.410.gd8fdbe21b5-goog Subject: [PATCH bpf-next v6 2/9] bpf: sync bpf.h to tools/ From: Stanislav Fomichev To: netdev@vger.kernel.org, bpf@vger.kernel.org Cc: davem@davemloft.net, ast@kernel.org, daniel@iogearbox.net, Stanislav Fomichev , Martin Lau Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Export new prog type and hook points to the libbpf. Cc: Martin Lau Signed-off-by: Stanislav Fomichev --- tools/include/uapi/linux/bpf.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index d0a23476f887..5dc906f1db02 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -170,6 +170,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_FLOW_DISSECTOR, BPF_PROG_TYPE_CGROUP_SYSCTL, BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, + BPF_PROG_TYPE_CGROUP_SOCKOPT, }; enum bpf_attach_type { @@ -192,6 +193,8 @@ enum bpf_attach_type { BPF_LIRC_MODE2, BPF_FLOW_DISSECTOR, BPF_CGROUP_SYSCTL, + BPF_CGROUP_GETSOCKOPT, + BPF_CGROUP_SETSOCKOPT, __MAX_BPF_ATTACH_TYPE }; @@ -3539,4 +3542,15 @@ struct bpf_sysctl { */ }; +struct bpf_sockopt { + __bpf_md_ptr(struct bpf_sock *, sk); + __bpf_md_ptr(void *, optval); + __bpf_md_ptr(void *, optval_end); + + __s32 level; + __s32 optname; + + __u32 optlen; +}; + #endif /* _UAPI__LINUX_BPF_H__ */ From patchwork Mon Jun 17 18:01:03 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanislav Fomichev X-Patchwork-Id: 1117251 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: incoming-bpf@patchwork.ozlabs.org Delivered-To: patchwork-incoming-bpf@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=bpf-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="YcPdsMN3"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 45SJv419SQz9sDX for ; Tue, 18 Jun 2019 04:01:52 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726048AbfFQSBv (ORCPT ); Mon, 17 Jun 2019 14:01:51 -0400 Received: from mail-yb1-f201.google.com ([209.85.219.201]:52229 "EHLO mail-yb1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726215AbfFQSBv (ORCPT ); Mon, 17 Jun 2019 14:01:51 -0400 Received: by mail-yb1-f201.google.com with SMTP id z6so10387142ybm.19 for ; Mon, 17 Jun 2019 11:01:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=KESt1YMCgEpYbvtvketbmAIj09uDDroaTTEMEO312Bs=; b=YcPdsMN3kwimWHq1bvDamEso7qkIzVFU1NsGe75DEZuyysANB4sQgNQy9JvyGmNA8L 6YGkAuYmwK8yy8Lo/WxhOdiAorPvxZilxyj0oflV870vkWS26eHiQw43lZzLkXm+Rlbe 0asyDvcgmrmAwAVsw+VbVg4pFsAohQ006nS7SBrdZU6SosDGns4SS70LWAyRidGBAN4b xnmfYSwnyizoq83Zf83MkbFyMiMRWFBjU/0hJBOPmY2BYiByzTmiu0nbd8LZRqccfEBU CEcFgiXiwIE7nnost8koHAQlG/RnINbYQcGnlaCvAKjqIG1K7PycfGRETti3f2etGVsZ kmyA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=KESt1YMCgEpYbvtvketbmAIj09uDDroaTTEMEO312Bs=; b=QBnREB2/GUX6IhCFislz/eqxR8otKyyF3g8tqWXr7FlWR9xPDwZ+4HeeEeyR6bFnZW 7CBb0GVTyz8qxLA2UdMbDXKpwcQYYFsBhWIYUEqjozKBUMzzBVEaSXGjCqr4hXcwWvyv KBJ4dzCUVgUSCQt9USafdlkjTZibdDsPsfJ8TwmXTIe8qPyyMX+YvCmGkbmvSmoWxqsb dkH5eyttXBJJ8x/IprHfqEo9VZv2kpfm8MsOnuvJ1A6M5zfY4C3kPFJBiXBywF0CkBGh 0rci9JdIjAK1VvD0Gv7c8VB1P3cohw7Q5cAVtndYL9XZetEmT81I6H0b7Qsp78I0+0VR gxZA== X-Gm-Message-State: APjAAAVLbN7WGHWYsdiKLvPGnp0ENmGSbCkOgitrYY4gRPOwGNHCq9mc KcSF4AIdXb4dodz4zTAeWCdJEaM= X-Google-Smtp-Source: APXvYqyi0loDKAmiUV4MQ1FA9+kUNUBiCRCkKPQIMxyhVLwEmG17V7wf+Mc/L+5zO4UoQXix2HYtlBM= X-Received: by 2002:a25:3c4:: with SMTP id 187mr59401766ybd.337.1560794510154; Mon, 17 Jun 2019 11:01:50 -0700 (PDT) Date: Mon, 17 Jun 2019 11:01:03 -0700 In-Reply-To: <20190617180109.34950-1-sdf@google.com> Message-Id: <20190617180109.34950-4-sdf@google.com> Mime-Version: 1.0 References: <20190617180109.34950-1-sdf@google.com> X-Mailer: git-send-email 2.22.0.410.gd8fdbe21b5-goog Subject: [PATCH bpf-next v6 3/9] libbpf: support sockopt hooks From: Stanislav Fomichev To: netdev@vger.kernel.org, bpf@vger.kernel.org Cc: davem@davemloft.net, ast@kernel.org, daniel@iogearbox.net, Stanislav Fomichev , Martin Lau Sender: bpf-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org Make libbpf aware of new sockopt hooks so it can derive prog type and hook point from the section names. Cc: Martin Lau Signed-off-by: Stanislav Fomichev --- tools/lib/bpf/libbpf.c | 5 +++++ tools/lib/bpf/libbpf_probes.c | 1 + 2 files changed, 6 insertions(+) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index e725fa86b189..98457fc6bb76 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -2244,6 +2244,7 @@ static bool bpf_prog_type__needs_kver(enum bpf_prog_type type) case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE: case BPF_PROG_TYPE_PERF_EVENT: case BPF_PROG_TYPE_CGROUP_SYSCTL: + case BPF_PROG_TYPE_CGROUP_SOCKOPT: return false; case BPF_PROG_TYPE_KPROBE: default: @@ -3197,6 +3198,10 @@ static const struct { BPF_CGROUP_UDP6_SENDMSG), BPF_EAPROG_SEC("cgroup/sysctl", BPF_PROG_TYPE_CGROUP_SYSCTL, BPF_CGROUP_SYSCTL), + BPF_EAPROG_SEC("cgroup/getsockopt", BPF_PROG_TYPE_CGROUP_SOCKOPT, + BPF_CGROUP_GETSOCKOPT), + BPF_EAPROG_SEC("cgroup/setsockopt", BPF_PROG_TYPE_CGROUP_SOCKOPT, + BPF_CGROUP_SETSOCKOPT), }; #undef BPF_PROG_SEC_IMPL diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c index 5e2aa83f637a..7e21db11dde8 100644 --- a/tools/lib/bpf/libbpf_probes.c +++ b/tools/lib/bpf/libbpf_probes.c @@ -101,6 +101,7 @@ probe_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns, case BPF_PROG_TYPE_SK_REUSEPORT: case BPF_PROG_TYPE_FLOW_DISSECTOR: case BPF_PROG_TYPE_CGROUP_SYSCTL: + case BPF_PROG_TYPE_CGROUP_SOCKOPT: default: break; } From patchwork Mon Jun 17 18:01:04 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanislav Fomichev X-Patchwork-Id: 1117253 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: incoming-bpf@patchwork.ozlabs.org Delivered-To: patchwork-incoming-bpf@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=bpf-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="FvdtVe3V"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 45SJv54xdWz9sDX for ; Tue, 18 Jun 2019 04:01:53 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726614AbfFQSBx (ORCPT ); Mon, 17 Jun 2019 14:01:53 -0400 Received: from mail-oi1-f202.google.com ([209.85.167.202]:50412 "EHLO mail-oi1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726215AbfFQSBx (ORCPT ); Mon, 17 Jun 2019 14:01:53 -0400 Received: by mail-oi1-f202.google.com with SMTP id p83so3824449oih.17 for ; Mon, 17 Jun 2019 11:01:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=aMAVJjYPVW0LDE+1k3jh4sYPHN+8Kpd0vTrAOfnc5og=; b=FvdtVe3Vbc39dmix+NbXtFO0MuPToF39PwPdhhwcCGBjaXhcsjoA1cZ/nxNgHkXDzs bDh7EnOcFzrQFcGVb1ZYN8oQypdkRkfrmpttn1B4VuXFewqPFXKnPZWLicYPWufdgZxV NizLHYuh/GucFT0L7dVVIWgGRmVraYJMqpwp1XZYTHgFZ7Sf/jNqalwqKfgv3KF8eZ6X iT9mJS4RU/1C89Gma3S87e8VViiFzMczPDPrjD46QAzv9QAokmdk+syLfcFiuDolECmH rv0QVS9sJxSIkbs89KVok78IEESMPTD1FIPikZvdyB9CcX8XDb0WTvzhIGgy4wLurg4r MiZQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=aMAVJjYPVW0LDE+1k3jh4sYPHN+8Kpd0vTrAOfnc5og=; b=E9+WhJnnL4ei1TMGuAWmgsYv1eYevzMTZ+ng17RqPpOFXvAssNf3j37JfmAq+H5fF2 GN3DqhfUsRfKCJB8lB6NzShVtGPAB4/SxvVbT4UYB6zVuas9Vcyljxn3VbFs4qzfUq70 SVhfbHBR/CRTuU7P1TPG6k203q6+aZ7C57oYWkfo2kXBD1oAmc943+pwW5JTfLWlIfKF Hv0yqamMrN3NuAoFZ16Y8ECZMnJKZFfQxMdXVZs+p40VlENiRkZJv2RGyT/NfQU8sEZ9 pDcTRa7jRPM3KRSxCxYDv7hAHvhh8rI1C9+vGBNRDPsveetpFbAZgZbvb2PhpsTuD1Ut r8vA== X-Gm-Message-State: APjAAAXUt59Ga7IfdBqwKnl1beHG5TdZnvq0Oy2UwDfMabKD4pElIvrq gvGYEGb64ijkmTVA18DIj1F5F/o= X-Google-Smtp-Source: APXvYqz0mqk9GIDPC4J50t86rZtPLM7U1vEjox029PZVgNZ5MlaXtPjOaPVh9OkXzxEyrgOK4HnCTrw= X-Received: by 2002:aca:cc85:: with SMTP id c127mr123475oig.81.1560794512480; Mon, 17 Jun 2019 11:01:52 -0700 (PDT) Date: Mon, 17 Jun 2019 11:01:04 -0700 In-Reply-To: <20190617180109.34950-1-sdf@google.com> Message-Id: <20190617180109.34950-5-sdf@google.com> Mime-Version: 1.0 References: <20190617180109.34950-1-sdf@google.com> X-Mailer: git-send-email 2.22.0.410.gd8fdbe21b5-goog Subject: [PATCH bpf-next v6 4/9] selftests/bpf: test sockopt section name From: Stanislav Fomichev To: netdev@vger.kernel.org, bpf@vger.kernel.org Cc: davem@davemloft.net, ast@kernel.org, daniel@iogearbox.net, Stanislav Fomichev , Martin Lau Sender: bpf-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org Add tests that make sure libbpf section detection works. Cc: Martin Lau Signed-off-by: Stanislav Fomichev --- tools/testing/selftests/bpf/test_section_names.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/testing/selftests/bpf/test_section_names.c b/tools/testing/selftests/bpf/test_section_names.c index bebd4fbca1f4..5f84b3b8c90b 100644 --- a/tools/testing/selftests/bpf/test_section_names.c +++ b/tools/testing/selftests/bpf/test_section_names.c @@ -124,6 +124,16 @@ static struct sec_name_test tests[] = { {0, BPF_PROG_TYPE_CGROUP_SYSCTL, BPF_CGROUP_SYSCTL}, {0, BPF_CGROUP_SYSCTL}, }, + { + "cgroup/getsockopt", + {0, BPF_PROG_TYPE_CGROUP_SOCKOPT, BPF_CGROUP_GETSOCKOPT}, + {0, BPF_CGROUP_GETSOCKOPT}, + }, + { + "cgroup/setsockopt", + {0, BPF_PROG_TYPE_CGROUP_SOCKOPT, BPF_CGROUP_SETSOCKOPT}, + {0, BPF_CGROUP_SETSOCKOPT}, + }, }; static int test_prog_type_by_name(const struct sec_name_test *test) From patchwork Mon Jun 17 18:01:05 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanislav Fomichev X-Patchwork-Id: 1117255 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="fwr7Ezg5"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 45SJvB1FVDz9sDX for ; Tue, 18 Jun 2019 04:01:58 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726715AbfFQSB5 (ORCPT ); Mon, 17 Jun 2019 14:01:57 -0400 Received: from mail-ot1-f73.google.com ([209.85.210.73]:53922 "EHLO mail-ot1-f73.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726673AbfFQSB4 (ORCPT ); Mon, 17 Jun 2019 14:01:56 -0400 Received: by mail-ot1-f73.google.com with SMTP id d13so5197275oth.20 for ; Mon, 17 Jun 2019 11:01:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=3J+yAVo2IA0q/7oYk92p3ERlE4p4BXv68cqGTezv1xo=; b=fwr7Ezg5zBSEUbyeleRYfcntd9aOfG3IipmNDW+S4k6RO3PBcoGpq31K3o5wsSnkt+ 6FFpfTzLruZcS+XU6inVQMElK/b0Yz5vywrw8QJMWUIofQlJWyMLJsVnq/eOJ45XYD3I vwZ83Pu6IsiivE4CW3VtUBOWd4FP9rZs4NygiKZTbRkiQ/mQeHDQYNgDk8N7awyHn97u exExe4ZsvXFeGbq5V0V9XbNZIq1nvXOROhTalAdJJdXYu/X5tL26FfXL+FSWlTdJZj/m JidfDhKMrLCKBniAK0e6yW4PJ0mT+7UJiAJpe7w1WgMvLzIctbK1DlaU5M1Vxa5ccRNs QScQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=3J+yAVo2IA0q/7oYk92p3ERlE4p4BXv68cqGTezv1xo=; b=cIjLOgeTmX9aoHwVvXcgLdVz7kWNRqGzIJZYa8kNs93PSRtr2lG6iTq/2ESJZmzmEx auhQO257MLlcDXcl9oNTbm1hpgRmxoQDz2Z/MkHBeulQsNGpGIXUg0VyVQJUEmKUHn5j WLMtSTKcmPJBhTPDEa1WkKKzPm8mkuweDKo15gh1ij/Gvqv4VutQg2cEfEVERxaKoZWY 63LBYIILpcDUNb7m8RszMPT9qmzCzLiw2MjCAFA6FSTnTESiS9xiDIPreu5BLYZY1Bes k5k+tF056RcnYzah+ZtDAnb9+1v2qm4nA7O7UlCur8r0JYtUScmuU2cHw3K2MSHV0s10 RqyA== X-Gm-Message-State: APjAAAWr158xl3LHUh0s49kOxhi3KrjaeAEJfcwDiB/7P8HIQrvD/RBA QQwS4842eAxMYingS5bQRQzGpFYKBRExm2fmigk/iDcNtbT6z/m0ufKk0JcaKSnEUSK03/UKiDi 7VEVN578O6Icm9efKn5NVsiuKYuSXyG7+1ayB+4LCaWZ11YSrL4pmBQ== X-Google-Smtp-Source: APXvYqxiPq2KnasowODL0dV2BVb/h5IpdSufRz2i8JjuLhQ6WJEqGlVTuZFow6bAaPkUAfmI0/IeHoo= X-Received: by 2002:a9d:3ba4:: with SMTP id k33mr60125196otc.68.1560794514988; Mon, 17 Jun 2019 11:01:54 -0700 (PDT) Date: Mon, 17 Jun 2019 11:01:05 -0700 In-Reply-To: <20190617180109.34950-1-sdf@google.com> Message-Id: <20190617180109.34950-6-sdf@google.com> Mime-Version: 1.0 References: <20190617180109.34950-1-sdf@google.com> X-Mailer: git-send-email 2.22.0.410.gd8fdbe21b5-goog Subject: [PATCH bpf-next v6 5/9] selftests/bpf: add sockopt test From: Stanislav Fomichev To: netdev@vger.kernel.org, bpf@vger.kernel.org Cc: davem@davemloft.net, ast@kernel.org, daniel@iogearbox.net, Stanislav Fomichev , Martin Lau Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add sockopt selftests: * require proper expected_attach_type * enforce context field read/write access * test bpf_sockopt_handled handler * test EPERM * test limiting optlen from getsockopt * test out-of-bounds access v3: * use DW for optval{,_end} loads v2: * use return code 2 for kernel bypass Cc: Martin Lau Signed-off-by: Stanislav Fomichev --- tools/testing/selftests/bpf/.gitignore | 1 + tools/testing/selftests/bpf/Makefile | 3 +- tools/testing/selftests/bpf/test_sockopt.c | 773 +++++++++++++++++++++ 3 files changed, 776 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/test_sockopt.c diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore index 7470327edcfe..3fe92601223d 100644 --- a/tools/testing/selftests/bpf/.gitignore +++ b/tools/testing/selftests/bpf/.gitignore @@ -39,3 +39,4 @@ libbpf.so.* test_hashmap test_btf_dump xdping +test_sockopt diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 44fb61f4d502..4ff4401a4024 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -26,7 +26,7 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user \ test_socket_cookie test_cgroup_storage test_select_reuseport test_section_names \ test_netcnt test_tcpnotify_user test_sock_fields test_sysctl test_hashmap \ - test_btf_dump test_cgroup_attach xdping + test_btf_dump test_cgroup_attach xdping test_sockopt BPF_OBJ_FILES = $(patsubst %.c,%.o, $(notdir $(wildcard progs/*.c))) TEST_GEN_FILES = $(BPF_OBJ_FILES) @@ -101,6 +101,7 @@ $(OUTPUT)/test_netcnt: cgroup_helpers.c $(OUTPUT)/test_sock_fields: cgroup_helpers.c $(OUTPUT)/test_sysctl: cgroup_helpers.c $(OUTPUT)/test_cgroup_attach: cgroup_helpers.c +$(OUTPUT)/test_sockopt: cgroup_helpers.c .PHONY: force diff --git a/tools/testing/selftests/bpf/test_sockopt.c b/tools/testing/selftests/bpf/test_sockopt.c new file mode 100644 index 000000000000..c007ad4d2c85 --- /dev/null +++ b/tools/testing/selftests/bpf/test_sockopt.c @@ -0,0 +1,773 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "bpf_rlimit.h" +#include "bpf_util.h" +#include "cgroup_helpers.h" + +#define CG_PATH "/sockopt" + +static char bpf_log_buf[4096]; +static bool verbose; + +enum sockopt_test_error { + OK = 0, + DENY_LOAD, + DENY_ATTACH, + EPERM_GETSOCKOPT, + EFAULT_GETSOCKOPT, + EPERM_SETSOCKOPT, +}; + +static struct sockopt_test { + const char *descr; + const struct bpf_insn insns[64]; + enum bpf_attach_type attach_type; + enum bpf_attach_type expected_attach_type; + + int level; + int optname; + + const char set_optval[64]; + socklen_t set_optlen; + + const char get_optval[64]; + socklen_t get_optlen; + socklen_t get_optlen_ret; + + enum sockopt_test_error error; +} tests[] = { + + /* ==================== getsockopt ==================== */ + + { + .descr = "getsockopt: no expected_attach_type", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = 0, + .error = DENY_LOAD, + }, + { + .descr = "getsockopt: wrong expected_attach_type", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + .error = DENY_ATTACH, + }, + { + .descr = "getsockopt: bypass bpf hook", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .level = SOL_IP, + .optname = IP_TOS, + + .set_optval = { 1 << 3 }, + .set_optlen = 1, + + .get_optval = { 1 << 3 }, + .get_optlen = 1, + }, + { + .descr = "getsockopt: return EPERM from bpf hook", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .level = SOL_IP, + .optname = IP_TOS, + + .get_optlen = 1, + .error = EPERM_GETSOCKOPT, + }, + { + .descr = "getsockopt: no optval bounds check, deny loading", + .insns = { + /* r6 = ctx->optval */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optval)), + + /* ctx->optval[0] = 0x80 */ + BPF_MOV64_IMM(BPF_REG_0, 0x80), + BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0), + + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + .error = DENY_LOAD, + }, + { + .descr = "getsockopt: read ctx->level", + .insns = { + /* r6 = ctx->level */ + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, level)), + + /* if (ctx->level == 123) { */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 2), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_JMP_A(1), + /* } else { */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .level = 123, + + .get_optlen = 1, + }, + { + .descr = "getsockopt: deny writing to ctx->level", + .insns = { + /* ctx->level = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, level)), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "getsockopt: read ctx->optname", + .insns = { + /* r6 = ctx->optname */ + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optname)), + + /* if (ctx->optname == 123) { */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 2), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_JMP_A(1), + /* } else { */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .optname = 123, + + .get_optlen = 1, + }, + { + .descr = "getsockopt: deny writing to ctx->optname", + .insns = { + /* ctx->optname = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optname)), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "getsockopt: read ctx->optlen", + .insns = { + /* r6 = ctx->optlen */ + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optlen)), + + /* if (ctx->optlen == 64) { */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 2), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_JMP_A(1), + /* } else { */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .get_optlen = 64, + }, + { + .descr = "getsockopt: deny bigger ctx->optlen", + .insns = { + /* ctx->optlen = 65 */ + BPF_MOV64_IMM(BPF_REG_0, 65), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optlen)), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .get_optlen = 64, + + .error = EFAULT_GETSOCKOPT, + }, + { + .descr = "getsockopt: support smaller ctx->optlen", + .insns = { + /* ctx->optlen = 32 */ + BPF_MOV64_IMM(BPF_REG_0, 32), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optlen)), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .get_optlen = 64, + .get_optlen_ret = 32, + }, + { + .descr = "getsockopt: deny writing to ctx->optval", + .insns = { + /* ctx->optval = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optval)), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "getsockopt: deny writing to ctx->optval_end", + .insns = { + /* ctx->optval_end = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optval_end)), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .error = DENY_LOAD, + }, + + { + .descr = "getsockopt: rewrite value", + .insns = { + /* r6 = ctx->optval */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optval)), + /* r2 = ctx->optval */ + BPF_MOV64_REG(BPF_REG_2, BPF_REG_6), + /* r6 = ctx->optval + 1 */ + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), + + /* r7 = ctx->optval_end */ + BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1, + offsetof(struct bpf_sockopt, optval_end)), + + /* if (ctx->optval + 1 <= ctx->optval_end) { */ + BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1), + /* ctx->optval[0] = 0xF0 */ + BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0), + /* } */ + + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .level = SOL_IP, + .optname = IP_TOS, + + .get_optval = { 0xF0 }, + .get_optlen = 1, + }, + + /* ==================== setsockopt ==================== */ + + { + .descr = "setsockopt: no expected_attach_type", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = 0, + .error = DENY_LOAD, + }, + { + .descr = "setsockopt: wrong expected_attach_type", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + .error = DENY_ATTACH, + }, + { + .descr = "setsockopt: bypass bpf hook", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .level = SOL_IP, + .optname = IP_TOS, + + .set_optval = { 1 << 3 }, + .set_optlen = 1, + + .get_optval = { 1 << 3 }, + .get_optlen = 1, + }, + { + .descr = "setsockopt: return EPERM from bpf hook", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .level = SOL_IP, + .optname = IP_TOS, + + .set_optlen = 1, + .error = EPERM_SETSOCKOPT, + }, + { + .descr = "setsockopt: no optval bounds check, deny loading", + .insns = { + /* r6 = ctx->optval */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optval)), + + /* r0 = ctx->optval[0] */ + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0), + + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + .error = DENY_LOAD, + }, + { + .descr = "setsockopt: read ctx->level", + .insns = { + /* r6 = ctx->level */ + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, level)), + + /* if (ctx->level == 123) { */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 2), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_JMP_A(1), + /* } else { */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .level = 123, + + .set_optlen = 1, + }, + { + .descr = "setsockopt: deny writing to ctx->level", + .insns = { + /* ctx->level = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, level)), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "setsockopt: read ctx->optname", + .insns = { + /* r6 = ctx->optname */ + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optname)), + + /* if (ctx->optname == 123) { */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 2), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_JMP_A(1), + /* } else { */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .optname = 123, + + .set_optlen = 1, + }, + { + .descr = "setsockopt: deny writing to ctx->optname", + .insns = { + /* ctx->optname = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optname)), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "setsockopt: read ctx->optlen", + .insns = { + /* r6 = ctx->optlen */ + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optlen)), + + /* if (ctx->optlen == 64) { */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 2), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_JMP_A(1), + /* } else { */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .set_optlen = 64, + }, + { + .descr = "setsockopt: deny writing to ctx->optlen", + .insns = { + /* ctx->optlen = 32 */ + BPF_MOV64_IMM(BPF_REG_0, 32), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optlen)), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .set_optlen = 64, + + .error = DENY_LOAD, + }, + { + .descr = "setsockopt: deny writing to ctx->optval", + .insns = { + /* ctx->optval = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optval)), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "setsockopt: deny writing to ctx->optval_end", + .insns = { + /* ctx->optval_end = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optval_end)), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "setsockopt: allow IP_TOS <= 128", + .insns = { + /* r6 = ctx->optval */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optval)), + /* r7 = ctx->optval + 1 */ + BPF_MOV64_REG(BPF_REG_7, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1), + + /* r8 = ctx->optval_end */ + BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1, + offsetof(struct bpf_sockopt, optval_end)), + + /* if (ctx->optval + 1 <= ctx->optval_end) { */ + BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4), + + /* r9 = ctx->optval[0] */ + BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0), + + /* if (ctx->optval[0] < 128) */ + BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_JMP_A(1), + /* } */ + + /* } else { */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .level = SOL_IP, + .optname = IP_TOS, + + .set_optval = { 0x80 }, + .set_optlen = 1, + .get_optval = { 0x80 }, + .get_optlen = 1, + }, + { + .descr = "setsockopt: deny IP_TOS > 128", + .insns = { + /* r6 = ctx->optval */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optval)), + /* r7 = ctx->optval + 1 */ + BPF_MOV64_REG(BPF_REG_7, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1), + + /* r8 = ctx->optval_end */ + BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1, + offsetof(struct bpf_sockopt, optval_end)), + + /* if (ctx->optval + 1 <= ctx->optval_end) { */ + BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4), + + /* r9 = ctx->optval[0] */ + BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0), + + /* if (ctx->optval[0] < 128) */ + BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_JMP_A(1), + /* } */ + + /* } else { */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .level = SOL_IP, + .optname = IP_TOS, + + .set_optval = { 0x81 }, + .set_optlen = 1, + .get_optval = { 0x00 }, + .get_optlen = 1, + + .error = EPERM_SETSOCKOPT, + }, +}; + +static int load_prog(const struct bpf_insn *insns, + enum bpf_attach_type expected_attach_type) +{ + struct bpf_load_program_attr attr = { + .prog_type = BPF_PROG_TYPE_CGROUP_SOCKOPT, + .expected_attach_type = expected_attach_type, + .insns = insns, + .license = "GPL", + .log_level = 2, + }; + int fd; + + for (; + insns[attr.insns_cnt].code != (BPF_JMP | BPF_EXIT); + attr.insns_cnt++) { + } + attr.insns_cnt++; + + fd = bpf_load_program_xattr(&attr, bpf_log_buf, sizeof(bpf_log_buf)); + if (verbose && fd < 0) + fprintf(stderr, "%s\n", bpf_log_buf); + + return fd; +} + +static int run_test(int cgroup_fd, struct sockopt_test *test) +{ + int sock_fd, err, prog_fd; + void *optval = NULL; + int ret = 0; + + prog_fd = load_prog(test->insns, test->expected_attach_type); + if (prog_fd < 0) { + if (test->error == DENY_LOAD) + return 0; + + perror("bpf_program__load"); + return -1; + } + + err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0); + if (err < 0) { + if (test->error == DENY_ATTACH) + goto close_prog_fd; + + perror("bpf_prog_attach"); + ret = -1; + goto close_prog_fd; + } + + sock_fd = socket(AF_INET, SOCK_STREAM, 0); + if (sock_fd < 0) { + perror("socket"); + ret = -1; + goto detach_prog; + } + + if (test->set_optlen) { + err = setsockopt(sock_fd, test->level, test->optname, + test->set_optval, test->set_optlen); + if (err) { + if (errno == EPERM && test->error == EPERM_SETSOCKOPT) + goto close_sock_fd; + + perror("setsockopt"); + ret = -1; + goto close_sock_fd; + } + } + + if (test->get_optlen) { + optval = malloc(test->get_optlen); + socklen_t optlen = test->get_optlen; + socklen_t expected_get_optlen = test->get_optlen_ret ?: + test->get_optlen; + + err = getsockopt(sock_fd, test->level, test->optname, + optval, &optlen); + if (err) { + if (errno == EPERM && test->error == EPERM_GETSOCKOPT) + goto free_optval; + if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT) + goto free_optval; + + perror("getsockopt"); + ret = -1; + goto free_optval; + } + + if (optlen != expected_get_optlen) { + perror("getsockopt optlen"); + ret = -1; + goto free_optval; + } + + if (memcmp(optval, test->get_optval, optlen) != 0) { + perror("getsockopt optval"); + ret = -1; + goto free_optval; + } + } + + ret = test->error != OK; + +free_optval: + free(optval); +close_sock_fd: + close(sock_fd); +detach_prog: + bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type); +close_prog_fd: + close(prog_fd); + return ret; +} + +int main(int args, char **argv) +{ + int err = EXIT_FAILURE, error_cnt = 0; + int cgroup_fd, i; + + if (setup_cgroup_environment()) + goto cleanup_obj; + + cgroup_fd = create_and_get_cgroup(CG_PATH); + if (cgroup_fd < 0) + goto cleanup_cgroup_env; + + if (join_cgroup(CG_PATH)) + goto cleanup_cgroup; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + int err = run_test(cgroup_fd, &tests[i]); + + if (err) + error_cnt++; + + printf("#%d %s: %s\n", i, err ? "FAIL" : "PASS", + tests[i].descr); + } + + printf("Summary: %ld PASSED, %d FAILED\n", + ARRAY_SIZE(tests) - error_cnt, error_cnt); + err = error_cnt ? EXIT_FAILURE : EXIT_SUCCESS; + +cleanup_cgroup: + close(cgroup_fd); +cleanup_cgroup_env: + cleanup_cgroup_environment(); +cleanup_obj: + return err; +} From patchwork Mon Jun 17 18:01:06 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanislav Fomichev X-Patchwork-Id: 1117257 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: incoming-bpf@patchwork.ozlabs.org Delivered-To: patchwork-incoming-bpf@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=bpf-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="dodxfQXM"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 45SJvD3PMRz9sDX for ; Tue, 18 Jun 2019 04:02:00 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726723AbfFQSB7 (ORCPT ); Mon, 17 Jun 2019 14:01:59 -0400 Received: from mail-ot1-f73.google.com ([209.85.210.73]:44146 "EHLO mail-ot1-f73.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726607AbfFQSB7 (ORCPT ); Mon, 17 Jun 2019 14:01:59 -0400 Received: by mail-ot1-f73.google.com with SMTP id q16so924175otn.11 for ; Mon, 17 Jun 2019 11:01:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=r1lVzBDqbxPjT7feQO/qbhQePatoVMrgNv/gJ19jM0o=; b=dodxfQXMk23OkceEkP+zRhtrj9igQYnX76Zrw7gHmQ6a8E6YOXT6QCWoOHjqQtjdap LbY4rDr0a5sZ9/883Wc6afT0SL7Ut2+BvsOv4O9h8+KErKqwwPUbRGrSiYsjLn1pVJ7c 0tg9GtQUtu7HT2Hsy0UpYFOd5s8Y8wOHP4fSn438MTySOItUrT13lm9Dxxw95Vh0El3U Ew0yGnBQKM8LOiKkB2WX6JKOx/vZG5aO5L7UJftlI9De0umXRNYGzZjfkxFleyV2KvI1 o+IKhGAFPyHErhXK5qYu7aHKP4+DTw3Zo76PE7oDdmxsIBJiVoa5Ucj6IhpgTV6wzBKO h/gg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=r1lVzBDqbxPjT7feQO/qbhQePatoVMrgNv/gJ19jM0o=; b=kJWE8SbWySWjHNqJLxzNod8gF8IXOhm2KL7pIII0PAj0xZj77TWkX977+hWSlN6CkV +lSRFzZvnw2F1IlwmkT9yHw1IE7dympNm136g+V3Rj5xY8Z7jSa/2anF080RgKmqd/XH ONrKjpmfoxnMn3s6yrOitKrunAQZtfrnbsl/vvjV6w/yr68eCbx7Yg5o+yaYkBNSd3NZ lSRTEarQ/d2ReAOQL6zKsgCjLwFnOKvjVOTMLu5syzDg+dCCnkAvSHX5hGLEXQwdf79y JK2PErwMyPb+SmZIbJc/6CH5NqK5li8P6Mx3AnL6YBR1b0B4Kj0rq43bcHFX0v++l/5T dIDQ== X-Gm-Message-State: APjAAAV/yTDQF2Sj4qXG8MMqQ0ctcoTeQ7Z0ApigjBN7qhIV4ZvEaPok affWsEU/e7G/KzYhkXd52W9MAeY= X-Google-Smtp-Source: APXvYqwe80Rbk2p7DJ7WU377bT4yyugQ2KSUvXfdUq1g4Uc5GD+S/h9rzlrN3UkRLXKtAMtb7U5NUgo= X-Received: by 2002:aca:d907:: with SMTP id q7mr9481oig.68.1560794517949; Mon, 17 Jun 2019 11:01:57 -0700 (PDT) Date: Mon, 17 Jun 2019 11:01:06 -0700 In-Reply-To: <20190617180109.34950-1-sdf@google.com> Message-Id: <20190617180109.34950-7-sdf@google.com> Mime-Version: 1.0 References: <20190617180109.34950-1-sdf@google.com> X-Mailer: git-send-email 2.22.0.410.gd8fdbe21b5-goog Subject: [PATCH bpf-next v6 6/9] selftests/bpf: add sockopt test that exercises sk helpers From: Stanislav Fomichev To: netdev@vger.kernel.org, bpf@vger.kernel.org Cc: davem@davemloft.net, ast@kernel.org, daniel@iogearbox.net, Stanislav Fomichev , Martin Lau Sender: bpf-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org socktop test that introduces new SOL_CUSTOM sockopt level and stores whatever users sets in sk storage. Whenever getsockopt is called, the original value is retrieved. v6: * test 'ret=1' use-case as well (Alexei Starovoitov) v4: * don't call bpf_sk_fullsock helper v3: * drop (__u8 *)(long) casts for optval{,_end} v2: * new test Cc: Martin Lau Signed-off-by: Stanislav Fomichev --- tools/testing/selftests/bpf/.gitignore | 1 + tools/testing/selftests/bpf/Makefile | 3 +- .../testing/selftests/bpf/progs/sockopt_sk.c | 82 ++++++++ tools/testing/selftests/bpf/test_sockopt_sk.c | 185 ++++++++++++++++++ 4 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/progs/sockopt_sk.c create mode 100644 tools/testing/selftests/bpf/test_sockopt_sk.c diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore index 3fe92601223d..8ac076c311d4 100644 --- a/tools/testing/selftests/bpf/.gitignore +++ b/tools/testing/selftests/bpf/.gitignore @@ -40,3 +40,4 @@ test_hashmap test_btf_dump xdping test_sockopt +test_sockopt_sk diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 4ff4401a4024..33aa4f97af28 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -26,7 +26,7 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user \ test_socket_cookie test_cgroup_storage test_select_reuseport test_section_names \ test_netcnt test_tcpnotify_user test_sock_fields test_sysctl test_hashmap \ - test_btf_dump test_cgroup_attach xdping test_sockopt + test_btf_dump test_cgroup_attach xdping test_sockopt test_sockopt_sk BPF_OBJ_FILES = $(patsubst %.c,%.o, $(notdir $(wildcard progs/*.c))) TEST_GEN_FILES = $(BPF_OBJ_FILES) @@ -102,6 +102,7 @@ $(OUTPUT)/test_sock_fields: cgroup_helpers.c $(OUTPUT)/test_sysctl: cgroup_helpers.c $(OUTPUT)/test_cgroup_attach: cgroup_helpers.c $(OUTPUT)/test_sockopt: cgroup_helpers.c +$(OUTPUT)/test_sockopt_sk: cgroup_helpers.c .PHONY: force diff --git a/tools/testing/selftests/bpf/progs/sockopt_sk.c b/tools/testing/selftests/bpf/progs/sockopt_sk.c new file mode 100644 index 000000000000..49c66d5efcdb --- /dev/null +++ b/tools/testing/selftests/bpf/progs/sockopt_sk.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include "bpf_helpers.h" + +char _license[] SEC("license") = "GPL"; +__u32 _version SEC("version") = 1; + +#define SOL_CUSTOM 0xdeadbeef + +struct socket_storage { + __u8 val; +}; + +struct bpf_map_def SEC("maps") socket_storage_map = { + .type = BPF_MAP_TYPE_SK_STORAGE, + .key_size = sizeof(int), + .value_size = sizeof(struct socket_storage), + .map_flags = BPF_F_NO_PREALLOC, +}; +BPF_ANNOTATE_KV_PAIR(socket_storage_map, int, struct socket_storage); + +SEC("cgroup/getsockopt") +int _getsockopt(struct bpf_sockopt *ctx) +{ + __u8 *optval_end = ctx->optval_end; + __u8 *optval = ctx->optval; + struct socket_storage *storage; + + if (ctx->level == SOL_IP && ctx->optname == IP_TOS) + /* Not interested in SOL_IP:IP_TOS; + * let next BPF program in the cgroup chain or kernel + * handle it. + */ + return 1; + + if (ctx->level != SOL_CUSTOM) + return 0; /* EPERM, deny everything except custom level */ + + if (optval + 1 > optval_end) + return 0; /* EPERM, bounds check */ + + storage = bpf_sk_storage_get(&socket_storage_map, ctx->sk, 0, + BPF_SK_STORAGE_GET_F_CREATE); + if (!storage) + return 0; /* EPERM, couldn't get sk storage */ + + optval[0] = storage->val; + ctx->optlen = 1; + + return 2; /* BPF consumed this option, return to userspace */ +} + +SEC("cgroup/setsockopt") +int _setsockopt(struct bpf_sockopt *ctx) +{ + __u8 *optval_end = ctx->optval_end; + __u8 *optval = ctx->optval; + struct socket_storage *storage; + + if (ctx->level == SOL_IP && ctx->optname == IP_TOS) + /* Not interested in SOL_IP:IP_TOS; + * let next BPF program in the cgroup chain or kernel + * handle it. + */ + return 1; + + if (ctx->level != SOL_CUSTOM) + return 0; /* EPERM, deny everything except custom level */ + + if (optval + 1 > optval_end) + return 0; /* EPERM, bounds check */ + + storage = bpf_sk_storage_get(&socket_storage_map, ctx->sk, 0, + BPF_SK_STORAGE_GET_F_CREATE); + if (!storage) + return 0; /* EPERM, couldn't get sk storage */ + + storage->val = optval[0]; + + return 2; /* BPF consumed this option, return to userspace */ +} diff --git a/tools/testing/selftests/bpf/test_sockopt_sk.c b/tools/testing/selftests/bpf/test_sockopt_sk.c new file mode 100644 index 000000000000..67558e2c5427 --- /dev/null +++ b/tools/testing/selftests/bpf/test_sockopt_sk.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "bpf_rlimit.h" +#include "bpf_util.h" +#include "cgroup_helpers.h" + +#define CG_PATH "/sockopt" + +#define SOL_CUSTOM 0xdeadbeef + +static int getsetsockopt(void) +{ + int fd, err; + char buf[4] = {}; + socklen_t optlen; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + log_err("Failed to create socket"); + return -1; + } + + /* IP_TOS - BPF bypass */ + + buf[0] = 0x08; + err = setsockopt(fd, SOL_IP, IP_TOS, buf, 1); + if (err) { + log_err("Failed to call setsockopt(IP_TOS)"); + goto err; + } + + buf[0] = 0x00; + optlen = 1; + err = getsockopt(fd, SOL_IP, IP_TOS, buf, &optlen); + if (err) { + log_err("Failed to call getsockopt(IP_TOS)"); + goto err; + } + + if (buf[0] != 0x08) { + log_err("Unexpected getsockopt(IP_TOS) buf[0] 0x%02x != 0x08", + buf[0]); + goto err; + } + + /* IP_TTL - EPERM */ + + buf[0] = 1; + err = setsockopt(fd, SOL_IP, IP_TTL, buf, 1); + if (!err || errno != EPERM) { + log_err("Unexpected success from setsockopt(IP_TTL)"); + goto err; + } + + /* SOL_CUSTOM - handled by BPF */ + + buf[0] = 0x01; + err = setsockopt(fd, SOL_CUSTOM, 0, buf, 1); + if (err) { + log_err("Failed to call setsockopt"); + goto err; + } + + buf[0] = 0x00; + optlen = 4; + err = getsockopt(fd, SOL_CUSTOM, 0, buf, &optlen); + if (err) { + log_err("Failed to call getsockopt"); + goto err; + } + + if (optlen != 1) { + log_err("Unexpected optlen %d != 1", optlen); + goto err; + } + if (buf[0] != 0x01) { + log_err("Unexpected buf[0] 0x%02x != 0x01", buf[0]); + goto err; + } + + close(fd); + return 0; +err: + close(fd); + return -1; +} + +static int prog_attach(struct bpf_object *obj, int cgroup_fd, const char *title) +{ + enum bpf_attach_type attach_type; + enum bpf_prog_type prog_type; + struct bpf_program *prog; + int err; + + err = libbpf_prog_type_by_name(title, &prog_type, &attach_type); + if (err) { + log_err("Failed to deduct types for %s BPF program", title); + return -1; + } + + prog = bpf_object__find_program_by_title(obj, title); + if (!prog) { + log_err("Failed to find %s BPF program", title); + return -1; + } + + err = bpf_prog_attach(bpf_program__fd(prog), cgroup_fd, + attach_type, 0); + if (err) { + log_err("Failed to attach %s BPF program", title); + return -1; + } + + return 0; +} + +static int run_test(int cgroup_fd) +{ + struct bpf_prog_load_attr attr = { + .file = "./sockopt_sk.o", + }; + struct bpf_object *obj; + int ignored; + int err; + + err = bpf_prog_load_xattr(&attr, &obj, &ignored); + if (err) { + log_err("Failed to load BPF object"); + return -1; + } + + err = prog_attach(obj, cgroup_fd, "cgroup/getsockopt"); + if (err) + goto close_bpf_object; + + err = prog_attach(obj, cgroup_fd, "cgroup/setsockopt"); + if (err) + goto close_bpf_object; + + err = getsetsockopt(); + +close_bpf_object: + bpf_object__close(obj); + return err; +} + +int main(int args, char **argv) +{ + int cgroup_fd; + int err = EXIT_SUCCESS; + + if (setup_cgroup_environment()) + goto cleanup_obj; + + cgroup_fd = create_and_get_cgroup(CG_PATH); + if (cgroup_fd < 0) + goto cleanup_cgroup_env; + + if (join_cgroup(CG_PATH)) + goto cleanup_cgroup; + + if (run_test(cgroup_fd)) + err = EXIT_FAILURE; + + printf("test_sockopt_sk: %s\n", + err == EXIT_SUCCESS ? "PASSED" : "FAILED"); + +cleanup_cgroup: + close(cgroup_fd); +cleanup_cgroup_env: + cleanup_cgroup_environment(); +cleanup_obj: + return err; +} From patchwork Mon Jun 17 18:01:07 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanislav Fomichev X-Patchwork-Id: 1117259 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: incoming-bpf@patchwork.ozlabs.org Delivered-To: patchwork-incoming-bpf@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=bpf-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="Lvmzm3oG"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 45SJvH3tX7z9sDX for ; Tue, 18 Jun 2019 04:02:03 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726834AbfFQSCD (ORCPT ); Mon, 17 Jun 2019 14:02:03 -0400 Received: from mail-pf1-f202.google.com ([209.85.210.202]:44516 "EHLO mail-pf1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726607AbfFQSCC (ORCPT ); Mon, 17 Jun 2019 14:02:02 -0400 Received: by mail-pf1-f202.google.com with SMTP id 5so7473134pff.11 for ; Mon, 17 Jun 2019 11:02:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=QOvCdMx8lS0T9jG22RJpZ/dXAJ7P9lLIIHx5Sw8+qPA=; b=Lvmzm3oGqaKwePBEbelirjw2EIIwVsJKYEmLipMzVsI8g3T+EM6N7D/mJtSYQwAlQo MXSVHUgXkUJQ4X03L8Nj1kzfr3VR537NrCOytiljw2fv68nZow/s8wDX2nMSvxeXon1k 8IV9oG1W3czaulx/UpW63aJyN9FONL3WWQ9d4Uf/16jMSQTSzTlPSaF+ir2wIK3cuf4W fJ9rQzCEsdDVaCUOYFwahF7BtWQhXsua4MTHRwpJ/2Sn3v8HvqyWzq96QhKEI034Nmtm RlWEGENG5SiY87UybKhybPfW/OR/Sp8xgbw/XvwHvZlrIKVjiuM1N8x+b45L37jOn4r6 IsIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=QOvCdMx8lS0T9jG22RJpZ/dXAJ7P9lLIIHx5Sw8+qPA=; b=DWatt6yolLGx/esmkR7K9eRMTpvL5mCenV3eIilT879J2NByS4cgsMVUsP4WOsRLOV vJ5P4z4u5u0CSjuDex+LqBaqsn2nea/qrXDZNv34ZcdTxIh5jDPkmiUJjHPgLytG/tzH FqIovSUU0nv3ek5wK9W2jS//AHdkSnGVK5ADtNizP8e8cKg2V8EcCRIdbf1P3OpaeCcW esJbWbPwWCKVQdJzlmJtVLMjhbMzYRED4zfF3zaMZZ7PQ4q2kQqhf+bIx4h2jHIXDyVR Jfsv1fT3g6od1XnVAnMqYy869Jt+c9AjoRyC3NKZZFwzudCV+iC3MAYZ2EEeYln2M7AY BRUw== X-Gm-Message-State: APjAAAWdvSJk5EVNYwqKl0Oh+z482dx6zk+pf7n5QZADTDPgGOrDtHm8 YDrbSxkTsIfRApwwgHQ/tNjygFQ= X-Google-Smtp-Source: APXvYqy3ZCrpDaGw2Clm04piN+Ee8uk+X9b3ZsI76kj+DCWl9iF92edyBn6Ym4qWozYmiUzR0I6+WQ8= X-Received: by 2002:a65:648e:: with SMTP id e14mr507pgv.317.1560794520074; Mon, 17 Jun 2019 11:02:00 -0700 (PDT) Date: Mon, 17 Jun 2019 11:01:07 -0700 In-Reply-To: <20190617180109.34950-1-sdf@google.com> Message-Id: <20190617180109.34950-8-sdf@google.com> Mime-Version: 1.0 References: <20190617180109.34950-1-sdf@google.com> X-Mailer: git-send-email 2.22.0.410.gd8fdbe21b5-goog Subject: [PATCH bpf-next v6 7/9] selftests/bpf: add sockopt test that exercises BPF_F_ALLOW_MULTI From: Stanislav Fomichev To: netdev@vger.kernel.org, bpf@vger.kernel.org Cc: davem@davemloft.net, ast@kernel.org, daniel@iogearbox.net, Stanislav Fomichev , Martin Lau Sender: bpf-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org sockopt test that verifies chaining behavior when 0/2 is returned. Cc: Martin Lau Signed-off-by: Stanislav Fomichev --- tools/testing/selftests/bpf/.gitignore | 1 + tools/testing/selftests/bpf/Makefile | 4 +- .../selftests/bpf/test_sockopt_multi.c | 264 ++++++++++++++++++ 3 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/test_sockopt_multi.c diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore index 8ac076c311d4..a2f7f79c7908 100644 --- a/tools/testing/selftests/bpf/.gitignore +++ b/tools/testing/selftests/bpf/.gitignore @@ -41,3 +41,4 @@ test_btf_dump xdping test_sockopt test_sockopt_sk +test_sockopt_multi diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 33aa4f97af28..d3a5b6f9080d 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -26,7 +26,8 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user \ test_socket_cookie test_cgroup_storage test_select_reuseport test_section_names \ test_netcnt test_tcpnotify_user test_sock_fields test_sysctl test_hashmap \ - test_btf_dump test_cgroup_attach xdping test_sockopt test_sockopt_sk + test_btf_dump test_cgroup_attach xdping test_sockopt test_sockopt_sk \ + test_sockopt_multi BPF_OBJ_FILES = $(patsubst %.c,%.o, $(notdir $(wildcard progs/*.c))) TEST_GEN_FILES = $(BPF_OBJ_FILES) @@ -103,6 +104,7 @@ $(OUTPUT)/test_sysctl: cgroup_helpers.c $(OUTPUT)/test_cgroup_attach: cgroup_helpers.c $(OUTPUT)/test_sockopt: cgroup_helpers.c $(OUTPUT)/test_sockopt_sk: cgroup_helpers.c +$(OUTPUT)/test_sockopt_multi: cgroup_helpers.c .PHONY: force diff --git a/tools/testing/selftests/bpf/test_sockopt_multi.c b/tools/testing/selftests/bpf/test_sockopt_multi.c new file mode 100644 index 000000000000..e667d762e8d0 --- /dev/null +++ b/tools/testing/selftests/bpf/test_sockopt_multi.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "bpf_rlimit.h" +#include "bpf_util.h" +#include "cgroup_helpers.h" + +static char bpf_log_buf[BPF_LOG_BUF_SIZE]; + +static struct bpf_insn prog_deny[] = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), +}; + +static struct bpf_insn prog_bypass[] = { + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_EXIT_INSN(), +}; + +static struct bpf_insn prog_inc[] = { + /* void *map_fd = NULL (to be filled by main()) */ + BPF_LD_MAP_FD(BPF_REG_1, 0), + + /* __u32 key = 0 */ + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), + BPF_ST_MEM(BPF_W, BPF_REG_2, 0, 0), + + /* r0 = bpf_map_lookup(map_fd, 0) */ + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + /* if (r0 != NULL) { */ + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + /* *r0 += 1 */ + BPF_MOV64_IMM(BPF_REG_1, 1), + BPF_STX_XADD(BPF_W, BPF_REG_0, BPF_REG_1, 0), + /* } */ + + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), +}; + +static int read_cnt(int map_fd) +{ + int key = 0; + int val; + + if (bpf_map_lookup_elem(map_fd, &key, &val) < 0) + error(-1, errno, "Failed to lookup the map"); + + return val; +} + +int main(int argc, char **argv) +{ + int prog_deny_fd = -1, prog_bypass_fd = -1, prog_inc_fd = -1; + struct bpf_load_program_attr load_attr = {}; + int cg_a = -1, cg_a_b = -1; + int err = EXIT_FAILURE; + char buf[1] = { 0x08 }; + int sock_fd = -1; + int map_fd = -1; + int ret; + + load_attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCKOPT, + load_attr.license = "GPL", + load_attr.expected_attach_type = BPF_CGROUP_SETSOCKOPT; + + map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, + sizeof(int), sizeof(int), 1, 0); + if (map_fd < 0) { + log_err("Failed to create map"); + goto out; + } + + prog_inc[0].imm = map_fd; + + if (setup_cgroup_environment()) { + log_err("Failed to setup cgroup environment\n"); + goto out; + } + + cg_a = create_and_get_cgroup("/a"); + if (cg_a < 0) { + log_err("Failed to create cgroup /a\n"); + goto out; + } + + cg_a_b = create_and_get_cgroup("/a/b"); + if (cg_a_b < 0) { + log_err("Failed to create cgroup /a/b\n"); + goto out; + } + + if (join_cgroup("/a/b")) { + log_err("Failed to join cgroup /a/b\n"); + goto out; + } + + sock_fd = socket(AF_INET, SOCK_STREAM, 0); + if (sock_fd < 0) { + log_err("Failed to create socket"); + goto out; + } + + load_attr.insns = prog_deny; + load_attr.insns_cnt = ARRAY_SIZE(prog_deny); + prog_deny_fd = bpf_load_program_xattr(&load_attr, bpf_log_buf, + sizeof(bpf_log_buf)); + if (prog_deny_fd < 0) { + log_err("Failed to load prog_deny:\n%s\n", bpf_log_buf); + goto out; + } + + load_attr.insns = prog_bypass; + load_attr.insns_cnt = ARRAY_SIZE(prog_bypass); + prog_bypass_fd = bpf_load_program_xattr(&load_attr, bpf_log_buf, + sizeof(bpf_log_buf)); + if (prog_bypass_fd < 0) { + log_err("Failed to load prog_bypass:\n%s\n", bpf_log_buf); + goto out; + } + + load_attr.insns = prog_inc; + load_attr.insns_cnt = ARRAY_SIZE(prog_inc); + prog_inc_fd = bpf_load_program_xattr(&load_attr, bpf_log_buf, + sizeof(bpf_log_buf)); + if (prog_inc_fd < 0) { + log_err("Failed to load prog_inc:\n%s\n", bpf_log_buf); + goto out; + } + + if (bpf_prog_attach(prog_inc_fd, cg_a, + BPF_CGROUP_SETSOCKOPT, BPF_F_ALLOW_MULTI)) { + log_err("Failed to attach prog_inc\n"); + goto out; + } + + /* No program was triggered so far, expected value is 0. + */ + + ret = read_cnt(map_fd); + if (ret != 0) { + log_err("Unexpected initial map value %d != 0\n", ret); + goto out; + } + + /* Call setsockopt that should trigger bpf program in the parent + * cgroup and increase the counter to 1. + */ + + if (setsockopt(sock_fd, SOL_IP, IP_TOS, buf, 1) < 0) { + log_err("Failed to call setsockopt(IP_TOS)"); + goto out; + } + + ret = read_cnt(map_fd); + if (ret != 1) { + log_err("Unexpected prog_inc sockopt map value %d != 1\n", ret); + goto out; + } + + /* Attach program that returns 0 to current cgroup, parent program + * should not trigger. + */ + + if (bpf_prog_attach(prog_deny_fd, cg_a_b, + BPF_CGROUP_SETSOCKOPT, BPF_F_ALLOW_MULTI)) { + log_err("Failed to attach prog_deny\n"); + goto out; + } + + if (setsockopt(sock_fd, SOL_IP, IP_TOS, buf, 1) >= 0) { + log_err("Unexpected success when calling setsockopt(IP_TOS)"); + goto out; + } + + ret = read_cnt(map_fd); + if (ret != 1) { + log_err("Unexpected prog_deny map value %d != 1\n", ret); + goto out; + } + + /* Attach program that returns 2 to current cgroup, parent program + * should not trigger. + */ + + if (bpf_prog_detach2(prog_deny_fd, cg_a_b, BPF_CGROUP_SETSOCKOPT)) { + log_err("Failed to detach prog_deny\n"); + goto out; + } + + if (bpf_prog_attach(prog_bypass_fd, cg_a_b, + BPF_CGROUP_SETSOCKOPT, BPF_F_ALLOW_MULTI)) { + log_err("Failed to attach prog_bypass\n"); + goto out; + } + + if (setsockopt(sock_fd, SOL_IP, IP_TOS, buf, 1) < 0) { + log_err("Failed to call setsockopt(IP_TOS)"); + goto out; + } + + ret = read_cnt(map_fd); + if (ret != 1) { + log_err("Unexpected prog_bypass map value %d != 1\n", ret); + goto out; + } + + /* Attach the same program that increases the counters to current + * cgroup, bpf program should trigger twice. + */ + + if (bpf_prog_detach2(prog_bypass_fd, cg_a_b, BPF_CGROUP_SETSOCKOPT)) { + log_err("Failed to detach prog_deny\n"); + goto out; + } + + if (bpf_prog_attach(prog_inc_fd, cg_a_b, + BPF_CGROUP_SETSOCKOPT, BPF_F_ALLOW_MULTI)) { + log_err("Failed to attach prog_inc\n"); + goto out; + } + + if (setsockopt(sock_fd, SOL_IP, IP_TOS, buf, 1) < 0) { + log_err("Failed to call setsockopt(IP_TOS)"); + goto out; + } + + ret = read_cnt(map_fd); + if (ret != 3) { + log_err("Unexpected 2x prog_inc map value %d != 3\n", ret); + goto out; + } + + err = EXIT_SUCCESS; + +out: + bpf_prog_detach2(prog_inc_fd, cg_a, BPF_CGROUP_SETSOCKOPT); + bpf_prog_detach2(prog_inc_fd, cg_a_b, BPF_CGROUP_SETSOCKOPT); + close(prog_inc_fd); + close(prog_bypass_fd); + close(prog_deny_fd); + close(sock_fd); + close(cg_a_b); + close(cg_a); + close(map_fd); + + printf("test_sockopt_multi: %s\n", + err == EXIT_SUCCESS ? "PASSED" : "FAILED"); + return err; +} From patchwork Mon Jun 17 18:01:08 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanislav Fomichev X-Patchwork-Id: 1117261 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: incoming-bpf@patchwork.ozlabs.org Delivered-To: patchwork-incoming-bpf@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=bpf-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="kLNAgFlZ"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 45SJvK529qz9sDX for ; Tue, 18 Jun 2019 04:02:05 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726964AbfFQSCF (ORCPT ); Mon, 17 Jun 2019 14:02:05 -0400 Received: from mail-pg1-f202.google.com ([209.85.215.202]:43447 "EHLO mail-pg1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726898AbfFQSCF (ORCPT ); Mon, 17 Jun 2019 14:02:05 -0400 Received: by mail-pg1-f202.google.com with SMTP id p7so2403721pgr.10 for ; Mon, 17 Jun 2019 11:02:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=nJpydzbPak9Eg8s7xRZ7/o8RHZeBNqY37LOtGQ3WNlY=; b=kLNAgFlZnrXl+BN0LZkAmCiC5EEg6vwBQlGfoc+KpEn4Hf3r7HOENoXJgsa2DD7pUf hI1CyrssUsqCHVnRvZIiYLS2Nu/KBWSSm3fBO6hZf+s8aTiZxqyUz0kbz4V+U4Z/0Rbm rue9D4n3yJkyA5mIzCBwM2YrBrLOAoflIRYVOIpRouQiMuaIXPY51tyGf7/kb61CxKK2 z4iBsVrtCfdLlkEtYg89YB+/v+p7F4y7bX0H8hJnGhwLYkBQkwXSJNuMMManT2fAwSEC u51KGdj7jRIQTi3jw4Q27SutjBXOBsKm1zYrz2wIsdwotGYIlV2rk/YFq+BguIbkKGJF HDdA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=nJpydzbPak9Eg8s7xRZ7/o8RHZeBNqY37LOtGQ3WNlY=; b=O6xKad05170ORRNGYMB0v+0am8Eyga83ir5GABqRz5ynqi2qiajb76FruuXIyKTc0f 6Jk4E3/6ZqW8Hyb4cmqigFyX9DPhmaNVYl49Iws5V8ApnD4DolpX6hPh7LS+YGI3Rf/L xKGZ7kIe4Bg/oVr8t32L3+gIJmqNIjrmwXPUmHRCQ18Pqs6RlzfrKMC7PA/8hXRagSIH OOek6/OrH4R1YAcDO41Z7+9F+41bpTnaZeyb0MQqwTOSfwajtV2KwwQiw/uA/EU8rSb4 Y0ydnQ/XDRE2mQGarpxgD/n22Cael95MDtKPXN3dY/iFS+Be49XcqymUmSyJdsTt8CDO VIcw== X-Gm-Message-State: APjAAAUrQitxHSP684KrEy8GwPdg4ZuFoRk0VabQ5SiLIvJI5orMI7O7 2+ww+rwTX4Bhc+aVt+S01W7UMUs= X-Google-Smtp-Source: APXvYqyPLznNUF2+eeXDxWfdNKqfwBD92iAwfzCjOMv4dONWjweNoSxWDTqTi7tP/B+I8g740PD6lPE= X-Received: by 2002:a63:f146:: with SMTP id o6mr5967pgk.179.1560794523619; Mon, 17 Jun 2019 11:02:03 -0700 (PDT) Date: Mon, 17 Jun 2019 11:01:08 -0700 In-Reply-To: <20190617180109.34950-1-sdf@google.com> Message-Id: <20190617180109.34950-9-sdf@google.com> Mime-Version: 1.0 References: <20190617180109.34950-1-sdf@google.com> X-Mailer: git-send-email 2.22.0.410.gd8fdbe21b5-goog Subject: [PATCH bpf-next v6 8/9] bpf: add sockopt documentation From: Stanislav Fomichev To: netdev@vger.kernel.org, bpf@vger.kernel.org Cc: davem@davemloft.net, ast@kernel.org, daniel@iogearbox.net, Stanislav Fomichev , Martin Lau Sender: bpf-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org Provide user documentation about sockopt prog type and cgroup hooks. v6: * describe cgroup chaining, add example v2: * use return code 2 for kernel bypass Cc: Martin Lau Signed-off-by: Stanislav Fomichev --- Documentation/bpf/index.rst | 1 + Documentation/bpf/prog_cgroup_sockopt.rst | 72 +++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 Documentation/bpf/prog_cgroup_sockopt.rst diff --git a/Documentation/bpf/index.rst b/Documentation/bpf/index.rst index d3fe4cac0c90..801a6ed3f2e5 100644 --- a/Documentation/bpf/index.rst +++ b/Documentation/bpf/index.rst @@ -42,6 +42,7 @@ Program types .. toctree:: :maxdepth: 1 + prog_cgroup_sockopt prog_cgroup_sysctl prog_flow_dissector diff --git a/Documentation/bpf/prog_cgroup_sockopt.rst b/Documentation/bpf/prog_cgroup_sockopt.rst new file mode 100644 index 000000000000..8b9d55a3e655 --- /dev/null +++ b/Documentation/bpf/prog_cgroup_sockopt.rst @@ -0,0 +1,72 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================ +BPF_PROG_TYPE_CGROUP_SOCKOPT +============================ + +``BPF_PROG_TYPE_CGROUP_SOCKOPT`` program type can be attached to two +cgroup hooks: + +* ``BPF_CGROUP_GETSOCKOPT`` - called every time process executes ``getsockopt`` + system call. +* ``BPF_CGROUP_SETSOCKOPT`` - called every time process executes ``setsockopt`` + system call. + +The context (``struct bpf_sockopt``) has associated socket (``sk``) and +all input arguments: ``level``, ``optname``, ``optval`` and ``optlen``. + +BPF_CGROUP_SETSOCKOPT +===================== + +``BPF_CGROUP_SETSOCKOPT`` has a read-only context and this hook has +access to cgroup and socket local storage. + +BPF_CGROUP_GETSOCKOPT +===================== + +``BPF_CGROUP_GETSOCKOPT`` has to fill in ``optval`` and adjust +``optlen`` accordingly. Input ``optlen`` contains the maximum length +of data that can be returned to the userspace. In other words, BPF +program can't increase ``optlen``, it can only decrease it. + +Return Type +=========== + +* ``0`` - reject the syscall, ``EPERM`` will be returned to the userspace. +* ``1`` - success: after returning from the BPF hook, kernel will also + handle this socket option. +* ``2`` - success: after returning from the BPF hook, kernel will _not_ + handle this socket option; control will be returned to the userspace + instead. + +Cgroup Inheritance +================== + +Suppose, there is the following cgroup hierarchy where each cgroup +has BPF_CGROUP_GETSOCKOPT attached at each level with +BPF_F_ALLOW_MULTI flag:: + + A (root) + \ + B + \ + C + +When the application calls getsockopt syscall from the cgroup C, +the programs are executed from the bottom up: C, B, A. As long as +BPF programs in the chain return 1, the execution continues. If +some program in the C, B, A chain returns 2 (bypass kernel) or +0 (EPERM), the control is immediately passed passed back to the +userspace. This is in contrast with any existing per-cgroup BPF +hook where all programs are called, even if some of them return +0 (EPERM). + +In the example above, if C returns 1 (continue) and then B returns +0 (EPERM) or 2 (bypass kernel), the program attached to A will _not_ +be executed. + +Example +======= + +See ``tools/testing/selftests/bpf/progs/sockopt_sk.c`` for an example +of BPF program that handles socket options. From patchwork Mon Jun 17 18:01:09 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanislav Fomichev X-Patchwork-Id: 1117263 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="lp35msJJ"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 45SJvP3d9bz9sND for ; Tue, 18 Jun 2019 04:02:09 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727523AbfFQSCI (ORCPT ); Mon, 17 Jun 2019 14:02:08 -0400 Received: from mail-qt1-f202.google.com ([209.85.160.202]:37396 "EHLO mail-qt1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727296AbfFQSCH (ORCPT ); Mon, 17 Jun 2019 14:02:07 -0400 Received: by mail-qt1-f202.google.com with SMTP id g56so9955956qte.4 for ; Mon, 17 Jun 2019 11:02:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=q6fAxPcopqOzKqAUUbb7EJUjbkYw4Cmey6lp2gh3NIw=; b=lp35msJJoyw9vPcyZJX2XnGmbmcqUYShSMIYpR4O5l/aeCzX92vpmyyjWoD7eMAv2u IbhOrrkv7x/fsCcVSWNrJqyOR+KCYDfzedWFG7wVTVAypWIA7nEWMMJwcmCO6GZlr8ho StzDNuZFQPzp5FoJ2VtXZ+LaR+PNKRf2LDup7InyTqxUaJGOfyEmEN50JSB0HaTch91t mFO/zOBuorUWfix4ob8XphFv/rBuj1JSq2jA9HxKz6d/TIHmTbUkfOn5HtnPsgd6MonH fGtg7vA6hjBFV6taT3QdbcPU3ge5XjoC9gLNUg8uMPd9R63f+RXRYF2LjXShM0/0WEuR YS3Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=q6fAxPcopqOzKqAUUbb7EJUjbkYw4Cmey6lp2gh3NIw=; b=sXzFRt4sIO41cFtC42nmAHXp4G+kF6Zq0jYDsOT1+h3tEiPpy6C6qkoMVo9HPcMV83 EMhNjUKf2C766w0ftC6CoOEOZHfQvEuD4WTphZCH97rLI8G16c9k2o5xctTOKwYn8/f/ lSYNQ9SB6vimfx8VI3UJQ7V/AggfWYqawODIUJUzKuHXvqKARRkFSrcqZtDsH0NPw3aU 6QCC8ARdhOWsrXnG3iW5xRtmDk6nJkagpTYaJuQrF6EdIYqgf4KFm4WjWtBlo/qfN7X6 qXW4MzEo4Xv3cOZY8Dnp0GJ0ezSqUhEdFKp5MDCegzS/eCifMl9uH8spRwD+CF07fLKM SKhQ== X-Gm-Message-State: APjAAAWWkLQx9RpnW2MTTYMm1b5cblMzdSW3+H5f0sOwAIAscDLVTQ2k vm7WD1Zpi4XPiJdV5+2l4/QTV1LjBTl+vVH7kXHc/AI6T4J7IfgFxYnoqFpqsFphMmyJKfoZkYA jnHs3eeYpTfZYBCcutZZPtUHUr9lFCNgIv8dkDCvZ0Qup8VQ8P7Eevg== X-Google-Smtp-Source: APXvYqxNA6xXx+YNo8r9AdPtGkfS1S4tcWad2IDkcTMFGnYSsn/DSauwT/cddaISCEqhEfg0S6yq78c= X-Received: by 2002:aed:3e0f:: with SMTP id l15mr97556106qtf.251.1560794526247; Mon, 17 Jun 2019 11:02:06 -0700 (PDT) Date: Mon, 17 Jun 2019 11:01:09 -0700 In-Reply-To: <20190617180109.34950-1-sdf@google.com> Message-Id: <20190617180109.34950-10-sdf@google.com> Mime-Version: 1.0 References: <20190617180109.34950-1-sdf@google.com> X-Mailer: git-send-email 2.22.0.410.gd8fdbe21b5-goog Subject: [PATCH bpf-next v6 9/9] bpftool: support cgroup sockopt From: Stanislav Fomichev To: netdev@vger.kernel.org, bpf@vger.kernel.org Cc: davem@davemloft.net, ast@kernel.org, daniel@iogearbox.net, Stanislav Fomichev , Martin Lau , Jakub Kicinski Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Support sockopt prog type and cgroup hooks in the bpftool. Cc: Martin Lau Acked-by: Jakub Kicinski Signed-off-by: Stanislav Fomichev --- tools/bpf/bpftool/Documentation/bpftool-cgroup.rst | 7 +++++-- tools/bpf/bpftool/Documentation/bpftool-prog.rst | 2 +- tools/bpf/bpftool/bash-completion/bpftool | 8 +++++--- tools/bpf/bpftool/cgroup.c | 5 ++++- tools/bpf/bpftool/main.h | 1 + tools/bpf/bpftool/prog.c | 3 ++- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst index 36807735e2a5..cac088a320a6 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst @@ -29,7 +29,8 @@ CGROUP COMMANDS | *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* } | *ATTACH_TYPE* := { **ingress** | **egress** | **sock_create** | **sock_ops** | **device** | | **bind4** | **bind6** | **post_bind4** | **post_bind6** | **connect4** | **connect6** | -| **sendmsg4** | **sendmsg6** | **sysctl** } +| **sendmsg4** | **sendmsg6** | **sysctl** | **getsockopt** | +| **setsockopt** } | *ATTACH_FLAGS* := { **multi** | **override** } DESCRIPTION @@ -86,7 +87,9 @@ DESCRIPTION unconnected udp4 socket (since 4.18); **sendmsg6** call to sendto(2), sendmsg(2), sendmmsg(2) for an unconnected udp6 socket (since 4.18); - **sysctl** sysctl access (since 5.2). + **sysctl** sysctl access (since 5.2); + **getsockopt** call to getsockopt (since 5.3); + **setsockopt** call to setsockopt (since 5.3). **bpftool cgroup detach** *CGROUP* *ATTACH_TYPE* *PROG* Detach *PROG* from the cgroup *CGROUP* and attach type diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index 228a5c863cc7..c6bade35032c 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -40,7 +40,7 @@ PROG COMMANDS | **lwt_seg6local** | **sockops** | **sk_skb** | **sk_msg** | **lirc_mode2** | | **cgroup/bind4** | **cgroup/bind6** | **cgroup/post_bind4** | **cgroup/post_bind6** | | **cgroup/connect4** | **cgroup/connect6** | **cgroup/sendmsg4** | **cgroup/sendmsg6** | -| **cgroup/sysctl** +| **cgroup/sysctl** | **cgroup/getsockopt** | **cgroup/setsockopt** | } | *ATTACH_TYPE* := { | **msg_verdict** | **stream_verdict** | **stream_parser** | **flow_dissector** diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 2725e27dfa42..7afb8b6fbaaa 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -378,7 +378,8 @@ _bpftool() cgroup/connect4 cgroup/connect6 \ cgroup/sendmsg4 cgroup/sendmsg6 \ cgroup/post_bind4 cgroup/post_bind6 \ - cgroup/sysctl" -- \ + cgroup/sysctl cgroup/getsockopt \ + cgroup/setsockopt" -- \ "$cur" ) ) return 0 ;; @@ -688,7 +689,8 @@ _bpftool() attach|detach) local ATTACH_TYPES='ingress egress sock_create sock_ops \ device bind4 bind6 post_bind4 post_bind6 connect4 \ - connect6 sendmsg4 sendmsg6 sysctl' + connect6 sendmsg4 sendmsg6 sysctl getsockopt \ + setsockopt' local ATTACH_FLAGS='multi override' local PROG_TYPE='id pinned tag' case $prev in @@ -698,7 +700,7 @@ _bpftool() ;; ingress|egress|sock_create|sock_ops|device|bind4|bind6|\ post_bind4|post_bind6|connect4|connect6|sendmsg4|\ - sendmsg6|sysctl) + sendmsg6|sysctl|getsockopt|setsockopt) COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \ "$cur" ) ) return 0 diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c index 7e22f115c8c1..3083f2e4886e 100644 --- a/tools/bpf/bpftool/cgroup.c +++ b/tools/bpf/bpftool/cgroup.c @@ -25,7 +25,8 @@ " ATTACH_TYPE := { ingress | egress | sock_create |\n" \ " sock_ops | device | bind4 | bind6 |\n" \ " post_bind4 | post_bind6 | connect4 |\n" \ - " connect6 | sendmsg4 | sendmsg6 | sysctl }" + " connect6 | sendmsg4 | sendmsg6 | sysctl |\n" \ + " getsockopt | setsockopt }" static const char * const attach_type_strings[] = { [BPF_CGROUP_INET_INGRESS] = "ingress", @@ -42,6 +43,8 @@ static const char * const attach_type_strings[] = { [BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4", [BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6", [BPF_CGROUP_SYSCTL] = "sysctl", + [BPF_CGROUP_GETSOCKOPT] = "getsockopt", + [BPF_CGROUP_SETSOCKOPT] = "setsockopt", [__MAX_BPF_ATTACH_TYPE] = NULL, }; diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 28a2a5857e14..9c5d9c80f71e 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -74,6 +74,7 @@ static const char * const prog_type_name[] = { [BPF_PROG_TYPE_SK_REUSEPORT] = "sk_reuseport", [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector", [BPF_PROG_TYPE_CGROUP_SYSCTL] = "cgroup_sysctl", + [BPF_PROG_TYPE_CGROUP_SOCKOPT] = "cgroup_sockopt", }; extern const char * const map_type_name[]; diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 1f209c80d906..a201e1c83346 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -1070,7 +1070,8 @@ static int do_help(int argc, char **argv) " sk_reuseport | flow_dissector | cgroup/sysctl |\n" " cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n" " cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n" - " cgroup/sendmsg4 | cgroup/sendmsg6 }\n" + " cgroup/sendmsg4 | cgroup/sendmsg6 | cgroup/getsockopt |\n" + " cgroup/setsockopt }\n" " ATTACH_TYPE := { msg_verdict | stream_verdict | stream_parser |\n" " flow_dissector }\n" " " HELP_SPEC_OPTIONS "\n"