From patchwork Sat Dec 17 01:57:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aditi Ghag X-Patchwork-Id: 13075755 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BFD74C10F1B for ; Sat, 17 Dec 2022 01:58:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230014AbiLQB6T (ORCPT ); Fri, 16 Dec 2022 20:58:19 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35246 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230190AbiLQB5y (ORCPT ); Fri, 16 Dec 2022 20:57:54 -0500 Received: from mail-pj1-x102c.google.com (mail-pj1-x102c.google.com [IPv6:2607:f8b0:4864:20::102c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 41F6011158 for ; Fri, 16 Dec 2022 17:57:49 -0800 (PST) Received: by mail-pj1-x102c.google.com with SMTP id u5so4095844pjy.5 for ; Fri, 16 Dec 2022 17:57:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=isovalent-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=QaxWeWngZMSAhXbJqUy2yy8VTPB8ppKaEKcVqmP8WIo=; b=AEn4HXfDAjNH/2hE1bXmKNe9N9YPDZb98Y/p3O9rOgAly02Cp9eZQP8BE+HetakznQ S16sNPtLkEGJ4f4fvmQvuxoZNiSUECFGtgIp356ywXVTaH6QYdHwh24tPMDYwXMtqYxq /mTrxJ/ASr5ACLKIbG2CPRWre0Y01PeJX7dsW+TXt+UdcvLDSXhbcPHRatdMdWhRQjTC 9heAZ/Y+5tLn2NBlsLEIflxd0i4B/gsJThSh8kcZUbwkj/6Ph2eH34P9BRVfuDkpMHh6 uaUA5RazSkLXBEE0SkflrMS4OsuSQDtcRNm0fTuKClmkNrGxAbnNsN0GmsuBsf5/yuQG m6DA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=QaxWeWngZMSAhXbJqUy2yy8VTPB8ppKaEKcVqmP8WIo=; b=b+LfvI+3NOERXKDgnbOr3/HaN0c2vs+2zOvVg4Hp5trMWb85qCVZo0+J1hre9Vtpl2 L+miIpyENd3HnZaY6RbDcxJBpZqguiTyMnGtfpaPvgUbSBK+MPkg21rhRdcl+Ot4i8Dt ahqmcFb4Y7bUtk+9nRGH2otSf86F7C++cwlXS3+1kVm7q08ZvtEpmRWLViZ3L+3Tk6T0 IsWKChIlSaTe4ARi+z9gVlE+5rm46/rVFv0K7BvIcAZAqOXyQ0TTXPek5NoHwoZ7MRMr QePagYZVIVYNiv3Idhte+JeyfLIVovd0QkhydnQpVK4SomD/x09QzlhaM+x7Xt/5keuR 40Bg== X-Gm-Message-State: AFqh2kqBGhWD/YX6LrtqJu9S55kgVxPie7mUkxE6a8sj/ynmdKeNuC76 yA8C9TxS5Mln4yoacExmBLOgl/+fHPTS9JD9zJ3MFRlEe3U= X-Google-Smtp-Source: AMrXdXsWMqt1AMmXRECKK+2nZs1QhKvmTkn1bj4hKZQ9WEemfjPgk0ZRNaIVcbqC0lcwddkmUkzguQ== X-Received: by 2002:a05:6a21:1506:b0:9d:efbf:813c with SMTP id nq6-20020a056a21150600b0009defbf813cmr827451pzb.5.1671242268289; Fri, 16 Dec 2022 17:57:48 -0800 (PST) Received: from localhost.localdomain ([2604:1380:4611:8100::1]) by smtp.gmail.com with ESMTPSA id y10-20020a17090a1f4a00b001ef8ab65052sm1924994pjy.11.2022.12.16.17.57.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Dec 2022 17:57:47 -0800 (PST) From: Aditi Ghag To: bpf@vger.kernel.org Cc: kafai@fb.com, sdf@google.com, edumazet@google.com, Aditi Ghag Subject: [PATCH 1/2] bpf: Add socket destroy capability Date: Sat, 17 Dec 2022 01:57:17 +0000 Message-Id: X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net The socket destroy helper is used to forcefully terminate sockets from certain BPF contexts. We plan to use the capability in Cilium to force client sockets to reconnect when their remote load-balancing backends are deleted. The other use case is on-the-fly policy enforcement where existing socket connections prevented by policies need to be terminated. The helper is currently exposed to iterator type BPF programs where users can filter, and terminate a set of sockets. Sockets are destroyed asynchronously using the work queue infrastructure. This allows for current the locking semantics within socket destroy handlers, as BPF iterators invoking the helper acquire *sock* locks. This also allows the helper to be invoked from non-sleepable contexts. The other approach to skip acquiring locks by passing an argument to the `diag_destroy` handler didn't work out well for UDP, as the UDP abort function internally invokes another function that ends up acquiring *sock* lock. While there are sleepable BPF iterators, these are limited to only certain map types. Furthermore, it's limiting in the sense that it wouldn't allow us to extend the helper to other non-sleepable BPF programs. The work queue infrastructure processes work items from per-cpu structures. As the sock destroy work items are executed asynchronously, we need to ref count sockets before they are added to the work queue. The 'work_pending' check prevents duplicate ref counting of sockets in case users invoke the destroy helper for a socket multiple times. The `{READ,WRITE}_ONCE` macros ensure that the socket pointer stored in a work queue item isn't clobbered while the item is being processed. As BPF programs are non-preemptible, we can expect that once a socket is ref counted, no other socket can sneak in before the ref counted socket is added to the work queue for asynchronous destroy. Finally, users are expected to retry when the helper fails to queue a work item for a socket to be destroyed in case there is another destroy operation is in progress. Signed-off-by: Aditi Ghag --- include/linux/bpf.h | 1 + include/uapi/linux/bpf.h | 17 +++++++++ kernel/bpf/core.c | 1 + kernel/trace/bpf_trace.c | 2 + net/core/filter.c | 70 ++++++++++++++++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 17 +++++++++ 6 files changed, 108 insertions(+) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 3de24cfb7a3d..60eaa05dfab3 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -2676,6 +2676,7 @@ extern const struct bpf_func_proto bpf_get_retval_proto; extern const struct bpf_func_proto bpf_user_ringbuf_drain_proto; extern const struct bpf_func_proto bpf_cgrp_storage_get_proto; extern const struct bpf_func_proto bpf_cgrp_storage_delete_proto; +extern const struct bpf_func_proto bpf_sock_destroy_proto; const struct bpf_func_proto *tracing_prog_func_proto( enum bpf_func_id func_id, const struct bpf_prog *prog); diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 464ca3f01fe7..789ac7c59fdf 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -5484,6 +5484,22 @@ union bpf_attr { * 0 on success. * * **-ENOENT** if the bpf_local_storage cannot be found. + * + * int bpf_sock_destroy(struct sock *sk) + * Description + * Destroy the given socket with **ECONNABORTED** error code. + * + * *sk* must be a non-**NULL** pointer to a socket. + * + * Return + * The socket is destroyed asynchronosuly, so 0 return value may + * not suggest indicate that the socket was successfully destroyed. + * + * On error, may return **EPROTONOSUPPORT**, **EBUSY**, **EINVAL**. + * + * **-EPROTONOSUPPORT** if protocol specific destroy handler is not implemented. + * + * **-EBUSY** if another socket destroy operation is in progress. */ #define ___BPF_FUNC_MAPPER(FN, ctx...) \ FN(unspec, 0, ##ctx) \ @@ -5698,6 +5714,7 @@ union bpf_attr { FN(user_ringbuf_drain, 209, ##ctx) \ FN(cgrp_storage_get, 210, ##ctx) \ FN(cgrp_storage_delete, 211, ##ctx) \ + FN(sock_destroy, 212, ##ctx) \ /* */ /* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 7f98dec6e90f..c59bef9805e5 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -2651,6 +2651,7 @@ const struct bpf_func_proto bpf_snprintf_btf_proto __weak; const struct bpf_func_proto bpf_seq_printf_btf_proto __weak; const struct bpf_func_proto bpf_set_retval_proto __weak; const struct bpf_func_proto bpf_get_retval_proto __weak; +const struct bpf_func_proto bpf_sock_destroy_proto __weak; const struct bpf_func_proto * __weak bpf_get_trace_printk_proto(void) { diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 3bbd3f0c810c..016dbee6b5e4 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -1930,6 +1930,8 @@ tracing_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_get_socket_ptr_cookie_proto; case BPF_FUNC_xdp_get_buff_len: return &bpf_xdp_get_buff_len_trace_proto; + case BPF_FUNC_sock_destroy: + return &bpf_sock_destroy_proto; #endif case BPF_FUNC_seq_printf: return prog->expected_attach_type == BPF_TRACE_ITER ? diff --git a/net/core/filter.c b/net/core/filter.c index 929358677183..9753606ecc26 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -11569,6 +11569,8 @@ bpf_sk_base_func_proto(enum bpf_func_id func_id) break; case BPF_FUNC_ktime_get_coarse_ns: return &bpf_ktime_get_coarse_ns_proto; + case BPF_FUNC_sock_destroy: + return &bpf_sock_destroy_proto; default: return bpf_base_func_proto(func_id); } @@ -11578,3 +11580,71 @@ bpf_sk_base_func_proto(enum bpf_func_id func_id) return func; } + +struct sock_destroy_work { + struct sock *sk; + struct work_struct destroy; +}; + +static DEFINE_PER_CPU(struct sock_destroy_work, sock_destroy_workqueue); + +static void bpf_sock_destroy_fn(struct work_struct *work) +{ + struct sock_destroy_work *sd_work = container_of(work, + struct sock_destroy_work, destroy); + struct sock *sk = READ_ONCE(sd_work->sk); + + sk->sk_prot->diag_destroy(sk, ECONNABORTED); + sock_put(sk); +} + +static int __init bpf_sock_destroy_workqueue_init(void) +{ + int cpu; + struct sock_destroy_work *work; + + for_each_possible_cpu(cpu) { + work = per_cpu_ptr(&sock_destroy_workqueue, cpu); + INIT_WORK(&work->destroy, bpf_sock_destroy_fn); + } + + return 0; +} +subsys_initcall(bpf_sock_destroy_workqueue_init); + +BPF_CALL_1(bpf_sock_destroy, struct sock *, sk) +{ + struct sock_destroy_work *sd_work; + + if (!sk->sk_prot->diag_destroy) + return -EOPNOTSUPP; + + sd_work = this_cpu_ptr(&sock_destroy_workqueue); + /* This check prevents duplicate ref counting + * of sockets, in case the handler is invoked + * multiple times for the same socket. + */ + if (work_pending(&sd_work->destroy)) + return -EBUSY; + + /* Ref counting ensures that the socket + * isn't deleted from underneath us before + * the work queue item is processed. + */ + if (!refcount_inc_not_zero(&sk->sk_refcnt)) + return -EINVAL; + + WRITE_ONCE(sd_work->sk, sk); + if (!queue_work(system_wq, &sd_work->destroy)) { + sock_put(sk); + return -EBUSY; + } + + return 0; +} + +const struct bpf_func_proto bpf_sock_destroy_proto = { + .func = bpf_sock_destroy, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON, +}; diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 464ca3f01fe7..07154a4d92f9 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -5484,6 +5484,22 @@ union bpf_attr { * 0 on success. * * **-ENOENT** if the bpf_local_storage cannot be found. + * + * int bpf_sock_destroy(void *sk) + * Description + * Destroy the given socket with **ECONNABORTED** error code. + * + * *sk* must be a non-**NULL** pointer to a socket. + * + * Return + * The socket is destroyed asynchronosuly, so 0 return value may + * not indicate that the socket was successfully destroyed. + * + * On error, may return **EPROTONOSUPPORT**, **EBUSY**, **EINVAL**. + * + * **-EPROTONOSUPPORT** if protocol specific destroy handler is not implemented. + * + * **-EBUSY** if another socket destroy operation is in progress. */ #define ___BPF_FUNC_MAPPER(FN, ctx...) \ FN(unspec, 0, ##ctx) \ @@ -5698,6 +5714,7 @@ union bpf_attr { FN(user_ringbuf_drain, 209, ##ctx) \ FN(cgrp_storage_get, 210, ##ctx) \ FN(cgrp_storage_delete, 211, ##ctx) \ + FN(sock_destroy, 212, ##ctx) \ /* */ /* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't From patchwork Sat Dec 17 01:57:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aditi Ghag X-Patchwork-Id: 13075756 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3B7BDC4332F for ; Sat, 17 Dec 2022 01:58:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229469AbiLQB6T (ORCPT ); Fri, 16 Dec 2022 20:58:19 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34862 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230268AbiLQB5y (ORCPT ); Fri, 16 Dec 2022 20:57:54 -0500 Received: from mail-pl1-x62f.google.com (mail-pl1-x62f.google.com [IPv6:2607:f8b0:4864:20::62f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D69A023381 for ; Fri, 16 Dec 2022 17:57:51 -0800 (PST) Received: by mail-pl1-x62f.google.com with SMTP id x2so3943679plb.13 for ; Fri, 16 Dec 2022 17:57:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=isovalent-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=PyQ+FFmbgko7U/NYm4WQvgLlWIvQ5jaACCw9RDLQDlQ=; b=CHGR/znC3HqI6gfTLQ+U0hUhyIJ9Jg355DJyJnrfwHNK7pavp1DHjTba/dM12Hy0K6 3nP3mNUZ2tWyEke7TJDs81ksyYbkrYU92jDayUECPrCIXp7PSZt2Y2+M5xXp6Fe/vdNW 0HNd04EmXcbyOP6nlENkDB1QajRrURskwt9n2uq3hhEscpc4uevHA4zeWpDVDdpulxBA tVoK3avjw4aH9UFrYVtZIMBiNIJ2uU9vHbnbK2LhUkMCS2BNBiSi+IpEZp9WuxKdNtHm eJ1vpi9ljsJr3AG5N1esnGXTvp5ViAr0jqzSLbDlnesEMVvMjDBnUIfl5Wgnah/ALb7Y ENHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=PyQ+FFmbgko7U/NYm4WQvgLlWIvQ5jaACCw9RDLQDlQ=; b=pGVX7tOjixRswKvw+YCTidZtAx+h63Xvezum6/ha2BPJT8Ngl29Fn3tZmdBKmIcbdv W2Zzh3y7VwZBFHgO78Yw6DNDr5jkyO67VAHwS7ce7lDrjCuVP7gm6OrJ2eltB9YJZTNi WCwnA56u/HnNmYccpI/nuGCo5dYOGC5RlVTQ50X9c59KTDhc92CZZ6Z17OeUDqGOjx4H XYJeln/KcA2Km2nQ628Se0sjtueHda544YkRtyj878J3AFQq9qiiKBO+oBGIfz67oSaf Jcp8MWPld9NM/jiDjbh3goWqkTTuK1pnirgWsu56E16Ty5W9d9ELmdeBI/RErQgZAGry qBdQ== X-Gm-Message-State: ANoB5pkTNE04lRIRDGrpxfd3KC+baE4Y5u907ng5S/CChDXZ0rICRQD+ 1kbjpskIBzOyo/8KsQhpwVrQ1Gi3haczStw8xun5UfFDu8E= X-Google-Smtp-Source: AA0mqf5O4BKVtAtoj9oAJaN/1DBehWeMuoYeuRyI9IU8esWGp2+Kx/yugBMOSbM4UEo8QRIDaFj2ig== X-Received: by 2002:a17:90b:1181:b0:219:c87a:6926 with SMTP id gk1-20020a17090b118100b00219c87a6926mr34874686pjb.26.1671242271063; Fri, 16 Dec 2022 17:57:51 -0800 (PST) Received: from localhost.localdomain ([2604:1380:4611:8100::1]) by smtp.gmail.com with ESMTPSA id y10-20020a17090a1f4a00b001ef8ab65052sm1924994pjy.11.2022.12.16.17.57.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Dec 2022 17:57:50 -0800 (PST) From: Aditi Ghag To: bpf@vger.kernel.org Cc: kafai@fb.com, sdf@google.com, edumazet@google.com, Aditi Ghag Subject: [PATCH 2/2] selftests/bpf: Add tests for bpf_sock_destroy Date: Sat, 17 Dec 2022 01:57:18 +0000 Message-Id: <16c81434c64f1c2a5d10e06c7199cc4715e467a0.1671242108.git.aditi.ghag@isovalent.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net The test cases for TCP and UDP mirror the intended usages of the helper. As the helper destroys sockets asynchronously, the tests have sleep invocation before validating if the sockets were destroyed by sending data. Also, while all the protocol specific helpers set `ECONNABORTED` error code on the destroyed sockets, only the TCP test case has the validation check. UDP sockets have an overriding error code from the disconnect call during abort. Signed-off-by: Aditi Ghag --- .../selftests/bpf/prog_tests/sock_destroy.c | 131 ++++++++++++++++++ .../selftests/bpf/progs/sock_destroy_prog.c | 96 +++++++++++++ 2 files changed, 227 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/sock_destroy.c create mode 100644 tools/testing/selftests/bpf/progs/sock_destroy_prog.c diff --git a/tools/testing/selftests/bpf/prog_tests/sock_destroy.c b/tools/testing/selftests/bpf/prog_tests/sock_destroy.c new file mode 100644 index 000000000000..b920f4501809 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/sock_destroy.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +#include "sock_destroy_prog.skel.h" +#include "network_helpers.h" + +#define ECONNABORTED 103 + +static int duration; + +static void start_iter_sockets(struct bpf_program *prog) +{ + struct bpf_link *link; + char buf[16] = {}; + int iter_fd, len; + + link = bpf_program__attach_iter(prog, NULL); + if (!ASSERT_OK_PTR(link, "attach_iter")) + return; + + iter_fd = bpf_iter_create(bpf_link__fd(link)); + if (!ASSERT_GE(iter_fd, 0, "create_iter")) + goto free_link; + + while ((len = read(iter_fd, buf, sizeof(buf))) > 0) + ; + CHECK(len < 0, "read", "read failed: %s\n", strerror(errno)); + + close(iter_fd); + +free_link: + bpf_link__destroy(link); +} + +void test_tcp(struct sock_destroy_prog *skel) +{ + int serv = -1, clien = -1, n = 0; + + serv = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0); + if (CHECK(serv < 0, "start_server", "failed to start server\n")) + goto cleanup_serv; + + clien = connect_to_fd(serv, 0); + if (CHECK(clien < 0, "connect_to_fd", "errno %d\n", errno)) + goto cleanup_serv; + + serv = accept(serv, NULL, NULL); + if (CHECK(serv < 0, "accept", "errno %d\n", errno)) + goto cleanup; + + n = send(clien, "t", 1, 0); + if (CHECK(n < 0, "client_send", "client failed to send on socket\n")) + goto cleanup; + + start_iter_sockets(skel->progs.iter_tcp6); + + // Sockets are destroyed asynchronously. + usleep(1000); + n = send(clien, "t", 1, 0); + + if (CHECK(n > 0, "client_send", "succeeded on destroyed socket\n")) + goto cleanup; + CHECK(errno != ECONNABORTED, "client_send", "unexpected error code on destroyed socket\n"); + + +cleanup: + close(clien); +cleanup_serv: + close(serv); +} + + +void test_udp(struct sock_destroy_prog *skel) +{ + int serv = -1, clien = -1, n = 0; + + serv = start_server(AF_INET6, SOCK_DGRAM, NULL, 0, 0); + if (CHECK(serv < 0, "start_server", "failed to start server\n")) + goto cleanup_serv; + + clien = connect_to_fd(serv, 0); + if (CHECK(clien < 0, "connect_to_fd", "errno %d\n", errno)) + goto cleanup_serv; + + n = send(clien, "t", 1, 0); + if (CHECK(n < 0, "client_send", "client failed to send on socket\n")) + goto cleanup; + + start_iter_sockets(skel->progs.iter_udp6); + + // Sockets are destroyed asynchronously. + usleep(1000); + + n = send(clien, "t", 1, 0); + if (CHECK(n > 0, "client_send", "succeeded on destroyed socket\n")) + goto cleanup; + // UDP sockets have an overriding error code after they are disconnected. + + +cleanup: + close(clien); +cleanup_serv: + close(serv); +} + +void test_sock_destroy(void) +{ + int cgroup_fd = 0; + struct sock_destroy_prog *skel; + + skel = sock_destroy_prog__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + cgroup_fd = test__join_cgroup("/sock_destroy"); + if (CHECK(cgroup_fd < 0, "join_cgroup", "cgroup creation failed\n")) + goto close_cgroup_fd; + + skel->links.sock_connect = bpf_program__attach_cgroup( + skel->progs.sock_connect, cgroup_fd); + if (!ASSERT_OK_PTR(skel->links.sock_connect, "prog_attach")) + goto close_cgroup_fd; + + test_tcp(skel); + test_udp(skel); + + +close_cgroup_fd: + close(cgroup_fd); + sock_destroy_prog__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/sock_destroy_prog.c b/tools/testing/selftests/bpf/progs/sock_destroy_prog.c new file mode 100644 index 000000000000..ec566033f41f --- /dev/null +++ b/tools/testing/selftests/bpf/progs/sock_destroy_prog.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "vmlinux.h" + +#include + +#define AF_INET6 10 + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} tcp_conn_sockets SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} udp_conn_sockets SEC(".maps"); + +SEC("cgroup/connect6") +int sock_connect(struct bpf_sock_addr *ctx) +{ + int key = 0; + __u64 sock_cookie = 0; + __u32 keyc = 0; + + if (ctx->family != AF_INET6 || ctx->user_family != AF_INET6) + return 1; + + sock_cookie = bpf_get_socket_cookie(ctx); + if (ctx->protocol == IPPROTO_TCP) + bpf_map_update_elem(&tcp_conn_sockets, &key, &sock_cookie, 0); + else if (ctx->protocol == IPPROTO_UDP) + bpf_map_update_elem(&udp_conn_sockets, &keyc, &sock_cookie, 0); + else + return 1; + + return 1; +} + +SEC("iter/tcp") +int iter_tcp6(struct bpf_iter__tcp *ctx) +{ + struct sock_common *sk_common = ctx->sk_common; + struct seq_file *seq = ctx->meta->seq; + __u64 sock_cookie = 0; + __u64 *val; + int key = 0; + + if (!sk_common) + return 0; + + if (sk_common->skc_family != AF_INET6) + return 0; + + sock_cookie = bpf_get_socket_cookie(sk_common); + val = bpf_map_lookup_elem(&tcp_conn_sockets, &key); + + if (!val) + return 0; + + if (sock_cookie == *val) + bpf_sock_destroy(sk_common); + + return 0; +} + +SEC("iter/udp") +int iter_udp6(struct bpf_iter__udp *ctx) +{ + struct seq_file *seq = ctx->meta->seq; + struct udp_sock *udp_sk = ctx->udp_sk; + struct sock *sk = (struct sock *) udp_sk; + __u64 sock_cookie = 0; + int key = 0; + __u64 *val; + + if (!sk) + return 0; + + sock_cookie = bpf_get_socket_cookie(sk); + val = bpf_map_lookup_elem(&udp_conn_sockets, &key); + + if (!val) + return 0; + + if (sock_cookie == *val) + bpf_sock_destroy(sk); + + return 0; +} + +char _license[] SEC("license") = "GPL";