From nobody Tue May 5 11:27:25 2026 Received: from mail-wr1-f48.google.com (mail-wr1-f48.google.com [209.85.221.48]) (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 A5E0528980F for ; Tue, 21 Apr 2026 19:13:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776798825; cv=none; b=GD1uYsypb3Fb/IM915N5Zc1FkyH0VgMqErnaQGIVnvfis5rz2gPUnva8nJw7DUQirnpadoaqSnpz6/9ClKn+P0WStRehQga9tJXkI+3P+vbieHuHcq7a41Pe2WkWc+YTFDROvvMpe1ZFUNMGCNkbfgHp9zWVgmr0kW6HwAVeFfI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776798825; c=relaxed/simple; bh=H2DxqvPp1p1h/pp8QJa8KtnMGSyLhcvXbIy6wIAiF9g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=R4CbU8hV4NCMi0UFaB3R4Zsz9GrhP1gIU0MhAsiPF+gHz86+MRizz8IijnLmg9LQXOQlrr7WijdWR5boi0XCIkJAEB5J5/KyRUVT04g0avdKxJAYEuTDqvZqemUutEs06tTF9XKbnF9JVwolVDKb/aV+n2vEb0+54mCWcxfVX5I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=DdvQDYTm; arc=none smtp.client-ip=209.85.221.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="DdvQDYTm" Received: by mail-wr1-f48.google.com with SMTP id ffacd0b85a97d-43d73352cf2so3866262f8f.1 for ; Tue, 21 Apr 2026 12:13:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776798822; x=1777403622; darn=lists.linux.dev; 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=/2WxMhAslpbON7+vuHLR3IrAIYssnVtmPuFkoGSXvBI=; b=DdvQDYTmKNs5vp6VEyPOSVskKFf/QpGbUZQCK0cSdLlmMXe3t9Xo1ChejZ0t4XQKGN ZRR0QdgCUpYhEXAAki22MkR0U3Otx0cWn1na+ri/22P0NPuyn6VCu5MZX6/3JGiWSCoD GgbjstaOtx/gJfbwH8BfMed8xThU4gtU3JR9TAMNLLjs07RxWmYqYx8tFsm1Nw/QF1AO dwyhTJ4cPAanJ9TAQgMhgtHAY+T/yVUn7VoRRL7DkPXHLZSm49XsY79xqX8TdS3FgfYp sDkB87GlgNbWC5CzA2OjASYwrkFvMrOMdH9JDIizP/juwgHQd6ueFTwgMuR/I6iqiA/p CWIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776798822; x=1777403622; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=/2WxMhAslpbON7+vuHLR3IrAIYssnVtmPuFkoGSXvBI=; b=Rwe3bLYv7IbKIgnjzihhas+4q93lQQexKaNBlqLikaJgjq88rVJH00MDVd4sfNg333 8E9ESEWkcYVWusNhFxjlFiPLzJCc4bBLIqo2Y1VUU6RJKgD0JFIt6MngacmeKaysyGXT UKDRxOov0/2hzWrHzg5IbosmL1T8PCDl7GlEs9Wvn5KnSRnoukQg8i68Zf4vx9q5POvb fjwtxfKJ6SXDgbXRRr6qQZHOg4nQ2FYNqSUd9tQQ8gzegrRMVuajLl441OrrI6Kz2EEr clxpIqrVHD9I0OZNfQ3RGa6R1ZFcr6tEVSfWqr/C3KmEqe/yChc0kYlxyLLl3GKdYNXh IZWA== X-Gm-Message-State: AOJu0YwXF7Z9lJ3f8+Xj5EuzTFp81TDD/8r1GNvU8aSGpSrhFAVuh63q LqWL3RNEyWHATZse1UxWemKe7B3zXAoJcnz/6IWTCWvg4YA5CEnUISdr1hP6r8mv2xU= X-Gm-Gg: AeBDietFGNESbRZ9oY8G+9p8JZ0Eyru2emmOJjjvhffaCqc0s2LzgFGKly32SjO0d5f /40FdI7/gizZ9uf0u18mC1rr11oisgm4FQaIj7BR/CfPL1aud5r8AIUXDpXLyW8RDMEpmclAVCK X+sLqji4sAxIRGdSQ0csvqIVBRCooNATMlFLD419ATSs7LzZjpGhUh39lqP/gbPqS2dcl1z16jO Ii0jl4X3THaPjpEg/xAshKC7pvsxyIqQxA9+VhpyqcCQbT/SvgIkS2e0XrtLYPFkTvOHqSWeytU eHOhhyf8DSpxKX4wd1m7wWKy5voAtLaPM4HSgnSGLZX57R4Ja4klYx/BdXC7gJXMZ/uv2GByn42 ZlVY6fGwt+gtUGK4Dblt8MsY5rQV321Itf5G8w4Txb3C7lNOI6EMnYGJZbUfY/TJyKXcK/Rt+0s hAiW7B50U63e8f/WTjmYZvib1K9/khKGe34KOItVkfIkSSDSeo6VV72V8ly8a0AW/tBQQqctYoQ SpiHzybU0NymioQpsTppg== X-Received: by 2002:a05:6000:40df:b0:43d:a37f:8d5c with SMTP id ffacd0b85a97d-43fe3dc812fmr32694067f8f.17.1776798821544; Tue, 21 Apr 2026 12:13:41 -0700 (PDT) Received: from dohko.chello.ie (188-141-5-72.dynamic.upc.ie. [188.141.5.72]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43fe4e4d112sm43859447f8f.29.2026.04.21.12.13.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Apr 2026 12:13:41 -0700 (PDT) From: David Carlier To: mptcp@lists.linux.dev Cc: Matthieu Baerts , Mat Martineau , Geliang Tang , David Carlier Subject: [PATCH mptcp-next v2 1/3] mptcp: propagate RECVERR sockopts to subflows Date: Tue, 21 Apr 2026 20:13:35 +0100 Message-ID: <20260421191337.58341-2-devnexen@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260421191337.58341-1-devnexen@gmail.com> References: <20260421191337.58341-1-devnexen@gmail.com> Precedence: bulk X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Propagate IP_RECVERR/IP_RECVERR_RFC4884 and IPV6_RECVERR/IPV6_RECVERR_RFC4884 from the MPTCP socket to existing and future subflows. Apply the matching sockopt according to the subflow family so mixed- family subflows stay aligned with the parent socket configuration, including disable-time errqueue purge semantics. Signed-off-by: David Carlier Assisted-by: Codex:gpt-5 Signed-off-by: David Carlier --- v1 -> v2: - Retargeted to mptcp-next per Matthieu Baerts' feedback (net-next closed during the merge window; iterate on the MPTCP tree). - Guard mptcp_setsockopt_v6_recverr() and its dispatch cases in mptcp_setsockopt_v6() with #if IS_ENABLED(CONFIG_IPV6) to fix the MPTCP CI link break on without_ipv6/with_mptcp configs (undefined reference to ipv6_setsockopt). v1: https://lore.kernel.org/mptcp/20260421152216.38127-1-devnexen@gmail.com/ net/mptcp/sockopt.c | 129 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index 79db15903e7a..acb0ca330e44 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -8,6 +8,8 @@ =20 #include #include +#include +#include #include #include #include @@ -384,6 +386,72 @@ static int mptcp_setsockopt_sol_socket(struct mptcp_so= ck *msk, int optname, return -EOPNOTSUPP; } =20 +static bool mptcp_recverr_enabled(const struct sock *sk, bool rfc4884) +{ + bool enabled; + + enabled =3D rfc4884 ? inet_test_bit(RECVERR_RFC4884, sk) : + inet_test_bit(RECVERR, sk); + +#if IS_ENABLED(CONFIG_IPV6) + if (sk->sk_family =3D=3D AF_INET6) + enabled |=3D rfc4884 ? inet6_test_bit(RECVERR6_RFC4884, sk) : + inet6_test_bit(RECVERR6, sk); +#endif + + return enabled; +} + +static int mptcp_subflow_set_recverr(struct sock *sk, struct sock *ssk, + bool rfc4884) +{ + int level, optname, val; + +#if IS_ENABLED(CONFIG_IPV6) + if (ssk->sk_family =3D=3D AF_INET6) { + level =3D SOL_IPV6; + optname =3D rfc4884 ? IPV6_RECVERR_RFC4884 : IPV6_RECVERR; + } else +#endif + { + level =3D SOL_IP; + optname =3D rfc4884 ? IP_RECVERR_RFC4884 : IP_RECVERR; + } + + val =3D mptcp_recverr_enabled(sk, rfc4884); + return tcp_setsockopt(ssk, level, optname, KERNEL_SOCKPTR(&val), + sizeof(val)); +} + +#if IS_ENABLED(CONFIG_IPV6) +static int mptcp_setsockopt_v6_recverr(struct mptcp_sock *msk, int optname, + sockptr_t optval, unsigned int optlen) +{ + struct mptcp_subflow_context *subflow; + struct sock *sk =3D (struct sock *)msk; + int ret; + + ret =3D ipv6_setsockopt(sk, SOL_IPV6, optname, optval, optlen); + if (ret) + return ret; + + lock_sock(sk); + sockopt_seq_inc(msk); + mptcp_for_each_subflow(msk, subflow) { + struct sock *ssk =3D mptcp_subflow_tcp_sock(subflow); + bool rfc4884 =3D optname =3D=3D IPV6_RECVERR_RFC4884; + + ret =3D mptcp_subflow_set_recverr(sk, ssk, rfc4884); + if (ret) + break; + subflow->setsockopt_seq =3D msk->setsockopt_seq; + } + release_sock(sk); + + return ret; +} +#endif + static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname, sockptr_t optval, unsigned int optlen) { @@ -426,6 +494,12 @@ static int mptcp_setsockopt_v6(struct mptcp_sock *msk,= int optname, =20 release_sock(sk); break; +#if IS_ENABLED(CONFIG_IPV6) + case IPV6_RECVERR: + case IPV6_RECVERR_RFC4884: + ret =3D mptcp_setsockopt_v6_recverr(msk, optname, optval, optlen); + break; +#endif } =20 return ret; @@ -760,6 +834,33 @@ static int mptcp_setsockopt_v4_set_tos(struct mptcp_so= ck *msk, int optname, return 0; } =20 +static int mptcp_setsockopt_v4_recverr(struct mptcp_sock *msk, int optname, + sockptr_t optval, unsigned int optlen) +{ + struct mptcp_subflow_context *subflow; + struct sock *sk =3D (struct sock *)msk; + int err; + + err =3D ip_setsockopt(sk, SOL_IP, optname, optval, optlen); + if (err) + return err; + + lock_sock(sk); + sockopt_seq_inc(msk); + mptcp_for_each_subflow(msk, subflow) { + struct sock *ssk =3D mptcp_subflow_tcp_sock(subflow); + bool rfc4884 =3D optname =3D=3D IP_RECVERR_RFC4884; + + err =3D mptcp_subflow_set_recverr(sk, ssk, rfc4884); + if (err) + break; + subflow->setsockopt_seq =3D msk->setsockopt_seq; + } + release_sock(sk); + + return err; +} + static int mptcp_setsockopt_v4(struct mptcp_sock *msk, int optname, sockptr_t optval, unsigned int optlen) { @@ -771,6 +872,9 @@ static int mptcp_setsockopt_v4(struct mptcp_sock *msk, = int optname, return mptcp_setsockopt_sol_ip_set(msk, optname, optval, optlen); case IP_TOS: return mptcp_setsockopt_v4_set_tos(msk, optname, optval, optlen); + case IP_RECVERR: + case IP_RECVERR_RFC4884: + return mptcp_setsockopt_v4_recverr(msk, optname, optval, optlen); } =20 return -EOPNOTSUPP; @@ -1459,6 +1563,12 @@ static int mptcp_getsockopt_v4(struct mptcp_sock *ms= k, int optname, case IP_LOCAL_PORT_RANGE: return mptcp_put_int_option(msk, optval, optlen, READ_ONCE(inet_sk(sk)->local_port_range)); + case IP_RECVERR: + return mptcp_put_int_option(msk, optval, optlen, + inet_test_bit(RECVERR, sk)); + case IP_RECVERR_RFC4884: + return mptcp_put_int_option(msk, optval, optlen, + inet_test_bit(RECVERR_RFC4884, sk)); } =20 return -EOPNOTSUPP; @@ -1479,6 +1589,12 @@ static int mptcp_getsockopt_v6(struct mptcp_sock *ms= k, int optname, case IPV6_FREEBIND: return mptcp_put_int_option(msk, optval, optlen, inet_test_bit(FREEBIND, sk)); + case IPV6_RECVERR: + return mptcp_put_int_option(msk, optval, optlen, + inet6_test_bit(RECVERR6, sk)); + case IPV6_RECVERR_RFC4884: + return mptcp_put_int_option(msk, optval, optlen, + inet6_test_bit(RECVERR6_RFC4884, sk)); } =20 return -EOPNOTSUPP; @@ -1536,6 +1652,7 @@ static void sync_socket_options(struct mptcp_sock *ms= k, struct sock *ssk) { static const unsigned int tx_rx_locks =3D SOCK_RCVBUF_LOCK | SOCK_SNDBUF_= LOCK; struct sock *sk =3D (struct sock *)msk; + bool recverr, recverr_rfc4884; bool keep_open; =20 keep_open =3D sock_flag(sk, SOCK_KEEPOPEN); @@ -1586,6 +1703,18 @@ static void sync_socket_options(struct mptcp_sock *m= sk, struct sock *ssk) inet_assign_bit(FREEBIND, ssk, inet_test_bit(FREEBIND, sk)); inet_assign_bit(BIND_ADDRESS_NO_PORT, ssk, inet_test_bit(BIND_ADDRESS_NO_= PORT, sk)); WRITE_ONCE(inet_sk(ssk)->local_port_range, READ_ONCE(inet_sk(sk)->local_p= ort_range)); + recverr =3D mptcp_recverr_enabled(sk, false); + recverr_rfc4884 =3D mptcp_recverr_enabled(sk, true); +#if IS_ENABLED(CONFIG_IPV6) + if (ssk->sk_family =3D=3D AF_INET6) { + inet6_assign_bit(RECVERR6, ssk, recverr); + inet6_assign_bit(RECVERR6_RFC4884, ssk, recverr_rfc4884); + } else +#endif + { + inet_assign_bit(RECVERR, ssk, recverr); + inet_assign_bit(RECVERR_RFC4884, ssk, recverr_rfc4884); + } } =20 void mptcp_sockopt_sync_locked(struct mptcp_sock *msk, struct sock *ssk) --=20 2.53.0 From nobody Tue May 5 11:27:25 2026 Received: from mail-wr1-f50.google.com (mail-wr1-f50.google.com [209.85.221.50]) (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 AADAA284693 for ; Tue, 21 Apr 2026 19:13:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776798826; cv=none; b=R/e02dJXHpdNFITHYJwFC9JSjXdNakqCJOxRoiOrQo+I2eGuYr8M5SCFjiBYtcPNVypK6uS+keABOKvAAyHdMH5JYhWDqHC1h26D1IuUcSXX+pUeAfBsbdlnu3qfagiy2CUUVQdBvIBx8tswTzqHN8IXXXJ2v472F4arqu/g1Bc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776798826; c=relaxed/simple; bh=4xyJeZXUQ2uBeQdp9f1NS8Fh0zmFyUANa9YQeTh+GVs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hDYnXz6xEkULUQAIx/LfWw/dGiTepMCeuALydmaJ//lIkj9fJcYldRA4mVNwIwRpQDR8RV0MSfqbkAif9+EiNNUI0XzyUKFjutp42lXP+Rf0VgfloVYPD9S1At8HSwHiO/+GEPRB4PuTsXXpDgVDEd3Wbl+uvKj0La5jBLd8wAQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=HcBqPQtf; arc=none smtp.client-ip=209.85.221.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HcBqPQtf" Received: by mail-wr1-f50.google.com with SMTP id ffacd0b85a97d-43fe8bda8e9so2406017f8f.1 for ; Tue, 21 Apr 2026 12:13:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776798823; x=1777403623; darn=lists.linux.dev; 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=3qFZTDU7vLzWx/IvTmuyXfCSxmgGLGdxMBwdf5JEknI=; b=HcBqPQtfXfJ8PvB7jYFHxtT1thSeJFM/e93SJuXM9EmbwwgeQVL1aw9aJzBP6u9iW3 cfAypEWA+IxFLImHYrlajhvDPeusxwlmRmEEtWAMWHJQm/JowZKlvWrLU8mIzjst+svh +rdtECSUz5r0t84PulaezGTQDPThkVzu3C1nFARgU+TZeo3n7M3MQgRc4FrsiQMlZ1H0 REw/pVJZBN5CU1Y4haIpSSQrD6znMw9e0aqHnjHLj1Gno9QpM0yI8Oy0fGfRIkBL5sX4 bZPrOw4Y7JMQdZjEu8RFuE78cVKrjy49zpU6VWOaii5gUoa1yWozE1T6RxHoh8pHFF7g /3/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776798823; x=1777403623; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=3qFZTDU7vLzWx/IvTmuyXfCSxmgGLGdxMBwdf5JEknI=; b=mDay9jNQxDbzhJRiBo1bK+Z/h1uFPgXCGgLRCjtJzErIAlBXJ7++9oyoedMmDQuTx7 YJ7G6DWqem1MDinMb1FLw+SwIIdLHImmRXlkPafZQOVWcGXL+xlpC9DQy+3e0eozIss6 SUGqpiJ0OBRu+fnnM40toTD4qGPfxtISngKoZcFQ0d6vwI+mRocHM2sz/iO2b084fqOx rUJD2LQiFymu5xkf6rthXta5wNXXx1uf1H4omx8bMts4D/jLgGmW7yoQUBFZRaU9b4TZ h+VQr27UmuUWWttNOpsFnJ5BFnQo7SzSgUCXhPIjgJ4xydUC7QAJX8Y/YZaPGMcj8vCe V1MQ== X-Gm-Message-State: AOJu0Yx8buw6GLIPKnOdYX+sLBLztPjhrnDD4B0ncpGkbwEhQw1vdEEu AQIT79BBFQJ4M5rZSesqOPkuhAHWG/WRpaCHAZ5pRGXCY2ScrfYNzZ1hZI4syL4Jb3c= X-Gm-Gg: AeBDiet9WUImJXOHge+5Y6G7bpCBXlIrhgPYQqkTqqU3KMGzUJ4ycrkb87RpQ0WBDVC nDP5+hswYthdOCyUfm6kz0qv/049sgP8/lYs24RBSxSovBCSHCNFGMs/zbqHbZxXI3ODfrLIXLl PNF2zFKJIK24dC/AAALL0WqrER0SlT2mMOcENuiIk4UQRm2ZXAaeIjsA0WSIjOTeJFeETWfPuim +ABv87hw2opVM5wCLwe6Tw7YLAQdfUnZiwNkN8dlhDIIB8bh7DMXdgbn3S5tRLq236id7djK8M7 R2T6AlnpC53IZtKJC2ocT9OAP1P2PoCrog0vRqJnbhgKIDj+jcj7mSGsFJAJUfFMnHSrYxMJUpO SDy2b1HoH3s2DGscCkIQvEOc5uezPRVpGSUv/sY1hlE4h+MEmAijJKPTBj8GkDwqw+pOHJSmmv2 Ta+MhuVLQFbs7FUE+F9aF4c6JSFTb9I5EHbMzx7/VC3A+C1CuWgRQuzWyat1LGBRvdr9wi3qNYs vmBdKV2NhwML4Yr169iXEKJVm0zp9s6 X-Received: by 2002:a05:6000:2909:b0:43f:e22d:e624 with SMTP id ffacd0b85a97d-43fe3db3cb2mr28198324f8f.1.1776798822599; Tue, 21 Apr 2026 12:13:42 -0700 (PDT) Received: from dohko.chello.ie (188-141-5-72.dynamic.upc.ie. [188.141.5.72]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43fe4e4d112sm43859447f8f.29.2026.04.21.12.13.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Apr 2026 12:13:42 -0700 (PDT) From: David Carlier To: mptcp@lists.linux.dev Cc: Matthieu Baerts , Mat Martineau , Geliang Tang , David Carlier Subject: [PATCH mptcp-next v2 2/3] mptcp: support MSG_ERRQUEUE on the parent socket Date: Tue, 21 Apr 2026 20:13:36 +0100 Message-ID: <20260421191337.58341-3-devnexen@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260421191337.58341-1-devnexen@gmail.com> References: <20260421191337.58341-1-devnexen@gmail.com> Precedence: bulk X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Handle MSG_ERRQUEUE on the MPTCP socket by selecting a subflow with pending errqueue data, moving one error skb to the parent socket, and consuming it through the parent socket ABI. This surfaces subflow errqueue activity through poll(), keeps the userspace ABI tied to the socket being used, and restores the skb to the subflow errqueue if requeueing to the parent fails under rmem pressure. Signed-off-by: David Carlier Assisted-by: Codex:gpt-5 --- net/mptcp/protocol.c | 121 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 18 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 6b486fc94c16..558aa57073a7 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -819,26 +819,29 @@ static bool __mptcp_subflow_error_report(struct sock = *sk, struct sock *ssk) { int ssk_state; int err; + bool has_errqueue; =20 - /* only propagate errors on fallen-back sockets or - * on MPC connect - */ - if (sk->sk_state !=3D TCP_SYN_SENT && !__mptcp_check_fallback(mptcp_sk(sk= ))) - return false; - + has_errqueue =3D !skb_queue_empty_lockless(&ssk->sk_error_queue); err =3D sock_error(ssk); - if (!err) + if (!err && !has_errqueue) return false; =20 - /* We need to propagate only transition to CLOSE state. - * Orphaned socket will see such state change via - * subflow_sched_work_if_closed() and that path will properly - * destroy the msk as needed. + /* Errqueue notifications should wake poll()/recvmsg(MSG_ERRQUEUE) on + * the MPTCP socket, but only fallback sockets and the MPC connect path + * inherit TCP's sk_err semantics. */ - ssk_state =3D inet_sk_state_load(ssk); - if (ssk_state =3D=3D TCP_CLOSE && !sock_flag(sk, SOCK_DEAD)) - mptcp_set_state(sk, ssk_state); - WRITE_ONCE(sk->sk_err, -err); + if (err && + (sk->sk_state =3D=3D TCP_SYN_SENT || __mptcp_check_fallback(mptcp_sk(= sk)))) { + /* We need to propagate only transition to CLOSE state. + * Orphaned socket will see such state change via + * subflow_sched_work_if_closed() and that path will properly + * destroy the msk as needed. + */ + ssk_state =3D inet_sk_state_load(ssk); + if (ssk_state =3D=3D TCP_CLOSE && !sock_flag(sk, SOCK_DEAD)) + mptcp_set_state(sk, ssk_state); + WRITE_ONCE(sk->sk_err, -err); + } =20 /* This barrier is coupled with smp_rmb() in mptcp_poll() */ smp_wmb(); @@ -2286,6 +2289,68 @@ static unsigned int mptcp_inq_hint(const struct sock= *sk) return 0; } =20 +static struct sock *mptcp_pick_errqueue_subflow(struct sock *sk) +{ + struct mptcp_subflow_context *subflow; + struct sock *ssk =3D NULL; + + lock_sock(sk); + mptcp_for_each_subflow(mptcp_sk(sk), subflow) { + struct sock *subflow_sk =3D mptcp_subflow_tcp_sock(subflow); + + if (skb_queue_empty_lockless(&subflow_sk->sk_error_queue)) + continue; + + if (!refcount_inc_not_zero(&subflow_sk->sk_refcnt)) + continue; + + ssk =3D subflow_sk; + break; + } + release_sock(sk); + + return ssk; +} + +static bool mptcp_has_error_queue(const struct sock *sk) +{ + return !skb_queue_empty_lockless(&sk->sk_error_queue); +} + +static int mptcp_recv_error(struct sock *sk, struct msghdr *msg, int len) +{ + struct sk_buff *skb; + struct sock *ssk; + int ret, ret2; + + if (mptcp_has_error_queue(sk)) + return inet_recv_error(sk, msg, len); + + ssk =3D mptcp_pick_errqueue_subflow(sk); + if (!ssk) + return -EAGAIN; + + skb =3D sock_dequeue_err_skb(ssk); + if (!skb) + goto put_ssk; + + ret =3D sock_queue_err_skb(sk, skb); + if (ret) { + ret2 =3D sock_queue_err_skb(ssk, skb); + sock_put(ssk); + if (ret2) + kfree_skb(skb); + return ret; + } + + sock_put(ssk); + return inet_recv_error(sk, msg, len); + +put_ssk: + sock_put(ssk); + return -EAGAIN; +} + static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags) { @@ -2295,9 +2360,8 @@ static int mptcp_recvmsg(struct sock *sk, struct msgh= dr *msg, size_t len, int target; long timeo; =20 - /* MSG_ERRQUEUE is really a no-op till we support IP_RECVERR */ if (unlikely(flags & MSG_ERRQUEUE)) - return inet_recv_error(sk, msg, len); + return mptcp_recv_error(sk, msg, len); =20 lock_sock(sk); if (unlikely(sk->sk_state =3D=3D TCP_LISTEN)) { @@ -4296,6 +4360,26 @@ static __poll_t mptcp_check_writeable(struct mptcp_s= ock *msk) return 0; } =20 +static bool mptcp_subflow_has_error(struct sock *sk) +{ + struct mptcp_subflow_context *subflow; + bool has_error =3D false; + + mptcp_data_lock(sk); + mptcp_for_each_subflow(mptcp_sk(sk), subflow) { + struct sock *ssk =3D mptcp_subflow_tcp_sock(subflow); + + if (READ_ONCE(ssk->sk_err) || + !skb_queue_empty_lockless(&ssk->sk_error_queue)) { + has_error =3D true; + break; + } + } + mptcp_data_unlock(sk); + + return has_error; +} + static __poll_t mptcp_poll(struct file *file, struct socket *sock, struct poll_table_struct *wait) { @@ -4339,7 +4423,8 @@ static __poll_t mptcp_poll(struct file *file, struct = socket *sock, =20 /* This barrier is coupled with smp_wmb() in __mptcp_error_report() */ smp_rmb(); - if (READ_ONCE(sk->sk_err)) + if (READ_ONCE(sk->sk_err) || mptcp_has_error_queue(sk) || + mptcp_subflow_has_error(sk)) mask |=3D EPOLLERR; =20 return mask; --=20 2.53.0 From nobody Tue May 5 11:27:25 2026 Received: from mail-wr1-f50.google.com (mail-wr1-f50.google.com [209.85.221.50]) (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 A445728980F for ; Tue, 21 Apr 2026 19:13:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776798827; cv=none; b=I/TSj+Cqo8qvnqEQ4usib+V7IGJ/US1hJR9dtBmiCpOV705c5kDc6zlhJdaCmzyKGD9HA7pNeW6kjhA1B+fX5YCWmoIyc8TbPIDARyQFOXPjslPTELMciwVyKC77iOD2e2qA46AlrzVpQuzUhI6bsCjStmNJUxpDGrLT3x6nTnE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776798827; c=relaxed/simple; bh=H8bkG9DBL9Xfb/wm1zVD5wdpScJ3eI851mJ2Uf1xggk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QVcqZ1gRtSOmB7NkcscUkvORxB/SgkqS4sCVLBzLpXJQ5SHbNT4hSnOur6fRpyRMDJPV1UOofvVerGA5Ut7CTNP11B0YY4KguU+zUsOep2lit/gSs54NZpRpT6V9n01ixM8ImQqVmskVyTKLWWpCiWD3YEtyvLStzEk8TUXaSOc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Rivnj8ws; arc=none smtp.client-ip=209.85.221.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Rivnj8ws" Received: by mail-wr1-f50.google.com with SMTP id ffacd0b85a97d-43fe62837baso2837657f8f.3 for ; Tue, 21 Apr 2026 12:13:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776798824; x=1777403624; darn=lists.linux.dev; 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=ApK1G5L+Mc6Cb2sCCSVe+Ii36gaQQarOC5kERgb3MHY=; b=Rivnj8wsJVEd6ZYnDOCSOhQBG8ugamnYiLNfESTR8OL9cjNkRmFglXhpFK5lww+6sj 7ZdZpkWCtcJO1dSLwb1Tz0KP9iXuW20DsPF4YvHTLq9gjL27U/Pc8hBbS7tgcOLIfRmc iJxbDxwuC9wpDCuPKGl7wCvyxUNCpoGq/i8RqEwzKaLLKf54XGLnhe9mU3RMfUtTIFi8 NLNFzIIMKaN2hX2EoMZAw7QMUSHIFWoX+wwFVnkOyi7Wt9qmsm0O27rSUOIbFV6Z/svx o+gNOy77vXgncf7guW11ukvAP9EJMT+TcuD1akyVZP1xHKlWDGVyJ4+aKBjw/nbbdZUT 3VpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776798824; x=1777403624; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=ApK1G5L+Mc6Cb2sCCSVe+Ii36gaQQarOC5kERgb3MHY=; b=NSyiOtuGFSrp6Ca109J99J5yMksqRSsjy5QoF9lNMwl99q/SikiJqToyynax5Meuc6 NfNw40HjfUJH/8aJbJqFXCNQQZnzVNq/mkIkBCNN7n3os1NoehEYHk/iRAXmDBbpAqXw fpAOfEfhn4jclXMcgialYWjHS7ohywtn7oOmbeDl711OiFeH/TbmpyLSJviZa+KZPlXT 5TGPk7zugMACLyPuvRaWp7gnQfrmiw4TrwFlLvD5JDutVx4SgM/YZcOCR+fck6T5f284 gGlcG50nTWN6QMZa68/0b2HRpo51V5xBpUhjtv/2k281qhIJDGxqNYZGTSaEGfMPbWdL NlSA== X-Gm-Message-State: AOJu0YzEADWLy985zL2fvHOiBZ7WkEeUOmCId06QDabD0VqepOO2VrxR S5XNJ9d8XlG4Q4ArNYY5IiMfnI4YR++o6rPVqeL1d3KF7NmG9fKH9256+0s7k/1NLbk= X-Gm-Gg: AeBDies/f7A27UTSZouep2WVBwvWBxvZhUf9oM0PJHQlDa1fn5yI5rGAvIhzKMK0iAv LZ9tm8QBWkW201UKafjnd3ZahSHDFWA2EAqdAd3cUTjPHRuoW37YbkJ2pmfaefzqnZGhF7hzvlu sUZ3RglvfTu+yTUixjDqBTS/7iSQnpveAoX2XlGNJ3ZGrd/DQEWm/dG/e1i7CKwHC8frYawUyHX /R+JMmcW7FEvY7lrzcvbmsPeD/CNTQsCT0NhVRC+uUZ2dzNRS2FAzMqA4jl4gif+TWEDrMx1E5v AtvJ1AWzUGz5DNdOdhXKvV0HTD82A/4xGiuUqDSC3rgXsy/MJA865b4OXuVgJ6uAuFum3ZVA+Si huM7Hpgyjh5jh772YDP7AX15qhRG5/hnN4Pv5G+O1ZIKjo+9tst5r/UlFwF6i5lPTrMOu3p0VL1 9GuU3pD0oV+Zdjx+fmk8duuJDoja7YeDg64Icrix1lgOf5Z8LApW1EjsIHi3mskEYToem8pAhSv jsRv1kn4dC6eBb9qOGFhJ1MWKEL40PT X-Received: by 2002:a05:6000:d81:b0:43f:e413:43ae with SMTP id ffacd0b85a97d-43fe41343cbmr19332130f8f.0.1776798823809; Tue, 21 Apr 2026 12:13:43 -0700 (PDT) Received: from dohko.chello.ie (188-141-5-72.dynamic.upc.ie. [188.141.5.72]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43fe4e4d112sm43859447f8f.29.2026.04.21.12.13.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Apr 2026 12:13:43 -0700 (PDT) From: David Carlier To: mptcp@lists.linux.dev Cc: Matthieu Baerts , Mat Martineau , Geliang Tang , David Carlier Subject: [PATCH mptcp-next v2 3/3] selftests: mptcp: cover RECVERR and MSG_ERRQUEUE Date: Tue, 21 Apr 2026 20:13:37 +0100 Message-ID: <20260421191337.58341-4-devnexen@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260421191337.58341-1-devnexen@gmail.com> References: <20260421191337.58341-1-devnexen@gmail.com> Precedence: bulk X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add MPTCP selftest coverage for RECVERR sockopt round-trips and parent-socket MSG_ERRQUEUE delivery. Enable TX software timestamping, send data over an MPTCP socket, wait for POLLERR, and verify that recvmsg(MSG_ERRQUEUE) returns timestamping metadata on the MPTCP parent socket. Signed-off-by: David Carlier Assisted-by: Codex:gpt-5 --- .../selftests/net/mptcp/mptcp_sockopt.c | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/test= ing/selftests/net/mptcp/mptcp_sockopt.c index b6e58d936ebe..b499e7585d38 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -26,9 +26,17 @@ =20 #include #include +#include +#include + +#include =20 static int pf =3D AF_INET; =20 +#ifndef SCM_TIMESTAMPING +#define SCM_TIMESTAMPING SO_TIMESTAMPING +#endif + #ifndef IPPROTO_MPTCP #define IPPROTO_MPTCP 262 #endif @@ -128,6 +136,9 @@ struct so_state { #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif =20 +static void enable_tx_timestamping(int fd); +static void test_msg_errqueue_timestamping(int fd); + static void __noreturn die_perror(const char *msg) { perror(msg); @@ -598,6 +609,8 @@ static void connect_one_server(int fd, int pipefd) =20 assert(strncmp(buf2, "xmit", 4) =3D=3D 0); =20 + enable_tx_timestamping(fd); + ret =3D write(fd, buf, len); if (ret < 0) die_perror("write"); @@ -605,6 +618,8 @@ static void connect_one_server(int fd, int pipefd) if (ret !=3D (ssize_t)len) xerror("short write"); =20 + test_msg_errqueue_timestamping(fd); + total =3D 0; do { ret =3D read(fd, buf2 + total, sizeof(buf2) - total); @@ -769,6 +784,142 @@ static void test_ip_tos_sockopt(int fd) xerror("expect socklen_t =3D=3D -1"); } =20 +static void test_ip_recverr_sockopt(int fd) +{ + struct iovec iov =3D { + .iov_base =3D &(char){ 0 }, + .iov_len =3D 1, + }; + struct msghdr msg =3D { + .msg_iov =3D &iov, + .msg_iovlen =3D 1, + }; + int one =3D 1, zero =3D 0, val =3D -1; + socklen_t s =3D sizeof(val); + int level, optname, r; + + switch (pf) { + case AF_INET: + level =3D SOL_IP; + optname =3D IP_RECVERR; + break; + case AF_INET6: + level =3D SOL_IPV6; + optname =3D IPV6_RECVERR; + break; + default: + xerror("Unknown pf %d\n", pf); + } + + r =3D setsockopt(fd, level, optname, &one, sizeof(one)); + if (r) + die_perror("setsockopt recverr on"); + + r =3D getsockopt(fd, level, optname, &val, &s); + if (r) + die_perror("getsockopt recverr on"); + if (s !=3D sizeof(val) || val !=3D one) + xerror("recverr on mismatch val=3D%d len=3D%u", val, s); + + r =3D recvmsg(fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT); + if (r !=3D -1 || errno !=3D EAGAIN) + xerror("expected empty errqueue to return EAGAIN, ret=3D%d errno=3D%d", = r, errno); + + r =3D setsockopt(fd, level, optname, &zero, sizeof(zero)); + if (r) + die_perror("setsockopt recverr off"); + + val =3D -1; + s =3D sizeof(val); + r =3D getsockopt(fd, level, optname, &val, &s); + if (r) + die_perror("getsockopt recverr off"); + if (s !=3D sizeof(val) || val !=3D zero) + xerror("recverr off mismatch val=3D%d len=3D%u", val, s); +} + +static void enable_tx_timestamping(int fd) +{ + int val =3D SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_OPT_TSONLY; + int ret; + + ret =3D setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING_OLD, + &val, sizeof(val)); + if (ret) + die_perror("setsockopt SO_TIMESTAMPING"); +} + +static void test_msg_errqueue_timestamping(int fd) +{ + char ctrl[512] =3D { 0 }; + char data[32] =3D { 0 }; + struct iovec iov =3D { + .iov_base =3D data, + .iov_len =3D sizeof(data), + }; + struct msghdr msg =3D { + .msg_iov =3D &iov, + .msg_iovlen =3D 1, + .msg_control =3D ctrl, + .msg_controllen =3D sizeof(ctrl), + }; + struct pollfd pfd =3D { + .fd =3D fd, + .events =3D POLLERR, + }; + struct cmsghdr *cm; + struct scm_timestamping *tss =3D NULL; + struct sock_extended_err *serr =3D NULL; + int ret, i; + + for (i =3D 0; i < 10; i++) { + ret =3D poll(&pfd, 1, 1000); + if (ret < 0) + die_perror("poll errqueue"); + if (ret =3D=3D 0) + continue; + if (!(pfd.revents & POLLERR)) + xerror("expected POLLERR, got revents %#x", pfd.revents); + break; + } + + if (i =3D=3D 10) + xerror("timed out waiting for MSG_ERRQUEUE event"); + + ret =3D recvmsg(fd, &msg, MSG_ERRQUEUE); + if (ret < 0) + die_perror("recvmsg timestamping errqueue"); + if (!(msg.msg_flags & MSG_ERRQUEUE)) + xerror("expected MSG_ERRQUEUE in msg_flags, got %#x", + msg.msg_flags); + + for (cm =3D CMSG_FIRSTHDR(&msg); cm; cm =3D CMSG_NXTHDR(&msg, cm)) { + if (cm->cmsg_level =3D=3D SOL_SOCKET && + cm->cmsg_type =3D=3D SCM_TIMESTAMPING) + tss =3D (void *)CMSG_DATA(cm); + if ((cm->cmsg_level =3D=3D SOL_IP && + cm->cmsg_type =3D=3D IP_RECVERR) || + (cm->cmsg_level =3D=3D SOL_IPV6 && + cm->cmsg_type =3D=3D IPV6_RECVERR)) + serr =3D (void *)CMSG_DATA(cm); + } + + if (!tss) + xerror("missing SCM_TIMESTAMPING cmsg"); + if (!serr) + xerror("missing sock_extended_err cmsg"); + if (serr->ee_errno !=3D ENOMSG || + serr->ee_origin !=3D SO_EE_ORIGIN_TIMESTAMPING) + xerror("unexpected timestamping err ee_errno=3D%u ee_origin=3D%u", + serr->ee_errno, serr->ee_origin); + if (!tss->ts[0].tv_sec && !tss->ts[0].tv_nsec && + !tss->ts[1].tv_sec && !tss->ts[1].tv_nsec && + !tss->ts[2].tv_sec && !tss->ts[2].tv_nsec) + xerror("all timestamp slots are zero"); +} + static int client(int pipefd) { int fd =3D -1; @@ -787,6 +938,7 @@ static int client(int pipefd) } =20 test_ip_tos_sockopt(fd); + test_ip_recverr_sockopt(fd); =20 connect_one_server(fd, pipefd); =20 --=20 2.53.0