From nobody Mon Jun 8 11:01:52 2026 Received: from mail-wm1-f74.google.com (mail-wm1-f74.google.com [209.85.128.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DAF2D1DDA18 for ; Fri, 29 May 2026 13:29:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780061371; cv=none; b=dS/N8U91dwKIDcMfFjRHvlMy3dUAnojn2Ca4TBKQ9jF5BLDhc7Lmd4FHkaL/1PaGxxMhqo3MoQCqf/OcFLBgU/YfAkykx4pod86b/AtqBwdKlzreVESTZKpNUYEOYUyKzXKZ1tsDxmMekHG0LOB0G3rcEMuhFofFkLMquONSmtQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780061371; c=relaxed/simple; bh=jQS5w3juKxh/8rsWEVFsuUj2CrwmODGzA/KyT3vg3gc=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=IBQfEnEuFJu4IBwjnv5VDY+jTklBGrc3FdseCkNPiwGED9pYqH+d2jghwGZ/D/hjXJ2KbK0cnoZlcPqWO1KRvSQpCB98ZHQfR6Y5BIgjcsAHJXreKObOmFMTfE6jXdF1OItjJ9f6W5HDTTj/tsHvwg9CtLnTSYljzQobTeYvwpE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=FmFZl64G; arc=none smtp.client-ip=209.85.128.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="FmFZl64G" Received: by mail-wm1-f74.google.com with SMTP id 5b1f17b1804b1-4908af8d7f9so19155105e9.1 for ; Fri, 29 May 2026 06:29:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1780061368; x=1780666168; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=oPbFopuJb3xJlIvAFVS2VoE+RIwABLyaYMui6R01KkA=; b=FmFZl64Gc7i49EyjzIirjWG2i/yTFM7NMaC3UUKBIJJClZNNSvXkZHrB7YB+GYqKPz GcwkGCjmLzrvKO+CR2XiW5EI+iI6MSo8+/GQd7fuHmboxNliLYSN3cC9htXs7Jhf63HV gdftUykQf07FDQflQs6x7kqfIjYB2Aj60uOMKY8IFlknYvZjvVdPmF5H9jvfE3bG25o6 6IOMhcmXZLfYC95eNvG5XLX3qz9nRrXbmZIISdTM4UgYEGgQyO9h+vINT6QDvy15tK7g F98braQndian39SJu9D78orsWNvUGOEKPcGNXuTpakl+nMOHSdm2JeoXMeA9pjr++p7w bnNg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780061368; x=1780666168; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=oPbFopuJb3xJlIvAFVS2VoE+RIwABLyaYMui6R01KkA=; b=kZ8mNBqMfnxTQgxjM/5Y33s4ph95jyN35QSHxqfgpKtC9e5XRYUQDtFecXVY3J2QJk iug3h69FDZDGCXPiZRN0dH9Zal/+FoXhsbdiT5KID9rKZ3jhNnpf5aH+23ArKWve0LUE 5XxTyQWE/XHUbMCs08pzvmt1Dc6+K8jKyVgCRW3aX9XbcpykWJYYqyCZmUaUtRYecUc9 2wYD9GGb+UkchavTIgfeoQPCW1NEjnKRd8stX88l9S6oIDt4a6nL3V5e4zSzMyozfl8P isYEmFs2MJzClpiOQAhOCWSF7DBWZTtx3Irc6WGO+Kg7F3uUfZf9evG+WslVMuiAbvws 9Rqw== X-Forwarded-Encrypted: i=1; AFNElJ9omsyZ2AfNB+37UbVuWlAL8Q6JrKZpNRNb98cs5APQ2j5CTqESFE9/i4AJqtKpqBOs24Tme1PId/6iztY=@vger.kernel.org X-Gm-Message-State: AOJu0YxjsOC1QvCrO/KgNQiWEEjssmRza8FOusGo97qAmV9erjbGCLKC B5m7jV3bIQEoWjXF+iwbJ+dBtR7aFqKOWJZU2gsl4zb0yRSFNUveycjJMMcbU8gk2mE/Ji5bpw1 Sd36nuQg/rDgxORVl5g== X-Received: from wmla10.prod.google.com ([2002:a05:600d:238a:b0:48f:d328:60c]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:4589:b0:490:50e8:45c3 with SMTP id 5b1f17b1804b1-4909c028fccmr57511305e9.0.1780061365120; Fri, 29 May 2026 06:29:25 -0700 (PDT) Date: Fri, 29 May 2026 13:28:38 +0000 In-Reply-To: <20260529-binder-netlink-v6-0-92dd74b6a32e@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260529-binder-netlink-v6-0-92dd74b6a32e@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=969; i=aliceryhl@google.com; h=from:subject:message-id; bh=jQS5w3juKxh/8rsWEVFsuUj2CrwmODGzA/KyT3vg3gc=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBqGZSqiLJlV643r8T6gfx80O09094kuKPpWw9NA E1PXS3vroWJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCahmUqgAKCRAEWL7uWMY5 RhBwD/90l0syKd+gZ1Z71dZ0oWqW+27vPX2QWMsTCj7diDQn408Q4NI5cWOxyYmIy19+bbOz/by MuUe5K1VYbw/5ifmAf5o77bZyisqa6EwQwLm2cNGrV8ErTf2uPngM9LDB/yRVZybATAeuMLeodA GJeof0Vo0m+cKrj6UoXmBEjiY5NbMXKjOnwdtmTQRvMhin2569HK7Dic++xoUUwv/sE09rZADrS cbfcLEBsv0ACIh+R/Oksnyz8Jq+GUjcBBt+nlpUWmHuCuYbN3OSiY6MaWodtWN7DbAOU4IGxpFV kbzWtpb4hoZeKQ6eKlJoenHuwLA0TlX3sIYRAwOkMtcpmRl9fxEqMn4SZxt2wJFb980pEzpOppL 7mD9POOAt/wexriuoQ5UeVn0XxWx4zPxlWmRrF2Y+FD9MN1CDQ982dqfrKVTwLfFVAV99viGtEb CJCqlfUaImTMmYjMDKVLe2KAiDFLDPKX8EKLQNmbQcHV3+69yysD3SgJlQAp72k6LNvwGmm5t8C EsQ/pQajhXT/HGp57F8EHAz5RZIA414UOnFuQyZ/3d7nR3HchfBCp9JFspdNNuXr5W6wBHhrUg4 2eikaXLsYTjTN5pMuAowxS+OYjHmwQpLZYUwN+cVTq2eKUEqaPxKG919gMEXr2nkLVQSy2UuDRb A0CcsEogqS3u2MQ== X-Mailer: b4 0.14.3 Message-ID: <20260529-binder-netlink-v6-1-92dd74b6a32e@google.com> Subject: [PATCH v6 1/3] rust: net: add rust/kernel/net to NETWORKING [GENERAL] From: Alice Ryhl To: Carlos Llamas , Greg Kroah-Hartman , Andrew Lunn , Donald Hunter , Jakub Kicinski , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Matthew Maurer Cc: Miguel Ojeda , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , Christian Brauner , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, netdev@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable To ensure that networking code can be found in a single shared place, add it to the relevant NETWORKING entry. The net.rs file is moved into the net/ directory so that it's included under the MAINTAINERS entry without needing a separate rust/kernel/net.rs entry. Signed-off-by: Alice Ryhl Acked-by: Jakub Kicinski Reviewed-by: Carlos Llamas --- MAINTAINERS | 2 ++ rust/kernel/{net.rs =3D> net/mod.rs} | 0 2 files changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index a61a229670b3..08bbe8e0a74a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18612,6 +18612,8 @@ F: include/uapi/linux/sctp.h F: lib/net_utils.c F: lib/random32.c F: net/ +F: rust/helpers/net/ +F: rust/kernel/net/ F: samples/pktgen/ F: tools/net/ F: tools/testing/selftests/net/ diff --git a/rust/kernel/net.rs b/rust/kernel/net/mod.rs similarity index 100% rename from rust/kernel/net.rs rename to rust/kernel/net/mod.rs --=20 2.54.0.823.g6e5bcc1fc9-goog From nobody Mon Jun 8 11:01:52 2026 Received: from mail-lj1-f201.google.com (mail-lj1-f201.google.com [209.85.208.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 14FBA33AD9B for ; Fri, 29 May 2026 13:29:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780061375; cv=none; b=RvedSO8ywcZeJY15WEOOlfjMfJvob+V+uR0Yo6MlDuQWF9igNS6TJM5dLl7KXUbiEbQxds3WNtO1MaUc0ToHT0vLzGetI2aQqKRrkUeQqEpywwxtt4AhLxJ44ikwVfJ2+G5mSxJWqEhafU8r7kobl1EGcRbrb04fVCz2iY268AE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780061375; c=relaxed/simple; bh=yDGG5xF8wlnsiwtSGIlOABVRQs6MDqiT5FxwAWF9130=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=QECqy8tc7QkGGUNyicODe7TCNSH5UIc0bHqbEAzuShGNeb4VkjbFWtr1LK4ByQ8sbGKHpdToFsPAqbrjNnUboiOfuteLYOAiuW/K1vZYW9eOqdkKvjOmMjKFU55nFcR01BPotMVWhQJD7GFP/Y+V0dswRqpLjaPS/2EcQ+RTg4M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=eQ/3hhEX; arc=none smtp.client-ip=209.85.208.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="eQ/3hhEX" Received: by mail-lj1-f201.google.com with SMTP id 38308e7fff4ca-393d5b5ae63so51736301fa.3 for ; Fri, 29 May 2026 06:29:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1780061371; x=1780666171; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=2FTeY3NhMYKOPR9wQenEEZNDfB3zMFvPRif+fMRL5ak=; b=eQ/3hhEXss8h/nmIlgxuKiGZ5xrf7pxJ22mDgJ3T3r/Rw2sCj0kORvBOUGoCe+T2mk 3XAzAPoXF0r0vEBQLq/HijiVwhbB1Rwu9+fP28Fdcl1HBF+i/MHZjtRlezOis3NrArKn plGTS1t65xIo+ISu1GYP2sMvdAgxPQbP3viXEwU9hINzrhmrdCqgVu8Hevo172anzQKq rhAxi+0TWlZmBMXSa4T96cZw3hs6pygOcQ6eqJ9pYM3tLVr1Xfls9wMjKLYj8jfC1wWe XB25Ape8qPvBHo6O+b4wPxMHjnmudMPVkVTyisNInxnzz46EdQOXH/laJLMbGOLdAiSt WxOA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780061371; x=1780666171; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=2FTeY3NhMYKOPR9wQenEEZNDfB3zMFvPRif+fMRL5ak=; b=RLkwJZ3lqzIE+3xl14QFJui8CIonCCkQhEvrJxylJNPlu4wN/Bn+K3A0kAPDwWLKrc zuMygZhFE6k5q+y+HJGnIUIREUq86vxJ5Krga7sBesgKfDADajujJoSPuYMpqCNuwU1I cPfFwknpMs/lkmrwRsWnH/SsFuXvDjr7cV7Cz8TmcH7l4bmEN5n3H4SEoPsaCRo0j3eZ w+seRCUXTxCwzuUjPD2naaS+TBbY1464nFL+N4Rd8azdHtzeU6uTN/KfAp7UzHC7IF9M 0tvev79Bs/KgVdyZFKzxtexJlUwsX66ohcYRoO2IsQeAFpFdoUTV+15toEkkaBDmSPP5 IsNw== X-Forwarded-Encrypted: i=1; AFNElJ8ObUvOhN3UoQFhGUP+wwq6gRCpj2ifS27GkdcCYEsPEMIvwaKFk6PyEbX9HrDYgcPrDt/nuuda6jsd45s=@vger.kernel.org X-Gm-Message-State: AOJu0YxzGo8LiR0OO4CH4OuKuxchZd12I9dHf/GTFB5pM5W93nC7Up29 XPnuhqXCFi6nmXH5usunhbEV2tLgeIoL7ci8rITBxCp/CbMIZD5UuremqJRGbS/VV62yKBM2Vnr FEwHjtD6qnBtatGKztg== X-Received: from ljju12.prod.google.com ([2002:a2e:854c:0:b0:38a:53e5:ea1c]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:651c:2359:10b0:394:1c6:6da4 with SMTP id 38308e7fff4ca-396536baf8cmr6619311fa.11.1780061371223; Fri, 29 May 2026 06:29:31 -0700 (PDT) Date: Fri, 29 May 2026 13:28:39 +0000 In-Reply-To: <20260529-binder-netlink-v6-0-92dd74b6a32e@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260529-binder-netlink-v6-0-92dd74b6a32e@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=15797; i=aliceryhl@google.com; h=from:subject:message-id; bh=yDGG5xF8wlnsiwtSGIlOABVRQs6MDqiT5FxwAWF9130=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBqGZSqnxDmJzUkiImZgSzexKmQ9Do5mXyXg/qWT PkgpxyXnFOJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCahmUqgAKCRAEWL7uWMY5 Rss5EACXuuhN9w22SdmdjXkp3NuHAqBW3iYJpwOWnxtlS1DmJfJHZAoQyUSIeWBcExPDXuDD352 WPJMVRN14NRFlqD4V3f9210HRVxIf/SAv1BvEwBHYgwEpWZkgv3MPoWDVLz/zBW5WzorinClZQC JhvY6MqpIz/IUq1DEDccpFz4Et9UYzaLJZZZ26fTFdcXwtcRk2S/zCXEyWEhpspCNMZ8itOee5t v9xelWJ4zpQ/ncolcDeKsnEE0PEy8bVuQRcYiE+Nb/yFw7m/VWXYQUYJYAp4+9dcQ0kddpHLgpN nqxvz/T9Mp22hZ2nIhc6i+FpHPW6/r3IRwvW/0IqhCT+RqYnbgNJXuPb24QWCXuD4N4g8d1JmI/ y/AZjjAN4nwD5j+gcNcYEGdvBbN0bv9/N3hExY+a14Jnfn7vJ/xWcpFyHLCo50ZbhnGigMmhLEK b8XnND5zp0zaXyqkPKZyp1v314mtSGLV38OiruhM8TTiIHTxRbGze0oCTCdPSQAb6m18SGSguZy DCZK5vWT4wV4qd7q2gZz3KLJC3wn0F/JZzRQaI9SLHhsE8STq5/YGTR0ZQnhxzzSMDBpU1pqvhK H5vrYBN7GtKI1CIHDt84FN+6mcf9jTZW1pWmVpVu31SMbzn9puyqgsLalV0JJD1xn8O679vtc2F lRSHci7YovhXBjQ== X-Mailer: b4 0.14.3 Message-ID: <20260529-binder-netlink-v6-2-92dd74b6a32e@google.com> Subject: [PATCH v6 2/3] rust: netlink: add raw netlink abstraction From: Alice Ryhl To: Carlos Llamas , Greg Kroah-Hartman , Andrew Lunn , Donald Hunter , Jakub Kicinski , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Matthew Maurer Cc: Miguel Ojeda , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , Christian Brauner , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, netdev@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable This implements a safe and relatively simple API over the netlink API, that allows you to add different attributes to a netlink message and broadcast it. As the first user of this API only makes use of broadcast, only broadcast messages are supported here. This API is intended to be safe and to be easy to use in *generated* code. This is because netlink is generally used with yaml files that describe the underlying API, and the python generator outputs C code (or, soon, Rust code) that lets you use the API more easily. So for example, if there is a string field, the code generator will output a method that internall calls `put_string()` with the right attr type. Reviewed-by: Matthew Maurer Reviewed-by: Andrew Lunn Signed-off-by: Alice Ryhl Acked-by: Jakub Kicinski Reviewed-by: Carlos Llamas --- rust/bindings/bindings_helper.h | 3 + rust/helpers/helpers.c | 1 + rust/helpers/net/genetlink.c | 46 ++++++ rust/kernel/net/mod.rs | 2 + rust/kernel/net/netlink.rs | 337 ++++++++++++++++++++++++++++++++++++= ++++ 5 files changed, 389 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 446dbeaf0866..612fa5388b7d 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -92,6 +92,8 @@ #include #include #include +#include +#include #include =20 /* @@ -109,6 +111,7 @@ const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN =3D ARCH_SLAB_MINALIGN; const size_t RUST_CONST_HELPER_ARCH_KMALLOC_MINALIGN =3D ARCH_KMALLOC_MINA= LIGN; const size_t RUST_CONST_HELPER_PAGE_SIZE =3D PAGE_SIZE; +const size_t RUST_CONST_HELPER_GENLMSG_DEFAULT_SIZE =3D GENLMSG_DEFAULT_SI= ZE; const gfp_t RUST_CONST_HELPER_GFP_ATOMIC =3D GFP_ATOMIC; const gfp_t RUST_CONST_HELPER_GFP_KERNEL =3D GFP_KERNEL; const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT =3D GFP_KERNEL_ACCOUNT; diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 625921e27dfb..d17eaec76450 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -70,6 +70,7 @@ #include "maple_tree.c" #include "mm.c" #include "mutex.c" +#include "net/genetlink.c" #include "of.c" #include "page.c" #include "pci.c" diff --git a/rust/helpers/net/genetlink.c b/rust/helpers/net/genetlink.c new file mode 100644 index 000000000000..3530b69f6cf7 --- /dev/null +++ b/rust/helpers/net/genetlink.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2026 Google LLC. + */ + +#include + +#ifdef CONFIG_NET + +__rust_helper struct sk_buff *rust_helper_genlmsg_new(size_t payload, gfp_= t flags) +{ + return genlmsg_new(payload, flags); +} + +__rust_helper +int rust_helper_genlmsg_multicast(const struct genl_family *family, + struct sk_buff *skb, u32 portid, + unsigned int group, gfp_t flags) +{ + return genlmsg_multicast(family, skb, portid, group, flags); +} + +__rust_helper void rust_helper_genlmsg_cancel(struct sk_buff *skb, void *h= dr) +{ + genlmsg_cancel(skb, hdr); +} + +__rust_helper void rust_helper_genlmsg_end(struct sk_buff *skb, void *hdr) +{ + genlmsg_end(skb, hdr); +} + +__rust_helper void rust_helper_nlmsg_free(struct sk_buff *skb) +{ + nlmsg_free(skb); +} + +__rust_helper +int rust_helper_genl_has_listeners(const struct genl_family *family, + struct net *net, unsigned int group) +{ + return genl_has_listeners(family, net, group); +} + +#endif diff --git a/rust/kernel/net/mod.rs b/rust/kernel/net/mod.rs index fe415cb369d3..8ecae7577ed2 100644 --- a/rust/kernel/net/mod.rs +++ b/rust/kernel/net/mod.rs @@ -4,3 +4,5 @@ =20 #[cfg(CONFIG_RUST_PHYLIB_ABSTRACTIONS)] pub mod phy; + +pub mod netlink; diff --git a/rust/kernel/net/netlink.rs b/rust/kernel/net/netlink.rs new file mode 100644 index 000000000000..22ef3dde36fa --- /dev/null +++ b/rust/kernel/net/netlink.rs @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2026 Google LLC. + +//! Rust support for generic netlink. +//! +//! Currently only supports exposing multicast groups. +//! +//! C header: [`include/net/genetlink.h`](srctree/include/net/genetlink.h) + +use kernel::{ + alloc::{self, AllocError}, + error::to_result, + prelude::*, + transmute::AsBytes, + types::Opaque, + ThisModule, +}; + +use core::{ + mem::ManuallyDrop, + ptr::NonNull, // +}; + +/// The default netlink message size. +pub const GENLMSG_DEFAULT_SIZE: usize =3D bindings::GENLMSG_DEFAULT_SIZE; + +/// A wrapper around `struct sk_buff` for generic netlink messages. +/// +/// This type is intended to be specific for buffers used with netlink onl= y, and other usecases for +/// `struct sk_buff` are out-of-scope for this abstraction. +/// +/// # Invariants +/// +/// The pointer has ownership over a valid `sk_buff`. +pub struct NetlinkSkBuff { + skb: NonNull, +} + +impl NetlinkSkBuff { + /// Creates a new `NetlinkSkBuff` with the given size. + pub fn new(size: usize, flags: alloc::Flags) -> Result { + // SAFETY: `genlmsg_new` only requires its arguments to be valid i= ntegers. + let skb =3D unsafe { bindings::genlmsg_new(size, flags.as_raw()) }; + let skb =3D NonNull::new(skb).ok_or(AllocError)?; + Ok(NetlinkSkBuff { skb }) + } + + /// Puts a generic netlink header into the `NetlinkSkBuff`. + pub fn genlmsg_put( + self, + portid: u32, + seq: u32, + family: &'static Family, + cmd: u8, + ) -> Result { + let skb =3D self.skb.as_ptr(); + // SAFETY: The skb and family pointers are valid. + let hdr =3D unsafe { bindings::genlmsg_put(skb, portid, seq, famil= y.as_raw(), 0, cmd) }; + let hdr =3D NonNull::new(hdr).ok_or(AllocError)?; + Ok(GenlMsg { skb: self, hdr }) + } +} + +impl Drop for NetlinkSkBuff { + fn drop(&mut self) { + // SAFETY: We have ownership over the `sk_buff`, so we may free it. + unsafe { bindings::nlmsg_free(self.skb.as_ptr()) } + } +} + +/// A generic netlink message being constructed. +/// +/// # Invariants +/// +/// `hdr` references the header in this netlink message. +pub struct GenlMsg { + skb: NetlinkSkBuff, + hdr: NonNull, +} + +impl GenlMsg { + /// Puts an attribute into the message. + #[inline] + fn put(&mut self, attrtype: c_int, value: &T) -> Result + where + T: ?Sized + AsBytes, + { + let skb =3D self.skb.skb.as_ptr(); + let len =3D size_of_val(value); + let ptr =3D core::ptr::from_ref(value).cast::(); + // SAFETY: `skb` is valid by `NetlinkSkBuff` type invariants, and = the provided value is + // readable and initialized for its `size_of` bytes. + to_result(unsafe { bindings::nla_put(skb, attrtype, len as c_int, = ptr) }) + } + + /// Puts a `u32` attribute into the message. + #[inline] + pub fn put_u32(&mut self, attrtype: c_int, value: u32) -> Result { + self.put(attrtype, &value) + } + + /// Puts a string attribute into the message. + #[inline] + pub fn put_string(&mut self, attrtype: c_int, value: &CStr) -> Result { + self.put(attrtype, value.to_bytes_with_nul()) + } + + /// Puts a flag attribute into the message. + #[inline] + pub fn put_flag(&mut self, attrtype: c_int) -> Result { + let skb =3D self.skb.skb.as_ptr(); + // SAFETY: `skb` is valid by `NetlinkSkBuff` type invariants, and = a null pointer is valid + // when the length is zero. + to_result(unsafe { bindings::nla_put(skb, attrtype, 0, core::ptr::= null()) }) + } + + /// Sends the generic netlink message as a multicast message. + #[inline] + pub fn multicast( + self, + family: &'static Family, + portid: u32, + group: u32, + flags: alloc::Flags, + ) -> Result { + let me =3D ManuallyDrop::new(self); + // SAFETY: The `skb` and `family` pointers are valid. We pass owne= rship of the `skb` to + // `genlmsg_multicast` by not dropping `self`. + unsafe { + bindings::genlmsg_end(me.skb.skb.as_ptr(), me.hdr.as_ptr()); + to_result(bindings::genlmsg_multicast( + family.as_raw(), + me.skb.skb.as_ptr(), + portid, + group, + flags.as_raw(), + )) + } + } +} +impl Drop for GenlMsg { + fn drop(&mut self) { + // SAFETY: The `hdr` pointer references the header of this generic= netlink message. + unsafe { bindings::genlmsg_cancel(self.skb.skb.as_ptr(), self.hdr.= as_ptr()) }; + } +} + +/// Flags for a generic netlink family. +struct FamilyFlags { + /// Whether the family supports network namespaces. + netnsok: bool, + /// Whether the family supports parallel operations. + parallel_ops: bool, +} + +impl FamilyFlags { + /// Converts the flags to the bitfield representation used by `genl_fa= mily`. + const fn into_bitfield(self) -> bindings::__BindgenBitfieldUnit<[u8; 1= ]> { + // The below shifts are verified correct by test_family_flags_bitf= ield() below. + // + // Although bindgen generates helpers to change bitfields based on= the C headers, these + // helpers unfortunately can't be used in const context. Since `Fa= mily` needs to be filled + // out at build-time, we use this helper instead. + let mut bits =3D 0; + if self.netnsok { + bits |=3D 1 << 0; + } + if self.parallel_ops { + bits |=3D 1 << 1; + } + // Convert from little endian to the target's endianness. + bits =3D u8::from_le(bits); + // SAFETY: This bitfield is represented as an u8. + unsafe { core::mem::transmute::>(bits) } + } +} + +/// A generic netlink family. +#[repr(transparent)] +pub struct Family { + inner: Opaque, +} + +// SAFETY: The `Family` type is thread safe. +unsafe impl Sync for Family {} + +impl Family { + /// Creates a new `Family` instance. + /// + /// Intended to be used from const context only. Will panic if provide= d with invalid arguments. + /// + /// The name must be a nul-terminated string, but it is taken as `&[u8= ]` so that it can be used + /// more conveniently with the strings generated by bindgen. + pub const fn const_new( + module: &ThisModule, + name: &[u8], + version: u32, + mcgrps: &'static [MulticastGroup], + ) -> Family { + let n_mcgrps =3D mcgrps.len() as u8; + if n_mcgrps as usize !=3D mcgrps.len() { + panic!("too many mcgrps"); + } + let mut genl_family =3D bindings::genl_family { + version, + _bitfield_1: FamilyFlags { + netnsok: true, + parallel_ops: true, + } + .into_bitfield(), + module: module.as_ptr(), + mcgrps: mcgrps.as_ptr().cast(), + n_mcgrps, + ..pin_init::zeroed() + }; + if CStr::from_bytes_with_nul(name).is_err() { + panic!("genl_family name not nul-terminated"); + } + if genl_family.name.len() < name.len() { + panic!("genl_family name too long"); + } + let mut i =3D 0; + while i < name.len() { + genl_family.name[i] =3D name[i]; + i +=3D 1; + } + Family { + inner: Opaque::new(genl_family), + } + } + + /// Checks if there are any listeners for the given multicast group. + pub fn has_listeners(&self, group: u32) -> bool { + // SAFETY: The family and init_net pointers are valid. + unsafe { + bindings::genl_has_listeners(self.as_raw(), &raw mut bindings:= :init_net, group) !=3D 0 + } + } + + /// Returns a raw pointer to the underlying `genl_family` structure. + pub fn as_raw(&self) -> *mut bindings::genl_family { + self.inner.get() + } +} + +/// A generic netlink multicast group. +#[repr(transparent)] +pub struct MulticastGroup { + // No Opaque because fully immutable + group: bindings::genl_multicast_group, +} + +// SAFETY: Pure data so thread safe. +unsafe impl Sync for MulticastGroup {} + +impl MulticastGroup { + /// Creates a new `MulticastGroup` instance. + /// + /// Intended to be used from const context only. Will panic if provide= d with invalid arguments. + pub const fn const_new(name: &CStr) -> MulticastGroup { + let mut group: bindings::genl_multicast_group =3D pin_init::zeroed= (); + + let name =3D name.to_bytes_with_nul(); + if group.name.len() < name.len() { + panic!("genl_multicast_group name too long"); + } + let mut i =3D 0; + while i < name.len() { + group.name[i] =3D name[i]; + i +=3D 1; + } + + MulticastGroup { group } + } +} + +/// A registration of a generic netlink family. +/// +/// This type represents the registration of a [`Family`]. When an instanc= e of this type is +/// dropped, its respective generic netlink family will be unregistered fr= om the system. +/// +/// # Invariants +/// +/// `self.family` always holds a valid reference to an initialized and reg= istered [`Family`]. +pub struct Registration { + family: &'static Family, +} + +impl Family { + /// Registers the generic netlink family with the kernel. + pub fn register(&'static self) -> Result { + // SAFETY: `self.as_raw()` is a valid pointer to a `genl_family` s= truct. + // The `genl_family` struct is static, so it will outlive the regi= stration. + to_result(unsafe { bindings::genl_register_family(self.as_raw()) }= )?; + Ok(Registration { family: self }) + } +} + +impl Drop for Registration { + fn drop(&mut self) { + // SAFETY: `self.family.as_raw()` is a valid pointer to a register= ed `genl_family` struct. + // The `Registration` struct ensures that `genl_unregister_family`= is called exactly once + // for this family when it goes out of scope. + unsafe { bindings::genl_unregister_family(self.family.as_raw()) }; + } +} + +#[macros::kunit_tests(rust_netlink)] +mod tests { + use super::*; + + #[test] + fn test_family_flags_bitfield() { + for netnsok in [false, true] { + for parallel_ops in [false, true] { + let mut b_fam =3D bindings::genl_family { + ..Default::default() + }; + b_fam.set_netnsok(if netnsok { 1 } else { 0 }); + b_fam.set_parallel_ops(if parallel_ops { 1 } else { 0 }); + + let c_bitfield =3D FamilyFlags { + netnsok, + parallel_ops, + } + .into_bitfield(); + + // SAFETY: The bit field is stored as u8. + let b_val: u8 =3D unsafe { core::mem::transmute(b_fam._bit= field_1) }; + // SAFETY: The bit field is stored as u8. + let c_val: u8 =3D unsafe { core::mem::transmute(c_bitfield= ) }; + assert_eq!(b_val, c_val); + } + } + } +} --=20 2.54.0.823.g6e5bcc1fc9-goog From nobody Mon Jun 8 11:01:52 2026 Received: from mail-wm1-f73.google.com (mail-wm1-f73.google.com [209.85.128.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 85BE033BBCB for ; Fri, 29 May 2026 13:29:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780061378; cv=none; b=INXkPNQm8lOBfdu9n/gA2bGvX49nEGxUWImiEanjnWivjxWN4m/3+oYQWDfB8cQD6uTdsp1K2kbPeZkLN0WCsPxsPZz+00Yr4A1e6h27L6ZDgKNZ9osFBAvCRJgnSNPs7vwWVJL5Astsx/sxvhoW5MsjadLc4d7CCIVosFzRbFI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780061378; c=relaxed/simple; bh=CkLNFLQoIejZn1R53j9upetRYQ373u30vK6NJJ/HCo4=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=khb2k7D4wePPESbqTkgnlsp+PvwtzHZsMlnhMT39G9F4QmeKEirHp7GpXm4xuyqHGppYRlQcqSLu5+1+eBpfrsF55/KUJepJC605/Vd2kt5R55K0NGdBibPRoED4w0eQiCaYnFSZZyErvNP+kipfEurIhGaqgx0bjHVYr5inWGc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=SoxiBUKs; arc=none smtp.client-ip=209.85.128.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="SoxiBUKs" Received: by mail-wm1-f73.google.com with SMTP id 5b1f17b1804b1-48fe6894f3fso86911795e9.2 for ; Fri, 29 May 2026 06:29:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1780061375; x=1780666175; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=xHhhVn4vsW86m1Qo+CSANVQihzAV4jC69efY1gMDYX8=; b=SoxiBUKs1DDsBdT2Ef5Pz01DPqkPumNhd+oaxXEsiGFmvT3JMAlRl+/1nWBlqKD+85 Ur2GdSkpqvipSUSUTSEhzkS+Q07gfs0mI+0BKt4rk6cn4Z58BRZSYYLtdWt9r7micDeA qsbI5XaarhPzuRSdBqmBXgtGI+t60teTcUqGBZykvETnQ/nPx75zoI0OfoxGDILZIcgi 9kD6ChKMlMH27RZPa24isaV2D2lr6xNt9PLwcvkYkyX+827vdgcM765Exst+/GUIx1Rd /rpC9LxmzgfR7UxsdUGZTEu5YkXH2iThHx8hONoBBrOztRNqewcmTHC9z1M4x58DS7q8 kxzw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780061375; x=1780666175; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=xHhhVn4vsW86m1Qo+CSANVQihzAV4jC69efY1gMDYX8=; b=gkSc5R5efaaLngsgemMQ/GaWYuncwfGbOqDW4cDpRYrRBHTEDObvI+J/JleV+sjlfO lI5Q2MunVBLiAK3T/jm7dE1F8e//AalUJKaHeg04mxOCBM3oYCjDXgSGHVvtuGWY3zwx cBBsFIJOesn6370ugwWAH3z7djoq7WNMVgJWZJ9vqa1XQ9tx0kcexncCcXSscxo42zu7 AeFh0hBCaqv11QxEKR3dfzQDCR6Y7bUXUtTjcaYGC9ZCmpaFL+9it3//xHVtN2EoKzgO 2kl0wo89KcGaaCzKdv0CpKpj80f37CRCmfJrGgubYaVVzpteSMcIUQenSSAySav01+vs 96NQ== X-Forwarded-Encrypted: i=1; AFNElJ82wuLgHT61tXXJSyVmXdmXlwK35FcvpUpcmFYL25C//A2ceG2PS8ecAsT/E8bk0u/jUxsgB4n+ROwb7DQ=@vger.kernel.org X-Gm-Message-State: AOJu0YzRrMHwmtcKRnA/LAsiC2O1RUVymxH1c1aY8LOZAAhYi6y962q3 9y4mDc4HE/IPQfjA4yZrozHVRMuaDjkAP8lJiLzHQjnucO7cuXInJPbuYytWza8kTDDbmI6vcCU aLpiDjMqOZrLczZEaVw== X-Received: from wmbgx17.prod.google.com ([2002:a05:600c:8591:b0:490:11e5:b5aa]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:e489:10b0:490:9804:afdc with SMTP id 5b1f17b1804b1-4909c0b9586mr38507975e9.23.1780061374856; Fri, 29 May 2026 06:29:34 -0700 (PDT) Date: Fri, 29 May 2026 13:28:40 +0000 In-Reply-To: <20260529-binder-netlink-v6-0-92dd74b6a32e@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260529-binder-netlink-v6-0-92dd74b6a32e@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=11751; i=aliceryhl@google.com; h=from:subject:message-id; bh=nRyqKbtrahdGEfuNVDlkowtAO1l90fpVZ9tCOqHlNa0=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBqGZSqk1tyiKegjAxOAAC2Kcc512v48b5eC7z3o HJtUF2rsTyJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCahmUqgAKCRAEWL7uWMY5 Rm/FD/9zhMA18YbeLCfnWk4v9OG5fX0xyWUWcoqwGUWF1Ze64dXrPNPq+FBSfIRYR76oPpNnK5N nAuGbS82gLThJMM10/Aq9w1Eg6SViQ1SHthyAD8ZnkwrRwYmGuuUIE2RbZu7vMZKjJ+GHZwFM6M 9h9fLPs4xnLBRp5/LaECVfRoy7tG3AHl5/URad6VGYAdbNIb58Y599lKMjrA98xxssPLlYQ7ziO RcbaaRMFaUoJWi/udgjqc+8ghIRAhcET8/aYsOdWrVX3kyRIEeKpWe33AJpotrFOAaJc/eXCRZu YYuakVZaisS9seQZlHIO/VCcudTziVYeywxg1SaQmbQUSfmfcf7cDDhnirQy0ryoQtzCVXmlOq8 tvK3qg4xAQNXiFezliDgbGZvCxIHOdth3fT0LN4C+Tlzl6a0KBqHxhfliyOWYSf5Cl2WiwVPav+ pB6SR2R3dZpgkCVewr01byEWFYsvePXENBM3lvnGtfstW9Dw4F+myIHPDQmA97nkuRLW3ofYGja 9BpCGbW6M6wbaXjeyek95NLmMcKQseQiDb1NGuBBZ9k/Myvr2zOhPq2scZnQ5UIT2fSDUcbgjHV hHvt4foePGyY3zzTWc/DdU3RM4X86/yeAzzihnqH0jRSsS80uYfDkp3LgP+NKx9Vz9DlsBEggS3 zz13KgOGTFoDVjQ== X-Mailer: b4 0.14.3 Message-ID: <20260529-binder-netlink-v6-3-92dd74b6a32e@google.com> Subject: [PATCH v6 3/3] rust_binder: report netlink transactions From: Alice Ryhl To: Carlos Llamas , Greg Kroah-Hartman , Andrew Lunn , Donald Hunter , Jakub Kicinski , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Matthew Maurer Cc: Miguel Ojeda , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , Christian Brauner , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, netdev@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Carlos Llamas The Android Binder driver supports a netlink API that reports transaction *failures* to a userspace daemon. This allows devices to monitor processes with many failed transactions so that it can e.g. kill misbehaving apps. One very important thing that this monitors is when many oneway messages are sent to a frozen process, so there is special handling to ensure this scenario is surfaced over netlink. Signed-off-by: Carlos Llamas Co-developed-by: Alice Ryhl Signed-off-by: Alice Ryhl Acked-by: Carlos Llamas --- drivers/android/Kconfig | 2 +- drivers/android/binder/netlink.rs | 117 +++++++++++++++++++++++++= ++++ drivers/android/binder/rust_binder_main.rs | 8 +- drivers/android/binder/thread.rs | 11 ++- drivers/android/binder/transaction.rs | 40 ++++++++++ rust/uapi/uapi_helper.h | 1 + 6 files changed, 175 insertions(+), 4 deletions(-) diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index e2e402c9d175..606a9d07f774 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig @@ -16,7 +16,7 @@ config ANDROID_BINDER_IPC =20 config ANDROID_BINDER_IPC_RUST bool "Rust version of Android Binder IPC Driver" - depends on RUST && MMU && !ANDROID_BINDER_IPC + depends on RUST && MMU && NET && !ANDROID_BINDER_IPC help This enables the Rust implementation of the Binder driver. =20 diff --git a/drivers/android/binder/netlink.rs b/drivers/android/binder/net= link.rs new file mode 100644 index 000000000000..beb7ea2edaff --- /dev/null +++ b/drivers/android/binder/netlink.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Cl= ause) +/* Based on: Documentation/netlink/specs/binder.yaml */ + +#![allow(unreachable_pub, clippy::wrong_self_convention)] +use kernel::{ + net::netlink::{ + Family, + GenlMsg, + MulticastGroup, + NetlinkSkBuff, // + }, + prelude::*, // +}; + +pub static BINDER_NL_FAMILY: Family =3D Family::const_new( + &crate::THIS_MODULE, + kernel::uapi::BINDER_FAMILY_NAME, + kernel::uapi::BINDER_FAMILY_VERSION, + &BINDER_NL_FAMILY_MCGRPS, +); + +static BINDER_NL_FAMILY_MCGRPS: [MulticastGroup; 1] =3D [MulticastGroup::c= onst_new(c"report")]; + +/// A multicast event sent to userspace subscribers to notify them about +/// binder transaction failures. The generated report provides the full +/// details of the specific transaction that failed. The intention is for +/// programs to monitor these events and react to the failures as needed. +pub struct Report { + skb: GenlMsg, +} + +impl Report { + /// Create a new multicast message. + pub fn new( + size: usize, + portid: u32, + seq: u32, + flags: kernel::alloc::Flags, + ) -> Result { + const BINDER_CMD_REPORT: u8 =3D kernel::uapi::BINDER_CMD_REPORT as= u8; + let skb =3D NetlinkSkBuff::new(size, flags)?; + let skb =3D skb.genlmsg_put(portid, seq, &BINDER_NL_FAMILY, BINDER= _CMD_REPORT)?; + Ok(Self { skb }) + } + + /// Broadcast this message. + pub fn multicast(self, portid: u32, flags: kernel::alloc::Flags) -> Re= sult { + self.skb.multicast(&BINDER_NL_FAMILY, portid, 0, flags) + } + + /// Check if this message type has listeners. + pub fn has_listeners() -> bool { + BINDER_NL_FAMILY.has_listeners(0) + } + + /// The enum binder_driver_return_protocol returned to the sender. + pub fn error(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_ERROR: c_int =3D kernel::uapi::BINDER_A_REPO= RT_ERROR as c_int; + self.skb.put_u32(BINDER_A_REPORT_ERROR, val) + } + + /// The binder context where the transaction occurred. + pub fn context(&mut self, val: &CStr) -> Result { + const BINDER_A_REPORT_CONTEXT: c_int =3D kernel::uapi::BINDER_A_RE= PORT_CONTEXT as c_int; + self.skb.put_string(BINDER_A_REPORT_CONTEXT, val) + } + + /// The PID of the sender process. + pub fn from_pid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FROM_PID: c_int =3D kernel::uapi::BINDER_A_R= EPORT_FROM_PID as c_int; + self.skb.put_u32(BINDER_A_REPORT_FROM_PID, val) + } + + /// The TID of the sender thread. + pub fn from_tid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FROM_TID: c_int =3D kernel::uapi::BINDER_A_R= EPORT_FROM_TID as c_int; + self.skb.put_u32(BINDER_A_REPORT_FROM_TID, val) + } + + /// The PID of the recipient process. This attribute may not be present + /// if the target could not be determined. + pub fn to_pid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_TO_PID: c_int =3D kernel::uapi::BINDER_A_REP= ORT_TO_PID as c_int; + self.skb.put_u32(BINDER_A_REPORT_TO_PID, val) + } + + /// The TID of the recipient thread. This attribute may not be present + /// if the target could not be determined. + pub fn to_tid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_TO_TID: c_int =3D kernel::uapi::BINDER_A_REP= ORT_TO_TID as c_int; + self.skb.put_u32(BINDER_A_REPORT_TO_TID, val) + } + + /// When present, indicates the failed transaction is a reply. + pub fn is_reply(&mut self) -> Result { + const BINDER_A_REPORT_IS_REPLY: c_int =3D kernel::uapi::BINDER_A_R= EPORT_IS_REPLY as c_int; + self.skb.put_flag(BINDER_A_REPORT_IS_REPLY) + } + + /// The bitmask of enum transaction_flags from the transaction. + pub fn flags(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FLAGS: c_int =3D kernel::uapi::BINDER_A_REPO= RT_FLAGS as c_int; + self.skb.put_u32(BINDER_A_REPORT_FLAGS, val) + } + + /// The application-defined code from the transaction. + pub fn code(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_CODE: c_int =3D kernel::uapi::BINDER_A_REPOR= T_CODE as c_int; + self.skb.put_u32(BINDER_A_REPORT_CODE, val) + } + + /// The transaction payload size in bytes. + pub fn data_size(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_DATA_SIZE: c_int =3D kernel::uapi::BINDER_A_= REPORT_DATA_SIZE as c_int; + self.skb.put_u32(BINDER_A_REPORT_DATA_SIZE, val) + } +} diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/b= inder/rust_binder_main.rs index dc1941cd2407..0dc62ac440ec 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -38,6 +38,7 @@ mod deferred_close; mod defs; mod error; +mod netlink; mod node; mod page_range; mod process; @@ -288,19 +289,22 @@ fn ptr_align(value: usize) -> Option { // SAFETY: We call register in `init`. static BINDER_SHRINKER: Shrinker =3D unsafe { Shrinker::new() }; =20 -struct BinderModule {} +struct BinderModule { + _netlink: kernel::net::netlink::Registration, +} =20 impl kernel::Module for BinderModule { fn init(_module: &'static kernel::ThisModule) -> Result { // SAFETY: The module initializer never runs twice, so we only cal= l this once. unsafe { crate::context::CONTEXTS.init() }; =20 + let netlink =3D crate::netlink::BINDER_NL_FAMILY.register()?; BINDER_SHRINKER.register(c"android-binder")?; =20 // SAFETY: The module is being loaded, so we can initialize binder= fs. unsafe { kernel::error::to_result(binderfs::init_rust_binderfs())?= }; =20 - Ok(Self {}) + Ok(Self { _netlink: netlink }) } } =20 diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thre= ad.rs index 97d5f31e8fe3..25ea01ce9ecf 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1246,7 +1246,6 @@ fn transaction(self: &Arc, cmd: u32, reader: &m= ut UserSliceReader) -> Resu self.push_return_work(err.reply); if let Some(source) =3D &err.source { info.errno =3D source.to_errno(); - info.reply =3D err.reply; =20 { let mut ee =3D self.inner.lock().extended_error; @@ -1263,6 +1262,15 @@ fn transaction(self: &Arc, cmd: u32, reader: &= mut UserSliceReader) -> Resu } } =20 + if info.oneway_spam_suspect { + // If this is both a oneway spam suspect and a failure, we rep= ort it twice. This is + // useful in case the transaction failed with BR_TRANSACTION_P= ENDING_FROZEN. + info.report_netlink(BR_ONEWAY_SPAM_SUSPECT, &self.process.ctx); + } + if info.reply !=3D 0 { + info.report_netlink(info.reply, &self.process.ctx); + } + Ok(()) } =20 @@ -1332,6 +1340,7 @@ fn reply_inner(self: &Arc, info: &mut Transacti= onInfo) -> BinderResult { ); let reply =3D Err(BR_FAILED_REPLY); orig.from.deliver_reply(reply, &orig); + info.reply =3D BR_FAILED_REPLY; err.reply =3D BR_TRANSACTION_COMPLETE; err }); diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder= /transaction.rs index 47d5e4d88b07..ce1b1ccd080a 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -3,6 +3,7 @@ // Copyright (C) 2025 Google LLC. =20 use kernel::{ + net::netlink::GENLMSG_DEFAULT_SIZE, prelude::*, seq_file::SeqFile, seq_print, @@ -17,6 +18,7 @@ allocation::{Allocation, TranslatedFds}, defs::*, error::{BinderError, BinderResult}, + netlink::Report, node::{Node, NodeRef}, process::{Process, ProcessInner}, ptr_align, @@ -49,6 +51,44 @@ impl TransactionInfo { pub(crate) fn is_oneway(&self) -> bool { self.flags & TF_ONE_WAY !=3D 0 } + + pub(crate) fn report_netlink(&self, reply: u32, ctx: &crate::Context) { + if let Err(err) =3D self.report_netlink_inner(reply, ctx) { + pr_warn!( + "{}:{} netlink report failed: {err:?}\n", + self.from_pid, + self.from_tid + ); + } + } + + fn report_netlink_inner(&self, reply: u32, ctx: &crate::Context) -> ke= rnel::error::Result { + if !Report::has_listeners() { + return Ok(()); + } + let mut report =3D Report::new(GENLMSG_DEFAULT_SIZE, 0, 0, GFP_KER= NEL)?; + + report.error(reply)?; + report.context(&ctx.name)?; + report.from_pid(self.from_pid as u32)?; + report.from_tid(self.from_tid as u32)?; + if self.to_pid !=3D 0 { + report.to_pid(self.to_pid as u32)?; + } + if self.to_tid !=3D 0 { + report.to_tid(self.to_tid as u32)?; + } + + if self.is_reply { + report.is_reply()?; + } + report.flags(self.flags)?; + report.code(self.code)?; + report.data_size(self.data_size as u32)?; + + report.multicast(0, GFP_KERNEL)?; + Ok(()) + } } =20 use core::mem::offset_of; diff --git a/rust/uapi/uapi_helper.h b/rust/uapi/uapi_helper.h index 06d7d1a2e8da..86c7b6b284b0 100644 --- a/rust/uapi/uapi_helper.h +++ b/rust/uapi/uapi_helper.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include --=20 2.54.0.823.g6e5bcc1fc9-goog