From nobody Sun Apr 5 16:22:39 2026 Received: from mail-pj1-f54.google.com (mail-pj1-f54.google.com [209.85.216.54]) (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 26FF538A710 for ; Mon, 30 Mar 2026 19:17:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774898225; cv=none; b=QuLugxg29jw1zUPTFhVWZL9HF2F0O/Q1mgjwumMl3QXSSCj/uy+JpO+qKAFHwBBwcqVIoMsAi6mEXhjwVwcrIxYNm9JNovUkD08la7zgjUPOP9iM97mMLAGBDPAW6RF902HHz6AgG1cxxj1j754D25mpUbbvtq6g6PfqSB2NozA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774898225; c=relaxed/simple; bh=cT/13qstn8/nOhBjQXRq0PqqXbdhTgppzUWbgSGQO70=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tcC+eOhkYs14K1JczvNzlQCVuDsLX+sQGRFFcCzN1sI3tF5m9b/XyAcQ8U1QCJcSyK94iaUUMY0aXNSxOC8gYuUvcleNIk1RsJMWBoe2H+mAQC9BN3OdHq+gMNSCQZa0UQ8juXArIdYKVEKAH8fODVWuQdq3l8Q8rvfX2+83VcM= 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=d8aaPRcY; arc=none smtp.client-ip=209.85.216.54 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="d8aaPRcY" Received: by mail-pj1-f54.google.com with SMTP id 98e67ed59e1d1-35d9f68d011so1219156a91.2 for ; Mon, 30 Mar 2026 12:17:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774898222; x=1775503022; darn=vger.kernel.org; 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=5f2zrKZrRaGjASZcWc6dPmrf+99AFfJekwpd+WSzFDo=; b=d8aaPRcYZSX7vySqHaQcFAOVSDfstKn2CX6UHyPseriRxIDyX9/Y93+rJWIWVawSxV +sQpksv8RIa/yE5NuYRlq5XRqQb+QTAfTHiKu7x4Cc8R0Nk64lxPTyaJCbMFKOC/C1cr NLKWHEBc/SqahVRsExjxPCTpnq2Ek3LSx4KSzohpB/AIzZmUFxgfLbtjnjf1+7p7jD6i Y/TbsfLvj90l/dKQzUubJEsLXh2E8XfBalxK/3LiuBwHDs8S+3S2PvEkalI/WPDvTRmd PzmEbOrDl0HnVLOx/5vPzcHyeEVk/GxxGOABa/Xw6byIWOVHrz3FB8FFvEFtUJRNRdx9 rg/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774898222; x=1775503022; 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=5f2zrKZrRaGjASZcWc6dPmrf+99AFfJekwpd+WSzFDo=; b=nnywv3RCEklYuDd7xIZY9Uhh89NFIHXO14+/mimrKiw7jY6vb5TcCaXqTqCwTJt67o GGBQtmvwLOh/7vS7lMmW5Ujz0sWpGnCxF5dOcTwt+FmKTEAPLNfsSPkBxE1yA6OJcwSB LA/hy/ik5J1x+g7WjJhmClW0PTVmiR6GZQa49TNYihnos+R2Ngcc4zuJfy7EMMeAPTVZ YnxJlw1YW8srd1rSOUPmJLaCTV9T8Ys1A/nVH/NepWFwX5YmnYWWIJg042hCVvSifp6k vmEyEXMhaQlBb8fWgSecLMlqJMN6RW1o32ybzKFd/+SmfHtfjMYFh2Z6IaEEm2eLlyr2 zIWA== X-Forwarded-Encrypted: i=1; AJvYcCWngZKfvvSO55p2YrQ2EulyTVMmPCodid5cAf0+uW7w5Yk/igA+4ceI7gfX1GrSk0MgLAEkk9nx3/ZegAs=@vger.kernel.org X-Gm-Message-State: AOJu0YyV02bowEc0a9jRIdraw0h2Cbu5r3siEWcEQ565fpzT/SEWQER5 i06glhJyCxycGeDZ9LKxS3/6kwzssLLvvrgOmhFXiXDm/W2G+gzEEqDV X-Gm-Gg: ATEYQzy8DKBQweZ1yNRr4VM/uuiJyCciaQzmJdHXlcw1GME8B5IxVQnPRnTYXp+K7cR JxjlipT1LYeOY95iAfzUo1/rTm2+s80SkDaiohls+LO1Rt2T/SiqA9dxGdauv8jCMn2RnjVxMq0 0/VyAykHbG5Ocj0j73Rp+GaW0H77ZgkLIX5wstwzSfKhYmFt6bd9Z5MkEedWlRrJdIhBRPz+Ro0 VoOwMEcjA+DFGZXtStO5yyxmQ59J8I6CCN1i5v5RduL1oI4KlsF7qs7X4+ce+SdXa+FhLkQTg3/ r+TFh7Txe0BHfZ5t6xxVrjBMVjgfJDrCRLvGCDDqF3zZG8NH9YAC+ECJBVww6ZCZYL4HsWF00mh UsoVnmAxo5SntMmxeA9Ojlx45w/jHDAgVygBgKcSuyqd+QK8lolxsCbK3A9rwy55FBBiQ4h1uyk QUozAKp+S8VWCNSS0N+ISbsZMcavb/NQfM+IpQT09W X-Received: by 2002:a17:90b:3cce:b0:336:b60f:3936 with SMTP id 98e67ed59e1d1-35c3000c6f0mr12496361a91.12.1774898222332; Mon, 30 Mar 2026 12:17:02 -0700 (PDT) Received: from DESKTOP-82PPU4A.localdomain ([202.8.116.192]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-35d950d9b12sm9627178a91.17.2026.03.30.12.16.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Mar 2026 12:17:01 -0700 (PDT) From: Ujjal Roy To: "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Nikolay Aleksandrov , Ido Schimmel , David Ahern , Shuah Khan , Andy Roulin , Yong Wang , Petr Machata Cc: Ujjal Roy , bridge@lists.linux.dev, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH net-next v2 5/5] selftests: net: bridge: add tests for igmpv3 MRC and QQIC validation Date: Mon, 30 Mar 2026 19:16:11 +0000 Message-ID: <20260330191611.16929-6-royujjal@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260330191611.16929-1-royujjal@gmail.com> References: <20260330191611.16929-1-royujjal@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add bridge selftests that configure IGMPv3 parameters and validate the resulting Query packet fields for Max Resp Code (MRC) and Querier Query Interval Code (QQIC). This also adds helper binary to encode floating-point exponential fields. Future extensions may cover corresponding IPv6 cases. Signed-off-by: Ujjal Roy --- .../selftests/net/forwarding/.gitignore | 2 + .../testing/selftests/net/forwarding/Makefile | 10 ++ .../selftests/net/forwarding/bridge_igmp.sh | 109 ++++++++++++++++++ .../selftests/net/forwarding/mc_decode.c | 38 ++++++ .../selftests/net/forwarding/mc_encode.c | 40 +++++++ 5 files changed, 199 insertions(+) create mode 100644 tools/testing/selftests/net/forwarding/mc_decode.c create mode 100644 tools/testing/selftests/net/forwarding/mc_encode.c diff --git a/tools/testing/selftests/net/forwarding/.gitignore b/tools/test= ing/selftests/net/forwarding/.gitignore index 418ff96c52ef..aa0c7f1afb4b 100644 --- a/tools/testing/selftests/net/forwarding/.gitignore +++ b/tools/testing/selftests/net/forwarding/.gitignore @@ -1,3 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only forwarding.config ipmr +mc_encode +mc_decode diff --git a/tools/testing/selftests/net/forwarding/Makefile b/tools/testin= g/selftests/net/forwarding/Makefile index bbaf4d937dd8..a26da846632d 100644 --- a/tools/testing/selftests/net/forwarding/Makefile +++ b/tools/testing/selftests/net/forwarding/Makefile @@ -1,5 +1,15 @@ # SPDX-License-Identifier: GPL-2.0+ OR MIT =20 +top_srcdir =3D ../../../../.. + +CFLAGS +=3D -Wall -Wl,--no-as-needed -O2 -g -I$(top_srcdir)/usr/include $(= KHDR_INCLUDES) +CFLAGS +=3D -I$(top_srcdir)/tools/include + +TEST_GEN_FILES :=3D \ + mc_encode \ + mc_decode \ +# end of TEST_GEN_FILES + TEST_PROGS :=3D \ bridge_activity_notify.sh \ bridge_fdb_learning_limit.sh \ diff --git a/tools/testing/selftests/net/forwarding/bridge_igmp.sh b/tools/= testing/selftests/net/forwarding/bridge_igmp.sh index d4e7dd659354..9841c4e4eca0 100755 --- a/tools/testing/selftests/net/forwarding/bridge_igmp.sh +++ b/tools/testing/selftests/net/forwarding/bridge_igmp.sh @@ -2,6 +2,8 @@ # SPDX-License-Identifier: GPL-2.0 =20 ALL_TESTS=3D" + v3query_mrc_test + v3query_qqic_test v2reportleave_test v3include_test v3inc_allow_test @@ -84,6 +86,7 @@ switch_destroy() { ip link set dev $swp2 down ip link set dev $swp1 down + ip link set dev br0 down =20 ip link del dev br0 } @@ -116,6 +119,112 @@ cleanup() vrf_cleanup } =20 +check_binary() +{ + local cmd=3D$1; shift + local args=3D$@ + + if [[ ! -x "$(command -v "$cmd")" ]]; then + log_test_skip "$args $cmd not found" + return $EXIT_STATUS + fi +} + +tcpdump_show_with_filter() +{ + local if_name=3D$1; shift + local filter=3D$@ + + tcpdump -e -n -r ${capfile[$if_name]} "$filter" 2>&1 +} + +validate_query() +{ + local if_name=3D$1; shift + local test=3D"$1"; shift + local value=3D"$1"; shift + local pattern=3D"" + local field_val=3D"" + local pos=3D"" + local msg=3D"IGMPv3 query: verify $test" + check_command tshark || return 1 + check_binary "./mc_encode" $msg || return 1 + + if [ "$test" =3D "MRC" ]; then + pos=3D1 # MRC field offset within IGMP header + field_val=3D$(tshark -r ${capfile[$if_name]} -Y "igmp.type=3D=3D0x11" \ + -V 2>/dev/null | grep "Max Resp Time") + elif [ "$test" =3D "QQIC" ]; then + pos=3D9 # QQIC field offset within IGMP header + field_val=3D$(tshark -r ${capfile[$if_name]} -Y "igmp.type=3D=3D0x11" \ + -V 2>/dev/null | grep "QQIC") + fi + + local enc_val=3D$(./mc_encode $value) + pattern=3D"ip proto 2 and igmp[0] =3D=3D 0x11 and igmp[$pos] =3D=3D $enc_= val" + local opt_str=3D"" + tcpdump_show_with_filter $if_name $pattern | grep -q "igmp query v3" + ret=3D$? + if [ "$field_val" !=3D "" -a $ret -ne 0 ]; then + opt_str=3D"Bad $test value in IGMP packet: $field_val" + fi + check_err $ret "$opt_str" + + log_test "$msg" "configured=3D$value, expected=3D$enc_val" +} + +v3query_mrc_test() +{ + RET=3D0 + local qri_val=3D160 + local br_qri=3D$((qri_val*10)) + + # Set MRT to validate + ip link set dev br0 type bridge mcast_query_interval 12500 \ + mcast_query_response_interval $br_qri \ + mcast_igmp_version 3 + check_err $? "IGMPv3 QUERY bridge configuration failed" + + ip link set dev br0 down + tcpdump_start $h2 + ip link set dev br0 up + sleep 2 + tcpdump_stop $h2 + + validate_query $h2 "MRC" $qri_val + tcpdump_cleanup $h2 + + ip link set dev br0 type bridge mcast_query_interval 12500 \ + mcast_query_response_interval 1000 \ + mcast_igmp_version 2 +} + +v3query_qqic_test() +{ + RET=3D0 + local qqi_val=3D160 + local br_qqi=3D$((qqi_val*100)) + + # Set QQIC to validate + ip link set dev br0 type bridge mcast_query_interval $br_qqi \ + mcast_query_response_interval 1000 \ + mcast_igmp_version 3 + check_err $? "IGMPv3 QUERY bridge configuration failed" + + ip link set dev br0 down + tcpdump_start $h2 + ip link set dev br0 up + sleep 2 + tcpdump_stop $h2 + + validate_query $h2 "QQIC" $qqi_val + tcpdump_cleanup $h2 + + ip link set dev br0 type bridge mcast_query_interval 12500 \ + mcast_query_response_interval 1000 \ + mcast_igmp_version 2 +} + v2reportleave_test() { RET=3D0 diff --git a/tools/testing/selftests/net/forwarding/mc_decode.c b/tools/tes= ting/selftests/net/forwarding/mc_decode.c new file mode 100644 index 000000000000..5b626101497d --- /dev/null +++ b/tools/testing/selftests/net/forwarding/mc_decode.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +#define IGMPV3_FP_EXP(value) (((value) >> 4) & 0x07) +#define IGMPV3_FP_MAN(value) ((value) & 0x0f) + +/* IGMPV3 floating-point exponential field threshold */ +#define IGMPV3_EXP_MIN_THRESHOLD 128 + +static inline unsigned long decode_field(const u8 code) +{ + /* RFC3376, relevant sections: + * - 4.1.1. Maximum Response Code + * - 4.1.7. QQIC (Querier's Query Interval Code) + */ + if (code < IGMPV3_EXP_MIN_THRESHOLD) { + return (unsigned long)code; + } else { + unsigned long mc_man, mc_exp; + mc_exp =3D IGMPV3_FP_EXP(code); + mc_man =3D IGMPV3_FP_MAN(code); + return ((mc_man | 0x10) << (mc_exp + 3)); + } +} + +int main(int argc, char *argv[]) +{ + uint8_t qqic =3D 0; + if (argc >=3D 2) + qqic =3D atoi(argv[1]); + unsigned long qqi =3D decode_field(qqic); + + printf("%lu\n", qqi); + + return 0; +} diff --git a/tools/testing/selftests/net/forwarding/mc_encode.c b/tools/tes= ting/selftests/net/forwarding/mc_encode.c new file mode 100644 index 000000000000..a2183b864be4 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/mc_encode.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +/* IGMPV3 floating-point exponential field threshold */ +#define IGMPV3_EXP_MIN_THRESHOLD 128 +/* Max representable (mant =3D 0xF, exp =3D 7) -> 31744 */ +#define IGMPV3_EXP_MAX_THRESHOLD 31744 + +static inline uint8_t encode_field(unsigned int value) +{ + uint8_t mc_exp, mc_man; + + /* RFC3376: QQIC/MRC < 128 is literal */ + if (value < IGMPV3_EXP_MIN_THRESHOLD) + return (uint8_t)value; + + /* Saturate at max representable (mant =3D 0xF, exp =3D 7) -> 31744 */ + if (value >=3D IGMPV3_EXP_MAX_THRESHOLD) + return 0xFF; + + mc_exp =3D (uint8_t)(fls(value) - 8); + mc_man =3D (uint8_t)((value >> (mc_exp + 3)) & 0x0F); + + return 0x80 | (mc_exp << 4) | mc_man; +} + +int main(int argc, char *argv[]) +{ + unsigned int qqi =3D 0; + if (argc >=3D 2) + qqi =3D atoi(argv[1]); + + uint8_t qqic =3D encode_field(qqi); + + printf("%u\n", qqic); + + return 0; +} --=20 2.43.0