* Re: [PATCH net 4/4] tcp: enforce tcp_min_snd_mss in tcp_mtu_probing()
From: Tyler Hicks @ 2019-06-17 17:18 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, netdev, Eric Dumazet, Greg Kroah-Hartman,
Jonathan Looney, Neal Cardwell, Yuchung Cheng, Bruce Curtis,
Jonathan Lemon
In-Reply-To: <20190617170354.37770-5-edumazet@google.com>
On 2019-06-17 10:03:54, Eric Dumazet wrote:
> If mtu probing is enabled tcp_mtu_probing() could very well end up
> with a too small MSS.
>
> Use the new sysctl tcp_min_snd_mss to make sure MSS search
> is performed in an acceptable range.
>
> CVE-2019-11479 -- tcp mss hardcoded to 48
>
> Signed-off-by: Eric Dumazet <edumazet@google.com>
> Reported-by: Jonathan Lemon <jonathan.lemon@gmail.com>
> Cc: Jonathan Looney <jtl@netflix.com>
> Acked-by: Neal Cardwell <ncardwell@google.com>
> Cc: Yuchung Cheng <ycheng@google.com>
> Cc: Tyler Hicks <tyhicks@canonical.com>
As mentioned for the other sysctl patch, I've given the two sysctl
patches a close review and some testing.
Acked-by: Tyler Hicks <tyhicks@canonical.com>
Tyler
> Cc: Bruce Curtis <brucec@netflix.com>
> ---
> net/ipv4/tcp_timer.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
> index 5bad937ce779ef8dca42a26dcbb5f1d60a571c73..c801cd37cc2a9c11f2dd4b9681137755e501a538 100644
> --- a/net/ipv4/tcp_timer.c
> +++ b/net/ipv4/tcp_timer.c
> @@ -155,6 +155,7 @@ static void tcp_mtu_probing(struct inet_connection_sock *icsk, struct sock *sk)
> mss = tcp_mtu_to_mss(sk, icsk->icsk_mtup.search_low) >> 1;
> mss = min(net->ipv4.sysctl_tcp_base_mss, mss);
> mss = max(mss, 68 - tcp_sk(sk)->tcp_header_len);
> + mss = max(mss, net->ipv4.sysctl_tcp_min_snd_mss);
> icsk->icsk_mtup.search_low = tcp_mss_to_mtu(sk, mss);
> }
> tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
> --
> 2.22.0.410.gd8fdbe21b5-goog
>
^ permalink raw reply
* Re: [PATCH v3 bpf-next 9/9] bpf: precise scalar_value tracking
From: Andrii Nakryiko @ 2019-06-17 17:20 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: David S. Miller, Daniel Borkmann, Networking, bpf, Kernel Team
In-Reply-To: <20190615191225.2409862-10-ast@kernel.org>
On Sat, Jun 15, 2019 at 12:13 PM Alexei Starovoitov <ast@kernel.org> wrote:
>
> Introduce precision tracking logic that
> helps cilium programs the most:
> old clang old clang new clang new clang
> with all patches with all patches
> bpf_lb-DLB_L3.o 1838 2283 1923 1863
> bpf_lb-DLB_L4.o 3218 2657 3077 2468
> bpf_lb-DUNKNOWN.o 1064 545 1062 544
> bpf_lxc-DDROP_ALL.o 26935 23045 166729 22629
> bpf_lxc-DUNKNOWN.o 34439 35240 174607 28805
> bpf_netdev.o 9721 8753 8407 6801
> bpf_overlay.o 6184 7901 5420 4754
> bpf_lxc_jit.o 39389 50925 39389 50925
>
<snip>
>
> It doesn't support bpf2bpf calls yet and enabled for root only.
>
> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
> ---
Looks good!
Acked-by: Andrii Nakryiko <andriin@fb.com>
> include/linux/bpf_verifier.h | 18 ++
> kernel/bpf/verifier.c | 491 ++++++++++++++++++++++++++++++++++-
> 2 files changed, 498 insertions(+), 11 deletions(-)
>
<snip>
^ permalink raw reply
* Re: [PATCH bpf-next 2/8] libbpf: extract BTF loading and simplify ELF parsing logic
From: Andrii Nakryiko @ 2019-06-17 17:24 UTC (permalink / raw)
To: Song Liu
Cc: Andrii Nakryiko, bpf, Networking, Alexei Starovoitov,
Daniel Borkmann, Kernel Team
In-Reply-To: <CAPhsuW6kAN=gMjtXiAJazDFTszuq4xE-9OQTP_GhDX2cxym0NQ@mail.gmail.com>
On Sat, Jun 15, 2019 at 1:26 PM Song Liu <liu.song.a23@gmail.com> wrote:
>
> On Mon, Jun 10, 2019 at 9:49 PM Andrii Nakryiko <andriin@fb.com> wrote:
> >
> > As a preparation for adding BTF-based BPF map loading, extract .BTF and
> > .BTF.ext loading logic. Also simplify error handling in
> > bpf_object__elf_collect() by returning early, as there is no common
> > clean up to be done.
> >
> > Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> > ---
> > tools/lib/bpf/libbpf.c | 137 ++++++++++++++++++++++-------------------
> > 1 file changed, 75 insertions(+), 62 deletions(-)
> >
> > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > index ba89d9727137..9e39a0a33aeb 100644
> > --- a/tools/lib/bpf/libbpf.c
> > +++ b/tools/lib/bpf/libbpf.c
> > @@ -1078,6 +1078,58 @@ static void bpf_object__sanitize_btf_ext(struct bpf_object *obj)
> > }
> > }
> >
> > +static int bpf_object__load_btf(struct bpf_object *obj,
> > + Elf_Data *btf_data,
> > + Elf_Data *btf_ext_data)
> > +{
> > + int err = 0;
> > +
> > + if (btf_data) {
> > + obj->btf = btf__new(btf_data->d_buf, btf_data->d_size);
> > + if (IS_ERR(obj->btf)) {
> > + pr_warning("Error loading ELF section %s: %d.\n",
> > + BTF_ELF_SEC, err);
> > + goto out;
>
> If we goto out here, we will return 0.
Yes, it's intentional. BTF is treated as optional, so if we fail to
load it, libbpf will emit warning, but will proceed as nothing
happened and no BTF was supposed to be loaded.
>
> > + }
> > + err = btf__finalize_data(obj, obj->btf);
> > + if (err) {
> > + pr_warning("Error finalizing %s: %d.\n",
> > + BTF_ELF_SEC, err);
> > + goto out;
> > + }
> > + bpf_object__sanitize_btf(obj);
> > + err = btf__load(obj->btf);
> > + if (err) {
> > + pr_warning("Error loading %s into kernel: %d.\n",
> > + BTF_ELF_SEC, err);
> > + goto out;
> > + }
> > + }
> > + if (btf_ext_data) {
> > + if (!obj->btf) {
> > + pr_debug("Ignore ELF section %s because its depending ELF section %s is not found.\n",
> > + BTF_EXT_ELF_SEC, BTF_ELF_SEC);
> > + goto out;
>
> We will also return 0 when goto out here.
See above, it's original behavior of libbpf.
>
> > + }
> > + obj->btf_ext = btf_ext__new(btf_ext_data->d_buf,
> > + btf_ext_data->d_size);
> > + if (IS_ERR(obj->btf_ext)) {
> > + pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
> > + BTF_EXT_ELF_SEC, PTR_ERR(obj->btf_ext));
> > + obj->btf_ext = NULL;
> > + goto out;
> And, here. And we will not free obj->btf.
This is situation in which we successfully loaded .BTF, but failed to
load .BTF.ext. In that case we'll warn about .BTF.ext, but will drop
it and continue with .BTF only.
>
> > + }
> > + bpf_object__sanitize_btf_ext(obj);
> > + }
> > +out:
> > + if (err || IS_ERR(obj->btf)) {
> > + if (!IS_ERR_OR_NULL(obj->btf))
> > + btf__free(obj->btf);
> > + obj->btf = NULL;
> > + }
> > + return 0;
> > +}
> > +
> > static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> > {
> > Elf *elf = obj->efile.elf;
> > @@ -1102,24 +1154,21 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> > if (gelf_getshdr(scn, &sh) != &sh) {
> > pr_warning("failed to get section(%d) header from %s\n",
> > idx, obj->path);
> > - err = -LIBBPF_ERRNO__FORMAT;
> > - goto out;
> > + return -LIBBPF_ERRNO__FORMAT;
> > }
> >
> > name = elf_strptr(elf, ep->e_shstrndx, sh.sh_name);
> > if (!name) {
> > pr_warning("failed to get section(%d) name from %s\n",
> > idx, obj->path);
> > - err = -LIBBPF_ERRNO__FORMAT;
> > - goto out;
> > + return -LIBBPF_ERRNO__FORMAT;
> > }
> >
> > data = elf_getdata(scn, 0);
> > if (!data) {
> > pr_warning("failed to get section(%d) data from %s(%s)\n",
> > idx, name, obj->path);
> > - err = -LIBBPF_ERRNO__FORMAT;
> > - goto out;
> > + return -LIBBPF_ERRNO__FORMAT;
> > }
> > pr_debug("section(%d) %s, size %ld, link %d, flags %lx, type=%d\n",
> > idx, name, (unsigned long)data->d_size,
> > @@ -1130,10 +1179,14 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> > err = bpf_object__init_license(obj,
> > data->d_buf,
> > data->d_size);
> > + if (err)
> > + return err;
> > } else if (strcmp(name, "version") == 0) {
> > err = bpf_object__init_kversion(obj,
> > data->d_buf,
> > data->d_size);
> > + if (err)
> > + return err;
> > } else if (strcmp(name, "maps") == 0) {
> > obj->efile.maps_shndx = idx;
> > } else if (strcmp(name, BTF_ELF_SEC) == 0) {
> > @@ -1144,11 +1197,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> > if (obj->efile.symbols) {
> > pr_warning("bpf: multiple SYMTAB in %s\n",
> > obj->path);
> > - err = -LIBBPF_ERRNO__FORMAT;
> > - } else {
> > - obj->efile.symbols = data;
> > - obj->efile.strtabidx = sh.sh_link;
> > + return -LIBBPF_ERRNO__FORMAT;
> > }
> > + obj->efile.symbols = data;
> > + obj->efile.strtabidx = sh.sh_link;
> > } else if (sh.sh_type == SHT_PROGBITS && data->d_size > 0) {
> > if (sh.sh_flags & SHF_EXECINSTR) {
> > if (strcmp(name, ".text") == 0)
> > @@ -1162,6 +1214,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> >
> > pr_warning("failed to alloc program %s (%s): %s",
> > name, obj->path, cp);
> > + return err;
> > }
> > } else if (strcmp(name, ".data") == 0) {
> > obj->efile.data = data;
> > @@ -1173,8 +1226,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> > pr_debug("skip section(%d) %s\n", idx, name);
> > }
> > } else if (sh.sh_type == SHT_REL) {
> > + int nr_reloc = obj->efile.nr_reloc;
> > void *reloc = obj->efile.reloc;
> > - int nr_reloc = obj->efile.nr_reloc + 1;
> > int sec = sh.sh_info; /* points to other section */
> >
> > /* Only do relo for section with exec instructions */
> > @@ -1184,79 +1237,39 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> > continue;
> > }
> >
> > - reloc = reallocarray(reloc, nr_reloc,
> > + reloc = reallocarray(reloc, nr_reloc + 1,
> > sizeof(*obj->efile.reloc));
> > if (!reloc) {
> > pr_warning("realloc failed\n");
> > - err = -ENOMEM;
> > - } else {
> > - int n = nr_reloc - 1;
> > + return -ENOMEM;
> > + }
> >
> > - obj->efile.reloc = reloc;
> > - obj->efile.nr_reloc = nr_reloc;
> > + obj->efile.reloc = reloc;
> > + obj->efile.nr_reloc++;
> >
> > - obj->efile.reloc[n].shdr = sh;
> > - obj->efile.reloc[n].data = data;
> > - }
> > + obj->efile.reloc[nr_reloc].shdr = sh;
> > + obj->efile.reloc[nr_reloc].data = data;
> > } else if (sh.sh_type == SHT_NOBITS && strcmp(name, ".bss") == 0) {
> > obj->efile.bss = data;
> > obj->efile.bss_shndx = idx;
> > } else {
> > pr_debug("skip section(%d) %s\n", idx, name);
> > }
> > - if (err)
> > - goto out;
> > }
> >
> > if (!obj->efile.strtabidx || obj->efile.strtabidx >= idx) {
> > pr_warning("Corrupted ELF file: index of strtab invalid\n");
> > return -LIBBPF_ERRNO__FORMAT;
> > }
> > - if (btf_data) {
> > - obj->btf = btf__new(btf_data->d_buf, btf_data->d_size);
> > - if (IS_ERR(obj->btf)) {
> > - pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
> > - BTF_ELF_SEC, PTR_ERR(obj->btf));
> > - obj->btf = NULL;
> > - } else {
> > - err = btf__finalize_data(obj, obj->btf);
> > - if (!err) {
> > - bpf_object__sanitize_btf(obj);
> > - err = btf__load(obj->btf);
> > - }
> > - if (err) {
> > - pr_warning("Error finalizing and loading %s into kernel: %d. Ignored and continue.\n",
> > - BTF_ELF_SEC, err);
> > - btf__free(obj->btf);
> > - obj->btf = NULL;
> > - err = 0;
> > - }
> > - }
> > - }
> > - if (btf_ext_data) {
> > - if (!obj->btf) {
> > - pr_debug("Ignore ELF section %s because its depending ELF section %s is not found.\n",
> > - BTF_EXT_ELF_SEC, BTF_ELF_SEC);
> > - } else {
> > - obj->btf_ext = btf_ext__new(btf_ext_data->d_buf,
> > - btf_ext_data->d_size);
> > - if (IS_ERR(obj->btf_ext)) {
> > - pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
> > - BTF_EXT_ELF_SEC,
> > - PTR_ERR(obj->btf_ext));
> > - obj->btf_ext = NULL;
> > - } else {
> > - bpf_object__sanitize_btf_ext(obj);
> > - }
> > - }
> > - }
> > + err = bpf_object__load_btf(obj, btf_data, btf_ext_data);
> > + if (err)
> > + return err;
> > if (bpf_object__has_maps(obj)) {
> > err = bpf_object__init_maps(obj, flags);
> > if (err)
> > - goto out;
> > + return err;
> > }
> > err = bpf_object__init_prog_names(obj);
> > -out:
> > return err;
> > }
> >
> > --
> > 2.17.1
> >
^ permalink raw reply
* Re: [PATCH 2/2] perf trace: Handle NULL pointer dereference in trace__syscall_info()
From: Arnaldo Carvalho de Melo @ 2019-06-17 17:32 UTC (permalink / raw)
To: Leo Yan
Cc: Alexander Shishkin, Jiri Olsa, Namhyung Kim, Alexei Starovoitov,
Daniel Borkmann, Martin KaFai Lau, Song Liu, Yonghong Song,
linux-kernel, netdev, bpf
In-Reply-To: <20190617091140.24372-2-leo.yan@linaro.org>
Em Mon, Jun 17, 2019 at 05:11:40PM +0800, Leo Yan escreveu:
> trace__init_bpf_map_syscall_args() invokes trace__syscall_info() to
> retrieve system calls information, it always passes NULL for 'evsel'
> argument; when id is an invalid value then the logging will try to
> output event name, this triggers NULL pointer dereference.
>
> This patch directly uses string "unknown" for event name when 'evsel'
> is NULL pointer.
>
> Signed-off-by: Leo Yan <leo.yan@linaro.org>
> ---
> tools/perf/builtin-trace.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
> index 5cd74651db4c..49dfb2fd393b 100644
> --- a/tools/perf/builtin-trace.c
> +++ b/tools/perf/builtin-trace.c
> @@ -1764,7 +1764,7 @@ static struct syscall *trace__syscall_info(struct trace *trace,
> static u64 n;
>
> pr_debug("Invalid syscall %d id, skipping (%s, %" PRIu64 ")\n",
> - id, perf_evsel__name(evsel), ++n);
> + id, evsel ? perf_evsel__name(evsel) : "unknown", ++n);
> return NULL;
What do you think of this instead?
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 68beef8f47ff..1d6af95b9207 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -590,6 +590,9 @@ const char *perf_evsel__name(struct perf_evsel *evsel)
{
char bf[128];
+ if (!evsel)
+ goto out_unknown;
+
if (evsel->name)
return evsel->name;
@@ -629,7 +632,10 @@ const char *perf_evsel__name(struct perf_evsel *evsel)
evsel->name = strdup(bf);
- return evsel->name ?: "unknown";
+ if (evsel->name)
+ return evsel->name;
+out_unknown:
+ return "unknown";
}
const char *perf_evsel__group_name(struct perf_evsel *evsel)
^ permalink raw reply related
* Re: [PATCH net 0/4] tcp: make sack processing more robust
From: David Miller @ 2019-06-17 17:41 UTC (permalink / raw)
To: edumazet
Cc: netdev, eric.dumazet, gregkh, jtl, ncardwell, tyhicks, ycheng,
brucec, jonathan.lemon
In-Reply-To: <20190617170354.37770-1-edumazet@google.com>
From: Eric Dumazet <edumazet@google.com>
Date: Mon, 17 Jun 2019 10:03:50 -0700
> Jonathan Looney brought to our attention multiple problems
> in TCP stack at the sender side.
>
> SACK processing can be abused by malicious peers to either
> cause overflows, or increase of memory usage.
>
> First two patches fix the immediate problems.
>
> Since the malicious peers abuse senders by advertizing a very
> small MSS in their SYN or SYNACK packet, the last two
> patches add a new sysctl so that admins can chose a higher
> limit for MSS clamping.
Series applied, thanks Eric.
^ permalink raw reply
* Re: [RFC PATCH net-next 1/2] tcp: ulp: add functions to dump ulp-specific information
From: Jakub Kicinski @ 2019-06-17 17:41 UTC (permalink / raw)
To: Davide Caratti
Cc: David S. Miller, Dave Watson, Boris Pismenny, Aviad Yehezkel,
John Fastabend, Daniel Borkmann, netdev
In-Reply-To: <d840dc535ab546408f6280e04b8d492fa2b0c24c.camel@redhat.com>
On Mon, 17 Jun 2019 15:06:33 +0200, Davide Caratti wrote:
> > > + if (icsk->icsk_ulp_ops->get_info_size)
> > > + size += icsk->icsk_ulp_ops->get_info_size(sk);
> >
> > I don't know the diag code, is the socket locked at this point?
>
> as far as I can see, it's not. Thanks a lot for noticing this!
>
> anyway, I see a similar pattern for icsk_ca_ops: when we set the congestion
> control with do_tcp_setsockopt(), the socket is locked - but then, when 'ss'
> reads a diag request with INET_DIAG_CONG bit set, the value of icsk->icsk_ca_ops
> is accessed with READ_ONCE(), surrounded by rcu_read_{,un}lock().
>
> Maybe it's sufficient to do something similar, and then the socket lock can be
> optionally taken within icsk_ulp_ops->get_info(), only in case we need to access
> members of sk that are protected with the socket lock?
Sounds reasonable, we just need to keep that in mind as we extend TLS
code do dump more information.
^ permalink raw reply
* Re: [PATCH 1/6] net: macb: add phylink support
From: Andrew Lunn @ 2019-06-17 17:42 UTC (permalink / raw)
To: Parshuram Thombare
Cc: nicolas.ferre, davem, f.fainelli, netdev, hkallweit1,
linux-kernel, rafalc, aniljoy, piotrs
In-Reply-To: <1560642367-26425-1-git-send-email-pthombar@cadence.com>
> @@ -4217,8 +4257,8 @@ static int macb_probe(struct platform_device *pdev)
>
> tasklet_init(&bp->hresp_err_tasklet, macb_hresp_error_task,
> (unsigned long)bp);
> -
> - phy_attached_info(phydev);
> + if (dev->phydev)
> + phy_attached_info(dev->phydev);
When can this happen? I don't see anything assigning to dev->phydev.
Andrew
^ permalink raw reply
* Re: [PATCH V2] net: phy: tja11xx: Add IRQ support to the driver
From: Marek Vasut @ 2019-06-17 17:43 UTC (permalink / raw)
To: Andrew Lunn
Cc: Heiner Kallweit, netdev, Florian Fainelli, Guenter Roeck,
Jean Delvare, linux-hwmon
In-Reply-To: <20190617171613.GJ17551@lunn.ch>
On 6/17/19 7:16 PM, Andrew Lunn wrote:
> On Thu, Jun 13, 2019 at 05:42:53PM +0200, Marek Vasut wrote:
>> On 5/30/19 1:46 AM, Marek Vasut wrote:
>>> On 5/30/19 1:29 AM, Andrew Lunn wrote:
>>>> On Tue, May 28, 2019 at 11:33:33PM +0200, Marek Vasut wrote:
>>>>> On 5/28/19 11:22 PM, Andrew Lunn wrote:
>>>>>>> The link detection on the TJA1100 (not TJA1101) seems unstable at best,
>>>>>>> so I better use all the interrupt sources to nudge the PHY subsystem and
>>>>>>> have it check the link change.
>>>>>>
>>>>>> Then it sounds like you should just ignore interrupts and stay will
>>>>>> polling for the TJA1100.
>>>>>
>>>>> Polling for the link status change is slow(er) than the IRQ driven
>>>>> operation, so I would much rather use the interrupts.
>>>>
>>>> I agree about the speed, but it seems like interrupts on this PHY are
>>>> not so reliable. Polling always works. But unfortunately, you cannot
>>>> have both interrupts and polling to fix up problems when interrupts
>>>> fail. Your call, do you think interrupts really do work?
>>>
>>> It works fine for me this way. And mind you, it's only the TJA1100
>>> that's flaky, the TJA1101 is better.
>>>
>>>> If you say that tja1101 works as expected, then please just use the
>>>> link up/down bits for it.
>>>
>>> I still don't know which bits really trigger link status changes, so I'd
>>> like to play it safe and just trigger on all of them.
>>
>> So what do we do here ?
>
> Hi Marek
>
> My personal preference would be to just enable what is needed. But
> I won't block a patch which enables everything.
Thanks. I don't know exactly what is needed , but I know that if I
enable everything, it works fine. And I'm not getting an interrupt storm
either, so it's probably OKish.
--
Best regards,
Marek Vasut
^ permalink raw reply
* Re: [PATCH bpf-next 2/8] libbpf: extract BTF loading and simplify ELF parsing logic
From: Andrii Nakryiko @ 2019-06-17 17:46 UTC (permalink / raw)
To: Song Liu
Cc: Andrii Nakryiko, bpf, Networking, Alexei Starovoitov,
Daniel Borkmann, Kernel Team
In-Reply-To: <CAPhsuW5AhXxiRj9x-EWLWy-3Akzh=2y83XxuG2Ma269FjkprrA@mail.gmail.com>
On Sat, Jun 15, 2019 at 1:28 PM Song Liu <liu.song.a23@gmail.com> wrote:
>
> On Sat, Jun 15, 2019 at 1:25 PM Song Liu <liu.song.a23@gmail.com> wrote:
> >
> > On Mon, Jun 10, 2019 at 9:49 PM Andrii Nakryiko <andriin@fb.com> wrote:
> > >
> > > As a preparation for adding BTF-based BPF map loading, extract .BTF and
> > > .BTF.ext loading logic. Also simplify error handling in
> > > bpf_object__elf_collect() by returning early, as there is no common
> > > clean up to be done.
>
> Maybe separate bpf_object__load_btf() and other cleanup in two patches?
> (I won't complain if you keep them together)
Split into two.
>
> Thanks,
> Song
>
> > >
> > > Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> > > ---
> > > tools/lib/bpf/libbpf.c | 137 ++++++++++++++++++++++-------------------
> > > 1 file changed, 75 insertions(+), 62 deletions(-)
<snip>
^ permalink raw reply
* Re: [PATCH] uapi: avoid namespace conflict in linux/posix_types.h
From: Linus Torvalds @ 2019-06-17 17:49 UTC (permalink / raw)
To: Florian Weimer
Cc: Joseph Myers, Arnd Bergmann, Linux API, linux-arch, Netdev,
Laura Abbott, Paul Burton, Deepa Dinamani,
Linux List Kernel Mailing
In-Reply-To: <87sgs8igfj.fsf@oldenburg2.str.redhat.com>
On Mon, Jun 17, 2019 at 4:45 AM Florian Weimer <fweimer@redhat.com> wrote:
>
> I wanted to introduce a new header, <asm/kernel_long_t.h>, and include
> it where the definition of __kernel_long_t is needed, something like
> this (incomplete, untested):
So this doesn't look interesting to me: __kernel_long_t is neither
interesting as a type anyway (it's just a way for user space to
override "long"), nor is it a namespace violation.
So honestly, user space could do whatever it wants for __kernel_long_t anyway.
The thing that I think we should try to fix is just the "val[]" thing, ie
> A different approach would rename <asm/posix_types.h> to something more
> basic, exclude the two structs, and move all internal #includes which do
> need the structs to the new header.
In fact, I wouldn't even rename <posix_types.h> at all, I'd just make
sure it's namespace-clean.
I _think_ the only thing causing problems is '__kernel_fsid_t' due to
that "val[]" thing, so just remove ity entirely, and add it to
<statfs.h> instead.
And yeah, then we'd need to maybe make sure that the (couple) of
__kernel_fsid_t users properly include that statfs.h file.
Hmm?
Linus
^ permalink raw reply
* [PATCH v5 0/5] brcmfmac: sdio: Deal better w/ transmission errors related to idle
From: Douglas Anderson @ 2019-06-17 17:56 UTC (permalink / raw)
To: Ulf Hansson, Kalle Valo, Adrian Hunter, Arend van Spriel
Cc: brcm80211-dev-list.pdl, linux-rockchip, Double Lo, briannorris,
linux-wireless, Naveen Gupta, Madhan Mohan R, mka, Wright Feng,
Chi-Hsien Lin, netdev, brcm80211-dev-list, Douglas Anderson,
YueHaibing, Allison Randal, Thomas Gleixner, Hante Meuleman,
Greg Kroah-Hartman, Niklas Söderlund, Ritesh Harjani,
Michael Trimarchi, Wolfram Sang, Franky Lin, Ondrej Jirman,
Jiong Wu, David S. Miller, linux-mmc, linux-kernel, Avri Altman
This series attempts to deal better with the expected transmission
errors related to the idle states (handled by the Always-On-Subsystem
or AOS) on the SDIO-based WiFi on rk3288-veyron-minnie,
rk3288-veyron-speedy, and rk3288-veyron-mickey.
Some details about those errors can be found in
<https://crbug.com/960222>, but to summarize it here: if we try to
send the wakeup command to the WiFi card at the same time it has
decided to wake up itself then it will behave badly on the SDIO bus.
This can cause timeouts or CRC errors.
When I tested on 4.19 and 4.20 these CRC errors can be seen to cause
re-tuning. Since I am currently developing on 4.19 this was the
original problem I attempted to solve.
On mainline it turns out that you don't see the retuning errors but
you see tons of spam about timeouts trying to wakeup from sleep. I
tracked down the commit that was causing that and have partially
reverted it here. I have no real knowledge about Broadcom WiFi, but
the commit that was causing problems sounds (from the descriptioin) to
be a hack commit penalizing all Broadcom WiFi users because of a bug
in a Cypress SD controller. I will let others comment if this is
truly the case and, if so, what the right solution should be.
For v3 of this series I have added 2 patches to the end of the series
to address errors that would show up on systems with these same SDIO
WiFi cards when used on controllers that do periodic retuning. These
systems need an extra fix to prevent the retuning from happening when
the card is asleep.
I believe v5 of this series is all ready to go assuming Kalle Valo is
good with it. I've added after-the-cut notes to patches awaiting his
Ack and have added other tags collected so far.
Changes in v5:
- Add missing sdio_retune_crc_enable() in comments (Ulf).
- /s/reneable/re-enable (Ulf).
- Remove leftover prototypes: mmc_expect_errors_begin() / end() (Ulf).
- Rewording of "sleep command" in commit message (Arend).
Changes in v4:
- Moved to SDIO API only (Adrian, Ulf).
- Renamed to make it less generic, now retune_crc_disable (Ulf).
- Function header makes it clear host must be claimed (Ulf).
- No more WARN_ON (Ulf).
- Adjust to API rename (Adrian, Ulf).
- Moved retune hold/release to SDIO API (Adrian).
- Adjust to API rename (Adrian).
Changes in v3:
- Took out the spinlock since I believe this is all in one context.
- Expect errors for all of brcmf_sdio_kso_control() (Adrian).
- ("mmc: core: Export mmc_retune_hold_now() mmc_retune_release()") new for v3.
- ("brcmfmac: sdio: Don't tune while the card is off") new for v3.
Changes in v2:
- A full revert, not just a partial one (Arend). ...with explicit Cc.
- Updated commit message to clarify based on discussion of v1.
Douglas Anderson (5):
Revert "brcmfmac: disable command decode in sdio_aos"
mmc: core: API to temporarily disable retuning for SDIO CRC errors
brcmfmac: sdio: Disable auto-tuning around commands expected to fail
mmc: core: Add sdio_retune_hold_now() and sdio_retune_release()
brcmfmac: sdio: Don't tune while the card is off
drivers/mmc/core/core.c | 5 +-
drivers/mmc/core/sdio_io.c | 77 +++++++++++++++++++
.../broadcom/brcm80211/brcmfmac/sdio.c | 17 ++--
include/linux/mmc/host.h | 1 +
include/linux/mmc/sdio_func.h | 6 ++
5 files changed, 99 insertions(+), 7 deletions(-)
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply
* [PATCH v5 1/5] Revert "brcmfmac: disable command decode in sdio_aos"
From: Douglas Anderson @ 2019-06-17 17:56 UTC (permalink / raw)
To: Ulf Hansson, Kalle Valo, Adrian Hunter, Arend van Spriel
Cc: brcm80211-dev-list.pdl, linux-rockchip, Double Lo, briannorris,
linux-wireless, Naveen Gupta, Madhan Mohan R, mka, Wright Feng,
Chi-Hsien Lin, netdev, brcm80211-dev-list, Douglas Anderson,
Franky Lin, linux-kernel, Hante Meuleman, YueHaibing,
David S. Miller
In-Reply-To: <20190617175653.21756-1-dianders@chromium.org>
This reverts commit 29f6589140a10ece8c1d73f58043ea5b3473ab3e.
After that patch landed I find that my kernel log on
rk3288-veyron-minnie and rk3288-veyron-speedy is filled with:
brcmfmac: brcmf_sdio_bus_sleep: error while changing bus sleep state -110
This seems to happen every time the Broadcom WiFi transitions out of
sleep mode. Reverting the commit fixes the problem for me, so that's
what this patch does.
Note that, in general, the justification in the original commit seemed
a little weak. It looked like someone was testing on a SD card
controller that would sometimes die if there were CRC errors on the
bus. This used to happen back in early days of dw_mmc (the controller
on my boards), but we fixed it. Disabling a feature on all boards
just because one SD card controller is broken seems bad.
Fixes: 29f6589140a1 ("brcmfmac: disable command decode in sdio_aos")
Cc: Wright Feng <wright.feng@cypress.com>
Cc: Double Lo <double.lo@cypress.com>
Cc: Madhan Mohan R <madhanmohan.r@cypress.com>
Cc: Chi-Hsien Lin <chi-hsien.lin@cypress.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---
This commit to land in the wireless tree. OK to land it separately
from the rest of the series.
Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2:
- A full revert, not just a partial one (Arend). ...with explicit Cc.
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 4e15ea57d4f5..4a750838d8cd 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -3364,11 +3364,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus,
static bool brcmf_sdio_aos_no_decode(struct brcmf_sdio *bus)
{
- if (bus->ci->chip == CY_CC_43012_CHIP_ID ||
- bus->ci->chip == CY_CC_4373_CHIP_ID ||
- bus->ci->chip == BRCM_CC_4339_CHIP_ID ||
- bus->ci->chip == BRCM_CC_4345_CHIP_ID ||
- bus->ci->chip == BRCM_CC_4354_CHIP_ID)
+ if (bus->ci->chip == CY_CC_43012_CHIP_ID)
return true;
else
return false;
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH v5 2/5] mmc: core: API to temporarily disable retuning for SDIO CRC errors
From: Douglas Anderson @ 2019-06-17 17:56 UTC (permalink / raw)
To: Ulf Hansson, Kalle Valo, Adrian Hunter, Arend van Spriel
Cc: brcm80211-dev-list.pdl, linux-rockchip, Double Lo, briannorris,
linux-wireless, Naveen Gupta, Madhan Mohan R, mka, Wright Feng,
Chi-Hsien Lin, netdev, brcm80211-dev-list, Douglas Anderson,
stable, Jiong Wu, Ritesh Harjani, Allison Randal, linux-mmc,
linux-kernel, Thomas Gleixner, Greg Kroah-Hartman, Wolfram Sang,
Avri Altman, Niklas Söderlund
In-Reply-To: <20190617175653.21756-1-dianders@chromium.org>
Normally when the MMC core sees an "-EILSEQ" error returned by a host
controller then it will trigger a retuning of the card. This is
generally a good idea.
However, if a command is expected to sometimes cause transfer errors
then these transfer errors shouldn't cause a re-tuning. This
re-tuning will be a needless waste of time. One example case where a
transfer is expected to cause errors is when transitioning between
idle (sometimes referred to as "sleep" in Broadcom code) and active
state on certain Broadcom WiFi SDIO cards. Specifically if the card
was already transitioning between states when the command was sent it
could cause an error on the SDIO bus.
Let's add an API that the SDIO function drivers can call that will
temporarily disable the auto-tuning functionality. Then we can add a
call to this in the Broadcom WiFi driver and any other driver that
might have similar needs.
NOTE: this makes the assumption that the card is already tuned well
enough that it's OK to disable the auto-retuning during one of these
error-prone situations. Presumably the driver code performing the
error-prone transfer knows how to recover / retry from errors. ...and
after we can get back to a state where transfers are no longer
error-prone then we can enable the auto-retuning again. If we truly
find ourselves in a case where the card needs to be retuned sometimes
to handle one of these error-prone transfers then we can always try a
few transfers first without auto-retuning and then re-try with
auto-retuning if the first few fail.
Without this change on rk3288-veyron-minnie I periodically see this in
the logs of a machine just sitting there idle:
dwmmc_rockchip ff0d0000.dwmmc: Successfully tuned phase to XYZ
Commit notes:
Patches #2 - #5 will go through Ulf's tree.
END
Fixes: bd11e8bd03ca ("mmc: core: Flag re-tuning is needed on CRC errors")
Cc: stable@vger.kernel.org
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
---
Changes in v5:
- Add missing sdio_retune_crc_enable() in comments (Ulf).
- /s/reneable/re-enable (Ulf).
- Remove leftover prototypes: mmc_expect_errors_begin() / end() (Ulf).
Changes in v4:
- Moved to SDIO API only (Adrian, Ulf).
- Renamed to make it less generic, now retune_crc_disable (Ulf).
- Function header makes it clear host must be claimed (Ulf).
- No more WARN_ON (Ulf).
Changes in v3:
- Took out the spinlock since I believe this is all in one context.
Changes in v2:
- Updated commit message to clarify based on discussion of v1.
drivers/mmc/core/core.c | 5 +++--
drivers/mmc/core/sdio_io.c | 37 +++++++++++++++++++++++++++++++++++
include/linux/mmc/host.h | 1 +
include/linux/mmc/sdio_func.h | 3 +++
4 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 6db36dc870b5..9020cb2490f7 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -144,8 +144,9 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
int err = cmd->error;
/* Flag re-tuning needed on CRC errors */
- if ((cmd->opcode != MMC_SEND_TUNING_BLOCK &&
- cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) &&
+ if (cmd->opcode != MMC_SEND_TUNING_BLOCK &&
+ cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200 &&
+ !host->retune_crc_disable &&
(err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) ||
(mrq->data && mrq->data->error == -EILSEQ) ||
(mrq->stop && mrq->stop->error == -EILSEQ)))
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index f79f0b0caab8..0acb1a29c968 100644
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -734,3 +734,40 @@ int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags)
return 0;
}
EXPORT_SYMBOL_GPL(sdio_set_host_pm_flags);
+
+/**
+ * sdio_retune_crc_disable - temporarily disable retuning on CRC errors
+ * @func: SDIO function attached to host
+ *
+ * If the SDIO card is known to be in a state where it might produce
+ * CRC errors on the bus in response to commands (like if we know it is
+ * transitioning between power states), an SDIO function driver can
+ * call this function to temporarily disable the SD/MMC core behavior of
+ * triggering an automatic retuning.
+ *
+ * This function should be called while the host is claimed and the host
+ * should remain claimed until sdio_retune_crc_enable() is called.
+ * Specifically, the expected sequence of calls is:
+ * - sdio_claim_host()
+ * - sdio_retune_crc_disable()
+ * - some number of calls like sdio_writeb() and sdio_readb()
+ * - sdio_retune_crc_enable()
+ * - sdio_release_host()
+ */
+void sdio_retune_crc_disable(struct sdio_func *func)
+{
+ func->card->host->retune_crc_disable = true;
+}
+EXPORT_SYMBOL_GPL(sdio_retune_crc_disable);
+
+/**
+ * sdio_retune_crc_enable - re-enable retuning on CRC errors
+ * @func: SDIO function attached to host
+ *
+ * This is the compement to sdio_retune_crc_disable().
+ */
+void sdio_retune_crc_enable(struct sdio_func *func)
+{
+ func->card->host->retune_crc_disable = false;
+}
+EXPORT_SYMBOL_GPL(sdio_retune_crc_enable);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 43d0f0c496f6..ecb7972e2423 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -398,6 +398,7 @@ struct mmc_host {
unsigned int retune_now:1; /* do re-tuning at next req */
unsigned int retune_paused:1; /* re-tuning is temporarily disabled */
unsigned int use_blk_mq:1; /* use blk-mq */
+ unsigned int retune_crc_disable:1; /* don't trigger retune upon crc */
int rescan_disable; /* disable card detection */
int rescan_entered; /* used with nonremovable devices */
diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
index e9dfdd501cd1..4820e6d09dac 100644
--- a/include/linux/mmc/sdio_func.h
+++ b/include/linux/mmc/sdio_func.h
@@ -167,4 +167,7 @@ extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b,
extern mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func);
extern int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags);
+extern void sdio_retune_crc_disable(struct sdio_func *func);
+extern void sdio_retune_crc_enable(struct sdio_func *func);
+
#endif /* LINUX_MMC_SDIO_FUNC_H */
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH v5 3/5] brcmfmac: sdio: Disable auto-tuning around commands expected to fail
From: Douglas Anderson @ 2019-06-17 17:56 UTC (permalink / raw)
To: Ulf Hansson, Kalle Valo, Adrian Hunter, Arend van Spriel
Cc: brcm80211-dev-list.pdl, linux-rockchip, Double Lo, briannorris,
linux-wireless, Naveen Gupta, Madhan Mohan R, mka, Wright Feng,
Chi-Hsien Lin, netdev, brcm80211-dev-list, Douglas Anderson,
stable, David S. Miller, Franky Lin, linux-kernel, Hante Meuleman,
YueHaibing, Michael Trimarchi
In-Reply-To: <20190617175653.21756-1-dianders@chromium.org>
There are certain cases, notably when transitioning between sleep and
active state, when Broadcom SDIO WiFi cards will produce errors on the
SDIO bus. This is evident from the source code where you can see that
we try commands in a loop until we either get success or we've tried
too many times. The comment in the code reinforces this by saying
"just one write attempt may fail"
Unfortunately these failures sometimes end up causing an "-EILSEQ"
back to the core which triggers a retuning of the SDIO card and that
blocks all traffic to the card until it's done.
Let's disable retuning around the commands we expect might fail.
Commit notes:
Patches #2 - #5 will go through Ulf's tree.
This patch is still lacking Kalle Valo's Ack, which should probably be
received before landing in Ulf's tree.
END
Fixes: bd11e8bd03ca ("mmc: core: Flag re-tuning is needed on CRC errors")
Cc: stable@vger.kernel.org
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Reviewed-by: Arend van Spriel <arend.vanspriel@broadcom.com>
---
Changes in v5: None
Changes in v4:
- Adjust to API rename (Adrian, Ulf).
Changes in v3:
- Expect errors for all of brcmf_sdio_kso_control() (Adrian).
Changes in v2: None
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 4a750838d8cd..ee76593259a7 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -667,6 +667,8 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
brcmf_dbg(TRACE, "Enter: on=%d\n", on);
+ sdio_retune_crc_disable(bus->sdiodev->func1);
+
wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
/* 1st KSO write goes to AOS wake up core if device is asleep */
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err);
@@ -727,6 +729,8 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
if (try_cnt > MAX_KSO_ATTEMPTS)
brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err);
+ sdio_retune_crc_enable(bus->sdiodev->func1);
+
return err;
}
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH v5 5/5] brcmfmac: sdio: Don't tune while the card is off
From: Douglas Anderson @ 2019-06-17 17:56 UTC (permalink / raw)
To: Ulf Hansson, Kalle Valo, Adrian Hunter, Arend van Spriel
Cc: brcm80211-dev-list.pdl, linux-rockchip, Double Lo, briannorris,
linux-wireless, Naveen Gupta, Madhan Mohan R, mka, Wright Feng,
Chi-Hsien Lin, netdev, brcm80211-dev-list, Douglas Anderson,
stable, Franky Lin, linux-kernel, Hante Meuleman, Ondrej Jirman,
YueHaibing, David S. Miller
In-Reply-To: <20190617175653.21756-1-dianders@chromium.org>
When Broadcom SDIO cards are idled they go to sleep and a whole
separate subsystem takes over their SDIO communication. This is the
Always-On-Subsystem (AOS) and it can't handle tuning requests.
Specifically, as tested on rk3288-veyron-minnie (which reports having
BCM4354/1 in dmesg), if I force a retune in brcmf_sdio_kso_control()
when "on = 1" (aka we're transition from sleep to wake) by whacking:
bus->sdiodev->func1->card->host->need_retune = 1
...then I can often see tuning fail. In this case dw_mmc reports "All
phases bad!"). Note that I don't get 100% failure, presumably because
sometimes the card itself has already transitioned away from the AOS
itself by the time we try to wake it up. If I force retuning when "on
= 0" (AKA force retuning right before sending the command to go to
sleep) then retuning is always OK.
NOTE: we need _both_ this patch and the patch to avoid triggering
tuning due to CRC errors in the sleep/wake transition, AKA ("brcmfmac:
sdio: Disable auto-tuning around commands expected to fail"). Though
both patches handle issues with Broadcom's AOS, the problems are
distinct:
1. We want to defer (but not ignore) asynchronous (like
timer-requested) tuning requests till the card is awake. However,
we want to ignore CRC errors during the transition, we don't want
to queue deferred tuning request.
2. You could imagine that the AOS could implement retuning but we
could still get errors while transitioning in and out of the AOS.
Similarly you could imagine a seamless transition into and out of
the AOS (with no CRC errors) even if the AOS couldn't handle
tuning.
ALSO NOTE: presumably there is never a desperate need to retune in
order to wake up the card, since doing so is impossible. Luckily the
only way the card can get into sleep state is if we had a good enough
tuning to send it the command to put it into sleep, so presumably that
"good enough" tuning is enough to wake us up, at least with a few
retries.
Cc: stable@vger.kernel.org
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Reviewed-by: Arend van Spriel <arend.vanspriel@broadcom.com>
---
Patches #2 - #5 will go through Ulf's tree.
This patch is still lacking Kalle Valo's Ack, which should probably be
received before landing in Ulf's tree.
I've CCed stable@ here without a version tag. As per Adrian Hunter
this patch applies cleanly to 4.18+ so that would be an easy first
target. However, if someone were so inclined they could provide
further backports. As per Adrian [1] the root problem has existed for
~4 years.
[1] https://lkml.kernel.org/r/4f39e152-04ba-a64e-985a-df93e6d15ff8@intel.com
Changes in v5:
- Rewording of "sleep command" in commit message (Arend).
Changes in v4:
- Adjust to API rename (Adrian).
Changes in v3:
- ("brcmfmac: sdio: Don't tune while the card is off") new for v3.
Changes in v2: None
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index ee76593259a7..629140b6d7e2 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -669,6 +669,10 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
sdio_retune_crc_disable(bus->sdiodev->func1);
+ /* Cannot re-tune if device is asleep; defer till we're awake */
+ if (on)
+ sdio_retune_hold_now(bus->sdiodev->func1);
+
wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
/* 1st KSO write goes to AOS wake up core if device is asleep */
brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err);
@@ -729,6 +733,9 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
if (try_cnt > MAX_KSO_ATTEMPTS)
brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err);
+ if (on)
+ sdio_retune_release(bus->sdiodev->func1);
+
sdio_retune_crc_enable(bus->sdiodev->func1);
return err;
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH v5 4/5] mmc: core: Add sdio_retune_hold_now() and sdio_retune_release()
From: Douglas Anderson @ 2019-06-17 17:56 UTC (permalink / raw)
To: Ulf Hansson, Kalle Valo, Adrian Hunter, Arend van Spriel
Cc: brcm80211-dev-list.pdl, linux-rockchip, Double Lo, briannorris,
linux-wireless, Naveen Gupta, Madhan Mohan R, mka, Wright Feng,
Chi-Hsien Lin, netdev, brcm80211-dev-list, Douglas Anderson,
stable, Allison Randal, linux-mmc, linux-kernel, Thomas Gleixner,
Greg Kroah-Hartman
In-Reply-To: <20190617175653.21756-1-dianders@chromium.org>
We want SDIO drivers to be able to temporarily stop retuning when the
driver knows that the SDIO card is not in a state where retuning will
work (maybe because the card is asleep). We'll move the relevant
functions to a place where drivers can call them.
Cc: stable@vger.kernel.org
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
---
Patches #2 - #5 will go through Ulf's tree.
I've CCed stable@ here without a version tag. As per Adrian Hunter
this patch applies cleanly to 4.18+ so that would be an easy first
target. However, if someone were so inclined they could provide
further backports. As per Adrian [1] the root problem has existed for
~4 years.
[1] https://lkml.kernel.org/r/4f39e152-04ba-a64e-985a-df93e6d15ff8@intel.com
Changes in v5: None
Changes in v4:
- Moved retune hold/release to SDIO API (Adrian).
Changes in v3:
- ("mmc: core: Export mmc_retune_hold_now() mmc_retune_release()") new for v3.
Changes in v2: None
drivers/mmc/core/sdio_io.c | 40 +++++++++++++++++++++++++++++++++++
include/linux/mmc/sdio_func.h | 3 +++
2 files changed, 43 insertions(+)
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index 0acb1a29c968..2ba00acf64e6 100644
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -15,6 +15,7 @@
#include "sdio_ops.h"
#include "core.h"
#include "card.h"
+#include "host.h"
/**
* sdio_claim_host - exclusively claim a bus for a certain SDIO function
@@ -771,3 +772,42 @@ void sdio_retune_crc_enable(struct sdio_func *func)
func->card->host->retune_crc_disable = false;
}
EXPORT_SYMBOL_GPL(sdio_retune_crc_enable);
+
+/**
+ * sdio_retune_hold_now - start deferring retuning requests till release
+ * @func: SDIO function attached to host
+ *
+ * This function can be called if it's currently a bad time to do
+ * a retune of the SDIO card. Retune requests made during this time
+ * will be held and we'll actually do the retune sometime after the
+ * release.
+ *
+ * This function could be useful if an SDIO card is in a power state
+ * where it can respond to a small subset of commands that doesn't
+ * include the retuning command. Care should be taken when using
+ * this function since (presumably) the retuning request we might be
+ * deferring was made for a good reason.
+ *
+ * This function should be called while the host is claimed.
+ */
+void sdio_retune_hold_now(struct sdio_func *func)
+{
+ mmc_retune_hold_now(func->card->host);
+}
+EXPORT_SYMBOL_GPL(sdio_retune_hold_now);
+
+/**
+ * sdio_retune_release - signal that it's OK to retune now
+ * @func: SDIO function attached to host
+ *
+ * This is the complement to sdio_retune_hold_now(). Calling this
+ * function won't make a retune happen right away but will allow
+ * them to be scheduled normally.
+ *
+ * This function should be called while the host is claimed.
+ */
+void sdio_retune_release(struct sdio_func *func)
+{
+ mmc_retune_release(func->card->host);
+}
+EXPORT_SYMBOL_GPL(sdio_retune_release);
diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
index 4820e6d09dac..5a177f7a83c3 100644
--- a/include/linux/mmc/sdio_func.h
+++ b/include/linux/mmc/sdio_func.h
@@ -170,4 +170,7 @@ extern int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags);
extern void sdio_retune_crc_disable(struct sdio_func *func);
extern void sdio_retune_crc_enable(struct sdio_func *func);
+extern void sdio_retune_hold_now(struct sdio_func *func);
+extern void sdio_retune_release(struct sdio_func *func);
+
#endif /* LINUX_MMC_SDIO_FUNC_H */
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH bpf-next v6 0/9] bpf: getsockopt and setsockopt hooks
From: Stanislav Fomichev @ 2019-06-17 18:01 UTC (permalink / raw)
To: netdev, bpf; +Cc: davem, ast, daniel, Stanislav Fomichev
This series implements two new per-cgroup hooks: getsockopt and
setsockopt along with a new sockopt program type. The idea is pretty
similar to recently introduced cgroup sysctl hooks, but
implementation is simpler (no need to convert to/from strings).
What this can be applied to:
* move business logic of what tos/priority/etc can be set by
containers (either pass or reject)
* handle existing options (or introduce new ones) differently by
propagating some information in cgroup/socket local storage
Compared to a simple syscall/{g,s}etsockopt tracepoint, those
hooks are context aware. Meaning, they can access underlying socket
and use cgroup and socket local storage.
v6:
* rework cgroup chaining; stop as soon as bpf program returns
0 or 2; see patch with the documentation for the details
Stanislav Fomichev (9):
bpf: implement getsockopt and setsockopt hooks
bpf: sync bpf.h to tools/
libbpf: support sockopt hooks
selftests/bpf: test sockopt section name
selftests/bpf: add sockopt test
selftests/bpf: add sockopt test that exercises sk helpers
selftests/bpf: add sockopt test that exercises BPF_F_ALLOW_MULTI
bpf: add sockopt documentation
bpftool: support cgroup sockopt
Documentation/bpf/index.rst | 1 +
Documentation/bpf/prog_cgroup_sockopt.rst | 72 ++
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 +
.../bpftool/Documentation/bpftool-cgroup.rst | 7 +-
.../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 +-
tools/include/uapi/linux/bpf.h | 14 +
tools/lib/bpf/libbpf.c | 5 +
tools/lib/bpf/libbpf_probes.c | 1 +
tools/testing/selftests/bpf/.gitignore | 3 +
tools/testing/selftests/bpf/Makefile | 6 +-
.../testing/selftests/bpf/progs/sockopt_sk.c | 82 ++
.../selftests/bpf/test_section_names.c | 10 +
tools/testing/selftests/bpf/test_sockopt.c | 773 ++++++++++++++++++
.../selftests/bpf/test_sockopt_multi.c | 264 ++++++
tools/testing/selftests/bpf/test_sockopt_sk.c | 185 +++++
29 files changed, 1857 insertions(+), 10 deletions(-)
create mode 100644 Documentation/bpf/prog_cgroup_sockopt.rst
create mode 100644 tools/testing/selftests/bpf/progs/sockopt_sk.c
create mode 100644 tools/testing/selftests/bpf/test_sockopt.c
create mode 100644 tools/testing/selftests/bpf/test_sockopt_multi.c
create mode 100644 tools/testing/selftests/bpf/test_sockopt_sk.c
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply
* [PATCH bpf-next v6 1/9] bpf: implement getsockopt and setsockopt hooks
From: Stanislav Fomichev @ 2019-06-17 18:01 UTC (permalink / raw)
To: netdev, bpf; +Cc: davem, ast, daniel, Stanislav Fomichev, Martin Lau
In-Reply-To: <20190617180109.34950-1-sdf@google.com>
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 <kafai@fb.com>
Signed-off-by: Stanislav Fomichev <sdf@google.com>
---
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 <linux/bpf.h>
#include <linux/bpf-cgroup.h>
#include <net/sock.h>
+#include <net/bpf_sk_storage.h>
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,
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH bpf-next v6 2/9] bpf: sync bpf.h to tools/
From: Stanislav Fomichev @ 2019-06-17 18:01 UTC (permalink / raw)
To: netdev, bpf; +Cc: davem, ast, daniel, Stanislav Fomichev, Martin Lau
In-Reply-To: <20190617180109.34950-1-sdf@google.com>
Export new prog type and hook points to the libbpf.
Cc: Martin Lau <kafai@fb.com>
Signed-off-by: Stanislav Fomichev <sdf@google.com>
---
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__ */
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH bpf-next v6 3/9] libbpf: support sockopt hooks
From: Stanislav Fomichev @ 2019-06-17 18:01 UTC (permalink / raw)
To: netdev, bpf; +Cc: davem, ast, daniel, Stanislav Fomichev, Martin Lau
In-Reply-To: <20190617180109.34950-1-sdf@google.com>
Make libbpf aware of new sockopt hooks so it can derive prog type
and hook point from the section names.
Cc: Martin Lau <kafai@fb.com>
Signed-off-by: Stanislav Fomichev <sdf@google.com>
---
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;
}
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH bpf-next v6 4/9] selftests/bpf: test sockopt section name
From: Stanislav Fomichev @ 2019-06-17 18:01 UTC (permalink / raw)
To: netdev, bpf; +Cc: davem, ast, daniel, Stanislav Fomichev, Martin Lau
In-Reply-To: <20190617180109.34950-1-sdf@google.com>
Add tests that make sure libbpf section detection works.
Cc: Martin Lau <kafai@fb.com>
Signed-off-by: Stanislav Fomichev <sdf@google.com>
---
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)
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH bpf-next v6 5/9] selftests/bpf: add sockopt test
From: Stanislav Fomichev @ 2019-06-17 18:01 UTC (permalink / raw)
To: netdev, bpf; +Cc: davem, ast, daniel, Stanislav Fomichev, Martin Lau
In-Reply-To: <20190617180109.34950-1-sdf@google.com>
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 <kafai@fb.com>
Signed-off-by: Stanislav Fomichev <sdf@google.com>
---
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 <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <linux/filter.h>
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+#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;
+}
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH bpf-next v6 6/9] selftests/bpf: add sockopt test that exercises sk helpers
From: Stanislav Fomichev @ 2019-06-17 18:01 UTC (permalink / raw)
To: netdev, bpf; +Cc: davem, ast, daniel, Stanislav Fomichev, Martin Lau
In-Reply-To: <20190617180109.34950-1-sdf@google.com>
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 <kafai@fb.com>
Signed-off-by: Stanislav Fomichev <sdf@google.com>
---
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 <netinet/in.h>
+#include <linux/bpf.h>
+#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 <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <linux/filter.h>
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+#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;
+}
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH bpf-next v6 7/9] selftests/bpf: add sockopt test that exercises BPF_F_ALLOW_MULTI
From: Stanislav Fomichev @ 2019-06-17 18:01 UTC (permalink / raw)
To: netdev, bpf; +Cc: davem, ast, daniel, Stanislav Fomichev, Martin Lau
In-Reply-To: <20190617180109.34950-1-sdf@google.com>
sockopt test that verifies chaining behavior when 0/2 is returned.
Cc: Martin Lau <kafai@fb.com>
Signed-off-by: Stanislav Fomichev <sdf@google.com>
---
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 <error.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <linux/filter.h>
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+#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;
+}
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH bpf-next v6 8/9] bpf: add sockopt documentation
From: Stanislav Fomichev @ 2019-06-17 18:01 UTC (permalink / raw)
To: netdev, bpf; +Cc: davem, ast, daniel, Stanislav Fomichev, Martin Lau
In-Reply-To: <20190617180109.34950-1-sdf@google.com>
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 <kafai@fb.com>
Signed-off-by: Stanislav Fomichev <sdf@google.com>
---
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.
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox