public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] net: bridge: mcast: add multicast exponential field encoding
@ 2026-03-26 15:07 Ujjal Roy
  2026-03-26 15:07 ` [PATCH 1/4] ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation Ujjal Roy
                   ` (4 more replies)
  0 siblings, 5 replies; 26+ messages in thread
From: Ujjal Roy @ 2026-03-26 15:07 UTC (permalink / raw)
  To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, Ido Schimmel, David Ahern
  Cc: Ujjal Roy, bridge, netdev, linux-kernel

Description:
This series addresses a mismatch in how multicast query
intervals and response codes are handled across IPv4 (IGMPv3)
and IPv6 (MLDv2). While decoding logic currently exists,
the corresponding encoding logic is missing during query
packet generation. This leads to incorrect intervals being
transmitted when values exceed their linear thresholds.

The patches introduce a unified floating-point encoding
approach based on RFC3376 and RFC3810, ensuring that large
intervals are correctly represented in QQIC and MRC fields
using the exponent-mantissa format.

Key Changes:
* ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation
  Removes legacy macros in favor of a cleaner, unified
  calculation for retrieving intervals from encoded fields,
  improving code maintainability.

* ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
  Standardizes MLDv2 terminology by renaming mldv2_mrc()
  to mldv2_mrd() (Maximum Response Delay) and introducing
  a new API mldv2_qqi for QQI calculation, improving code
  readability.

* ipv4: igmp: encode multicast exponential fields
  Introduces the logic to dynamically calculate the exponent
  and mantissa using bit-scan (fls). This ensures QQIC and
  MRC fields (8-bit) are properly encoded when transmitting
  query packets with intervals that exceed their respective
  linear threshold value of 128 (for QQI/MRT).

* ipv6: mld: encode multicast exponential fields
  Applies similar encoding logic for MLDv2. This ensures
  QQIC (8-bit) and MRC (16-bit) fields are properly encoded
  when transmitting query packets with intervals that exceed
  their respective linear thresholds (128 for QQI; 32768
  for MRD).

Impact:
These changes ensure that multicast queriers and listeners
stay synchronized on timing intervals, preventing protocol
timeouts or premature group membership expiration caused
by incorrectly formatted packet headers.

Ujjal Roy (4):
  ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation
  ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
  ipv4: igmp: encode multicast exponential fields
  ipv6: mld: encode multicast exponential fields

 include/linux/igmp.h      | 158 +++++++++++++++++++++++++++++++--
 include/net/mld.h         | 179 ++++++++++++++++++++++++++++++++++++--
 net/bridge/br_multicast.c |  22 +++--
 net/ipv4/igmp.c           |   6 +-
 net/ipv6/mcast.c          |  19 +---
 5 files changed, 336 insertions(+), 48 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 1/4] ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation
  2026-03-26 15:07 [PATCH 0/4] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
@ 2026-03-26 15:07 ` Ujjal Roy
  2026-03-26 15:07 ` [PATCH 2/4] ipv6: mld: rename mldv2_mrc() and add mldv2_qqi() Ujjal Roy
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 26+ messages in thread
From: Ujjal Roy @ 2026-03-26 15:07 UTC (permalink / raw)
  To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, Ido Schimmel, David Ahern
  Cc: Ujjal Roy, bridge, netdev, linux-kernel

Get rid of the IGMPV3_MRC macro and use the igmpv3_mrt() API to
calculate the Max Resp Time from the Maximum Response Code.

Similarly, for IGMPV3_QQIC, use the igmpv3_qqi() API to calculate
the Querier's Query Interval from the QQIC field.

Signed-off-by: Ujjal Roy <royujjal@gmail.com>
---
 include/linux/igmp.h      | 78 +++++++++++++++++++++++++++++++++++----
 net/bridge/br_multicast.c |  2 +-
 net/ipv4/igmp.c           |  6 +--
 3 files changed, 74 insertions(+), 12 deletions(-)

diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index 073b30a9b850..3c12c0a63492 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -92,15 +92,77 @@ struct ip_mc_list {
 	struct rcu_head		rcu;
 };
 
+/* RFC3376, relevant sections:
+ *  - 4.1.1. Maximum Response Code
+ *  - 4.1.7. QQIC (Querier's Query Interval Code)
+ *
+ * If Max Resp Code >= 128, Max Resp Code represents a floating-point
+ * value as follows:
+ * If QQIC >= 128, QQIC represents a floating-point value as follows:
+ *
+ *  0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+
+ * |1| exp | mant  |
+ * +-+-+-+-+-+-+-+-+
+ */
+#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
+
 /* V3 exponential field decoding */
-#define IGMPV3_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value))
-#define IGMPV3_EXP(thresh, nbmant, nbexp, value) \
-	((value) < (thresh) ? (value) : \
-        ((IGMPV3_MASK(value, nbmant) | (1<<(nbmant))) << \
-         (IGMPV3_MASK((value) >> (nbmant), nbexp) + (nbexp))))
-
-#define IGMPV3_QQIC(value) IGMPV3_EXP(0x80, 4, 3, value)
-#define IGMPV3_MRC(value) IGMPV3_EXP(0x80, 4, 3, value)
+
+/*
+ * IGMPv3 QQI/MRT 8-bit exponential field decode.
+ *
+ * RFC3376, 4.1.1 & 4.1.7. defines the decoding formula:
+ *      0 1 2 3 4 5 6 7
+ *     +-+-+-+-+-+-+-+-+
+ *     |1| exp | mant  |
+ *     +-+-+-+-+-+-+-+-+
+ * Max Resp Time = (mant | 0x10) << (exp + 3)
+ * QQI = (mant | 0x10) << (exp + 3)
+ */
+static inline unsigned long igmpv3_exp_field_decode(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 = IGMPV3_FP_EXP(code);
+		mc_man = IGMPV3_FP_MAN(code);
+
+		return ((mc_man | 0x10) << (mc_exp + 3));
+	}
+}
+
+/* Calculate Max Resp Time from Maximum Response Code */
+static inline unsigned long igmpv3_mrt(const struct igmpv3_query *ih3)
+{
+	/* RFC3376, relevant sections:
+	 *  - 4.1.1. Maximum Response Code
+	 *  - 8.3. Query Response Interval
+	 */
+	return igmpv3_exp_field_decode(ih3->code);
+}
+
+/* Calculate Querier's Query Interval from Querier's Query Interval Code */
+static inline unsigned long igmpv3_qqi(const struct igmpv3_query *ih3)
+{
+	/* RFC3376, relevant sections:
+	 *  - 4.1.7. QQIC (Querier's Query Interval Code)
+	 *  - 8.2. Query Interval
+	 *  - 8.12. Older Version Querier Present Timeout
+	 *    (the [Query Interval] in the last Query received)
+	 */
+	return igmpv3_exp_field_decode(ih3->qqic);
+}
 
 static inline int ip_mc_may_pull(struct sk_buff *skb, unsigned int len)
 {
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 881d866d687a..9fec76e887bc 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -3518,7 +3518,7 @@ static void br_ip4_multicast_query(struct net_bridge_mcast *brmctx,
 			goto out;
 
 		max_delay = ih3->code ?
-			    IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
+			    igmpv3_mrt(ih3) * (HZ / IGMP_TIMER_SCALE) : 1;
 	} else {
 		goto out;
 	}
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index a674fb44ec25..8c6102737096 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -991,7 +991,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
 		 * different encoding. We use the v3 encoding as more likely
 		 * to be intended in a v3 query.
 		 */
-		max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
+		max_delay = igmpv3_mrt(ih3)*(HZ/IGMP_TIMER_SCALE);
 		if (!max_delay)
 			max_delay = 1;	/* can't mod w/ 0 */
 	} else { /* v3 */
@@ -1006,7 +1006,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
 			ih3 = igmpv3_query_hdr(skb);
 		}
 
-		max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
+		max_delay = igmpv3_mrt(ih3)*(HZ/IGMP_TIMER_SCALE);
 		if (!max_delay)
 			max_delay = 1;	/* can't mod w/ 0 */
 		WRITE_ONCE(in_dev->mr_maxdelay, max_delay);
@@ -1016,7 +1016,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
 		 * configured value.
 		 */
 		in_dev->mr_qrv = ih3->qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
-		in_dev->mr_qi = IGMPV3_QQIC(ih3->qqic)*HZ ?: IGMP_QUERY_INTERVAL;
+		in_dev->mr_qi = igmpv3_qqi(ih3)*HZ ?: IGMP_QUERY_INTERVAL;
 
 		/* RFC3376, 8.3. Query Response Interval:
 		 * The number of seconds represented by the [Query Response
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 2/4] ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
  2026-03-26 15:07 [PATCH 0/4] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
  2026-03-26 15:07 ` [PATCH 1/4] ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation Ujjal Roy
@ 2026-03-26 15:07 ` Ujjal Roy
  2026-03-27 15:49   ` kernel test robot
  2026-03-26 15:07 ` [PATCH 3/4] ipv4: igmp: encode multicast exponential fields Ujjal Roy
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 26+ messages in thread
From: Ujjal Roy @ 2026-03-26 15:07 UTC (permalink / raw)
  To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, Ido Schimmel, David Ahern
  Cc: Ujjal Roy, bridge, netdev, linux-kernel

Rename mldv2_mrc() to mldv2_mrd() as it used to calculate
the Maximum Response Delay from the Maximum Response Code.

Introduce a new API mldv2_qqi() to define the existing
calculation logic of QQI from QQIC. This also organizes
the existing mld_update_qi() API.

Added by e3f5b1704 ("net: ipv6: mld: get rid of MLDV2_MRC and simplify calculation").

Signed-off-by: Ujjal Roy <royujjal@gmail.com>
---
 include/net/mld.h         | 64 +++++++++++++++++++++++++++++++++------
 net/bridge/br_multicast.c |  2 +-
 net/ipv6/mcast.c          | 19 ++----------
 3 files changed, 58 insertions(+), 27 deletions(-)

diff --git a/include/net/mld.h b/include/net/mld.h
index c07359808493..6373eb76efe5 100644
--- a/include/net/mld.h
+++ b/include/net/mld.h
@@ -89,29 +89,73 @@ struct mld2_query {
 #define MLDV2_QQIC_EXP(value)	(((value) >> 4) & 0x07)
 #define MLDV2_QQIC_MAN(value)	((value) & 0x0f)
 
-#define MLD_EXP_MIN_LIMIT	32768UL
-#define MLDV1_MRD_MAX_COMPAT	(MLD_EXP_MIN_LIMIT - 1)
+#define MLD_QQIC_MIN_THRESHOLD	128
+#define MLD_MRC_MIN_THRESHOLD	32768UL
+#define MLDV1_MRD_MAX_COMPAT	(MLD_MRC_MIN_THRESHOLD - 1)
 
 #define MLD_MAX_QUEUE		8
 #define MLD_MAX_SKBS		32
 
-static inline unsigned long mldv2_mrc(const struct mld2_query *mlh2)
-{
-	/* RFC3810, 5.1.3. Maximum Response Code */
-	unsigned long ret, mc_mrc = ntohs(mlh2->mld2q_mrc);
+/* V2 exponential field decoding */
 
-	if (mc_mrc < MLD_EXP_MIN_LIMIT) {
-		ret = mc_mrc;
+/* Calculate Maximum Response Delay from Maximum Response Code
+ *
+ * RFC3810, 5.1.3. defines the decoding formula:
+ *      0 1 2 3 4 5 6 7 8 9 A B C D E F
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *     |1| exp |          mant         |
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Maximum Response Delay = (mant | 0x1000) << (exp+3)
+ */
+static inline unsigned long mldv2_mrd(const struct mld2_query *mlh2)
+{
+	/* RFC3810, relevant sections:
+	 *  - 5.1.3. Maximum Response Code
+	 *  - 9.3. Query Response Interval
+	 */
+	unsigned long mc_mrc = ntohs(mlh2->mld2q_mrc);
+
+	if (mc_mrc < MLD_MRC_MIN_THRESHOLD) {
+		return mc_mrc;
 	} else {
 		unsigned long mc_man, mc_exp;
 
 		mc_exp = MLDV2_MRC_EXP(mc_mrc);
 		mc_man = MLDV2_MRC_MAN(mc_mrc);
 
-		ret = (mc_man | 0x1000) << (mc_exp + 3);
+		return ((mc_man | 0x1000) << (mc_exp + 3));
 	}
+}
 
-	return ret;
+/* Calculate Querier's Query Interval from Querier's Query Interval Code
+ *
+ * RFC3810, 5.1.9. defines the decoding formula:
+ *      0 1 2 3 4 5 6 7
+ *     +-+-+-+-+-+-+-+-+
+ *     |1| exp | mant  |
+ *     +-+-+-+-+-+-+-+-+
+ * QQI = (mant | 0x10) << (exp + 3)
+ */
+static inline unsigned long mldv2_qqi(const struct mld2_query *mlh2)
+{
+	/* RFC3810, relevant sections:
+	 *  - 5.1.9. QQIC (Querier's Query Interval Code)
+	 *  - 9.2. Query Interval
+	 *  - 9.12. Older Version Querier Present Timeout
+	 *    (the [Query Interval] in the last Query received)
+	 */
+	unsigned long qqic = ntohs(mlh2->mld2q_qqic);
+
+	if (qqic < MLD_QQIC_MIN_THRESHOLD) {
+		return qqic;
+	} else {
+		unsigned long mc_man, mc_exp;
+
+		mc_exp = MLDV2_QQIC_EXP(qqic);
+		mc_man = MLDV2_QQIC_MAN(qqic);
+
+		return ((mc_man | 0x10) << (mc_exp + 3));
+	}
 }
 
 #endif
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 9fec76e887bc..1438c023db62 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -3606,7 +3606,7 @@ static int br_ip6_multicast_query(struct net_bridge_mcast *brmctx,
 		    mld2q->mld2q_suppress)
 			goto out;
 
-		max_delay = max(msecs_to_jiffies(mldv2_mrc(mld2q)), 1UL);
+		max_delay = max(msecs_to_jiffies(mldv2_mrd(mld2q)), 1UL);
 	}
 
 	is_general_query = group && ipv6_addr_any(group);
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 3330adcf26db..6ddc18ac59b9 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1315,20 +1315,7 @@ static void mld_update_qi(struct inet6_dev *idev,
 	 *  - 9.12. Older Version Querier Present Timeout
 	 *    (the [Query Interval] in the last Query received)
 	 */
-	unsigned long mc_qqi;
-
-	if (mlh2->mld2q_qqic < 128) {
-		mc_qqi = mlh2->mld2q_qqic;
-	} else {
-		unsigned long mc_man, mc_exp;
-
-		mc_exp = MLDV2_QQIC_EXP(mlh2->mld2q_qqic);
-		mc_man = MLDV2_QQIC_MAN(mlh2->mld2q_qqic);
-
-		mc_qqi = (mc_man | 0x10) << (mc_exp + 3);
-	}
-
-	idev->mc_qi = mc_qqi * HZ;
+	idev->mc_qi = mldv2_qqi(mlh2) * HZ;
 }
 
 static void mld_update_qri(struct inet6_dev *idev,
@@ -1338,7 +1325,7 @@ static void mld_update_qri(struct inet6_dev *idev,
 	 *  - 5.1.3. Maximum Response Code
 	 *  - 9.3. Query Response Interval
 	 */
-	idev->mc_qri = msecs_to_jiffies(mldv2_mrc(mlh2));
+	idev->mc_qri = msecs_to_jiffies(mldv2_mrd(mlh2));
 }
 
 static int mld_process_v1(struct inet6_dev *idev, struct mld_msg *mld,
@@ -1390,7 +1377,7 @@ static int mld_process_v1(struct inet6_dev *idev, struct mld_msg *mld,
 static void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld,
 			   unsigned long *max_delay)
 {
-	*max_delay = max(msecs_to_jiffies(mldv2_mrc(mld)), 1UL);
+	*max_delay = max(msecs_to_jiffies(mldv2_mrd(mld)), 1UL);
 
 	mld_update_qrv(idev, mld);
 	mld_update_qi(idev, mld);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 3/4] ipv4: igmp: encode multicast exponential fields
  2026-03-26 15:07 [PATCH 0/4] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
  2026-03-26 15:07 ` [PATCH 1/4] ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation Ujjal Roy
  2026-03-26 15:07 ` [PATCH 2/4] ipv6: mld: rename mldv2_mrc() and add mldv2_qqi() Ujjal Roy
@ 2026-03-26 15:07 ` Ujjal Roy
  2026-03-27 12:19   ` Nikolay Aleksandrov
  2026-03-26 15:07 ` [PATCH 4/4] ipv6: mld: encode multicast exponential fields Ujjal Roy
  2026-03-27  6:41 ` [PATCH 0/4] net: bridge: mcast: add multicast exponential field encoding Nikolay Aleksandrov
  4 siblings, 1 reply; 26+ messages in thread
From: Ujjal Roy @ 2026-03-26 15:07 UTC (permalink / raw)
  To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, Ido Schimmel, David Ahern
  Cc: Ujjal Roy, bridge, netdev, linux-kernel

In IGMP, QQIC and MRC fields are not currently encoded
when generating query packets. Since the receiver of the
query interprets these fields using the IGMPv3 floating-
point decoding logic, any raw interval value that exceeds
the linear threshold is currently parsed incorrectly as
an exponential value, leading to an incorrect interval
calculation.

Encode and assign the corresponding protocol fields during
query generation. Introduce the logic to dynamically
calculate the exponent and mantissa using bit-scan (fls).
This ensures QQIC and MRC fields (8-bit) are properly
encoded when transmitting query packets with intervals
that exceed their respective linear threshold value of
128 (for QQI/MRT).

RFC 3376: if QQIC/MRC >= 128, the QQIC/MRC field represents
a floating-point value as follows:
     0 1 2 3 4 5 6 7
    +-+-+-+-+-+-+-+-+
    |1| exp | mant  |
    +-+-+-+-+-+-+-+-+

Signed-off-by: Ujjal Roy <royujjal@gmail.com>
---
 include/linux/igmp.h      | 80 +++++++++++++++++++++++++++++++++++++++
 net/bridge/br_multicast.c | 14 +++----
 2 files changed, 86 insertions(+), 8 deletions(-)

diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index 3c12c0a63492..99fce6b0625f 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -110,6 +110,86 @@ struct ip_mc_list {
 
 /* IGMPV3 floating-point exponential field threshold */
 #define IGMPV3_EXP_MIN_THRESHOLD	128
+/* Max representable (mant = 0xF, exp = 7) -> 31744 */
+#define IGMPV3_EXP_MAX_THRESHOLD	31744
+
+/* V3 exponential field encoding */
+
+/*
+ * IGMPv3 QQIC/MRC 8-bit exponential field encode.
+ *
+ * RFC3376 defines only the decoding formula:
+ * QQI/MRT = (mant | 0x10) << (exp + 3)
+ *
+ * but does NOT define the encoding procedure. To derive exponent:
+ *
+ * For any value of mantissa and exponent, the decoding formula
+ * indicates that the "hidden bit" (0x10) is shifted 4 bits left
+ * to sit above the 4-bit mantissa. The RFC again shifts this
+ * entire block left by (exp + 3) to reconstruct the value.
+ * So, 'hidden bit' is the MSB which is shifted by (4 + exp + 3).
+ *
+ * Total left shift of the 'hidden bit' = 4 + (exp + 3) = exp + 7.
+ * This is the MSB at the 0-based bit position: (exp + 7).
+ * Since fls() is 1-based, fls(value) - 1 = exp + 7.
+ *
+ * Therefore:
+ *     exp  = fls(value) - 8
+ *     mant = (value >> (exp + 3)) & 0x0F
+ *
+ * Final encoding formula:
+ *     0x80 | (exp << 4) | mant
+ *
+ * Example (value = 3200):
+ *  0               1
+ *  0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0| (value = 3200)
+ * |        ^-^-mant^ ^..(exp+3)..^| exp = 4, mant = 9
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Encoded:
+ *   0x80 | (4 << 4) | 9 = 0xC9
+ */
+static inline u8 igmpv3_exp_field_encode(unsigned long value)
+{
+	u8 mc_exp, mc_man;
+
+	/* RFC3376: QQIC/MRC < 128 is literal */
+	if (value < IGMPV3_EXP_MIN_THRESHOLD)
+		return (u8)value;
+
+	/* Saturate at max representable (mant = 0xF, exp = 7) -> 31744 */
+	if (value >= IGMPV3_EXP_MAX_THRESHOLD)
+		return 0xFF;
+
+	mc_exp  = (u8)(fls(value) - 8);
+	mc_man = (u8)((value >> (mc_exp + 3)) & 0x0F);
+
+	return 0x80 | (mc_exp << 4) | mc_man;
+}
+
+/* Calculate Maximum Response Code from Max Resp Time */
+static inline u8 igmpv3_mrc(unsigned long mrt)
+{
+	/* RFC3376, relevant sections:
+	 *  - 4.1.1. Maximum Response Code
+	 *  - 8.3. Query Response Interval
+	 */
+	return igmpv3_exp_field_encode(mrt);
+}
+
+/* Calculate Querier's Query Interval Code from Query Interval */
+static inline u8 igmpv3_qqic(unsigned long qi)
+{
+	/* RFC3376, relevant sections:
+	 *  - 4.1.7. QQIC (Querier's Query Interval Code)
+	 *  - 8.2. Query Interval
+	 *  - 8.12. Older Version Querier Present Timeout
+	 *    (the [Query Interval] in the last Query received)
+	 */
+	return igmpv3_exp_field_encode(qi);
+}
 
 /* V3 exponential field decoding */
 
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 1438c023db62..1de6242413e0 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -939,7 +939,7 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
 	struct sk_buff *skb;
 	struct igmphdr *ih;
 	struct ethhdr *eth;
-	unsigned long lmqt;
+	unsigned long lmqt, mrt;
 	struct iphdr *iph;
 	u16 lmqt_srcs = 0;
 
@@ -1004,15 +1004,15 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
 	skb_put(skb, 24);
 
 	skb_set_transport_header(skb, skb->len);
+	mrt = group ? brmctx->multicast_last_member_interval :
+		      brmctx->multicast_query_response_interval;
 	*igmp_type = IGMP_HOST_MEMBERSHIP_QUERY;
 
 	switch (brmctx->multicast_igmp_version) {
 	case 2:
 		ih = igmp_hdr(skb);
 		ih->type = IGMP_HOST_MEMBERSHIP_QUERY;
-		ih->code = (group ? brmctx->multicast_last_member_interval :
-				    brmctx->multicast_query_response_interval) /
-			   (HZ / IGMP_TIMER_SCALE);
+		ih->code = mrt / (HZ / IGMP_TIMER_SCALE);
 		ih->group = group;
 		ih->csum = 0;
 		csum = &ih->csum;
@@ -1021,11 +1021,9 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
 	case 3:
 		ihv3 = igmpv3_query_hdr(skb);
 		ihv3->type = IGMP_HOST_MEMBERSHIP_QUERY;
-		ihv3->code = (group ? brmctx->multicast_last_member_interval :
-				      brmctx->multicast_query_response_interval) /
-			     (HZ / IGMP_TIMER_SCALE);
+		ihv3->code = igmpv3_mrc(mrt / (HZ / IGMP_TIMER_SCALE));
 		ihv3->group = group;
-		ihv3->qqic = brmctx->multicast_query_interval / HZ;
+		ihv3->qqic = igmpv3_qqic(brmctx->multicast_query_interval / HZ);
 		ihv3->nsrcs = htons(lmqt_srcs);
 		ihv3->resv = 0;
 		ihv3->suppress = sflag;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 4/4] ipv6: mld: encode multicast exponential fields
  2026-03-26 15:07 [PATCH 0/4] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
                   ` (2 preceding siblings ...)
  2026-03-26 15:07 ` [PATCH 3/4] ipv4: igmp: encode multicast exponential fields Ujjal Roy
@ 2026-03-26 15:07 ` Ujjal Roy
  2026-03-27  6:41 ` [PATCH 0/4] net: bridge: mcast: add multicast exponential field encoding Nikolay Aleksandrov
  4 siblings, 0 replies; 26+ messages in thread
From: Ujjal Roy @ 2026-03-26 15:07 UTC (permalink / raw)
  To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, Ido Schimmel, David Ahern
  Cc: Ujjal Roy, bridge, netdev, linux-kernel

In MLD, QQIC and MRC fields are not currently encoded when
generating query packets. Since the receiver of the query
interprets these fields using the MLDv2 floating-point
decoding logic, any raw interval value that exceeds the
linear threshold is currently parsed incorrectly as an
exponential value, leading to an incorrect interval
calculation.

Encode and assign the corresponding protocol fields during
query generation. Introduce the logic to dynamically
calculate the exponent and mantissa using bit-scan (fls).
This ensures QQIC (8-bit) and MRC (16-bit) fields are
properly encoded when transmitting query packets with
intervals that exceed their respective linear thresholds
(128 for QQI; 32768 for MRD).

RFC3810: If QQIC >= 128, the QQIC field represents a
floating-point value as follows:
     0 1 2 3 4 5 6 7
    +-+-+-+-+-+-+-+-+
    |1| exp | mant  |
    +-+-+-+-+-+-+-+-+

RFC3810: If Maximum Response Code >= 32768, the Maximum
Response Code field represents a floating-point value as
follows:
     0 1 2 3 4 5 6 7 8 9 A B C D E F
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |1| exp |          mant         |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Signed-off-by: Ujjal Roy <royujjal@gmail.com>
---
 include/net/mld.h         | 117 ++++++++++++++++++++++++++++++++++++++
 net/bridge/br_multicast.c |   4 +-
 2 files changed, 119 insertions(+), 2 deletions(-)

diff --git a/include/net/mld.h b/include/net/mld.h
index 6373eb76efe5..0962312d29b9 100644
--- a/include/net/mld.h
+++ b/include/net/mld.h
@@ -90,12 +90,129 @@ struct mld2_query {
 #define MLDV2_QQIC_MAN(value)	((value) & 0x0f)
 
 #define MLD_QQIC_MIN_THRESHOLD	128
+/* Max representable (mant = 0xF, exp = 7) -> 31744 */
+#define MLD_QQIC_MAX_THRESHOLD	31744
 #define MLD_MRC_MIN_THRESHOLD	32768UL
+/* Max representable (mant = 0xFFF, exp = 7) -> 8387584 */
+#define MLD_MRC_MAX_THRESHOLD	8387584
 #define MLDV1_MRD_MAX_COMPAT	(MLD_MRC_MIN_THRESHOLD - 1)
 
 #define MLD_MAX_QUEUE		8
 #define MLD_MAX_SKBS		32
 
+/* V2 exponential field encoding */
+
+/*
+ * Calculate Maximum Response Code from Maximum Response Delay
+ *
+ * MLDv2 Maximum Response Code 16-bit encoding (RFC3810).
+ *
+ * RFC3810 defines only the decoding formula:
+ * Maximum Response Delay = (mant | 0x1000) << (exp + 3)
+ *
+ * but does NOT define the encoding procedure. To derive exponent:
+ *
+ * For the 16-bit MRC, the "hidden bit" (0x1000) is left shifted by 12
+ * to sit above the 12-bit mantissa. The RFC then shifts this entire
+ * block left by (exp + 3) to reconstruct the value.
+ * So, 'hidden bit' is the MSB which is shifted by (12 + exp + 3).
+ *
+ * - Total left shift of the hidden bit = 12 + (exp + 3) = exp + 15.
+ * - This is the MSB at the 0-based bit position: (exp + 15).
+ * - Since fls() is 1-based, fls(value) - 1 = exp + 15.
+ *
+ * Therefore:
+ *     exp  = fls(value) - 16
+ *     mant = (value >> (exp + 3)) & 0x0FFF
+ *
+ * Final encoding formula:
+ *     0x8000 | (exp << 12) | mant
+ *
+ * Example (value = 1311744):
+ *  0               1               2               3
+ *  0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0| 1311744
+ * |                      ^-^--------mant---------^ ^...(exp+3)...^| exp=5
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Encoded:
+ *   0x8000 | (5 << 12) | 0x404 = 0xD404
+ */
+static inline u16 mldv2_mrc(unsigned long mrd)
+{
+	u16 mc_man, mc_exp;
+
+	/* RFC3810: MRC < 32768 is literal */
+	if (mrd < MLD_MRC_MIN_THRESHOLD)
+		return (u16)mrd;
+
+	/* Saturate at max representable (mant = 0xFFF, exp = 7) -> 8387584 */
+	if (mrd >= MLD_MRC_MAX_THRESHOLD)
+		return 0xFFFF;
+
+	mc_exp = (u16)(fls(mrd) - 16);
+	mc_man = (u16)((mrd >> (mc_exp + 3)) & 0x0FFF);
+
+	return (0x8000 | (mc_exp << 12) | mc_man);
+}
+
+/*
+ * Calculate Querier's Query Interval Code from Query Interval
+ *
+ * MLDv2 QQIC 8-bit floating-point encoding (RFC3810).
+ *
+ * RFC3810 defines only the decoding formula:
+ * QQI = (mant | 0x10) << (exp + 3)
+ *
+ * but does NOT define the encoding procedure. To derive exponent:
+ *
+ * For any value of mantissa and exponent, the decoding formula
+ * indicates that the "hidden bit" (0x10) is shifted 4 bits left
+ * to sit above the 4-bit mantissa. The RFC again shifts this
+ * entire block left by (exp + 3) to reconstruct the value.
+ * So, 'hidden bit' is the MSB which is shifted by (4 + exp + 3).
+ *
+ * Total left shift of the 'hidden bit' = 4 + (exp + 3) = exp + 7.
+ * This is the MSB at the 0-based bit position: (exp + 7).
+ * Since fls() is 1-based, fls(value) - 1 = exp + 7.
+ *
+ * Therefore:
+ *     exp  = fls(value) - 8
+ *     mant = (value >> (exp + 3)) & 0x0F
+ *
+ * Final encoding formula:
+ *     0x80 | (exp << 4) | mant
+ *
+ * Example (value = 3200):
+ *  0               1
+ *  0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0| (value = 3200)
+ * |        ^-^-mant^ ^..(exp+3)..^| exp = 4, mant = 9
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Encoded:
+ *   0x80 | (4 << 4) | 9 = 0xC9
+ */
+static inline u8 mldv2_qqic(unsigned long value)
+{
+	u8 mc_man, mc_exp;
+
+	/* RFC3810: QQIC < 128 is literal */
+	if (value < MLD_QQIC_MIN_THRESHOLD)
+		return (u8)value;
+
+	/* Saturate at max representable (mant = 0xF, exp = 7) -> 31744 */
+	if (value >= MLD_QQIC_MAX_THRESHOLD)
+		return 0xFF;
+
+	mc_exp  = (u8)(fls(value) - 8);
+	mc_man = (u8)((value >> (mc_exp + 3)) & 0x0F);
+
+	return (0x80 | (mc_exp << 4) | mc_man);
+}
+
 /* V2 exponential field decoding */
 
 /* Calculate Maximum Response Delay from Maximum Response Code
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 1de6242413e0..c59dab6fff4e 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -1181,7 +1181,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
 		break;
 	case 2:
 		mld2q = (struct mld2_query *)icmp6_hdr(skb);
-		mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(interval));
+		mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(mldv2_mrc(interval)));
 		mld2q->mld2q_type = ICMPV6_MGM_QUERY;
 		mld2q->mld2q_code = 0;
 		mld2q->mld2q_cksum = 0;
@@ -1190,7 +1190,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
 		mld2q->mld2q_suppress = sflag;
 		mld2q->mld2q_qrv = 2;
 		mld2q->mld2q_nsrcs = htons(llqt_srcs);
-		mld2q->mld2q_qqic = brmctx->multicast_query_interval / HZ;
+		mld2q->mld2q_qqic = mldv2_qqic(brmctx->multicast_query_interval / HZ);
 		mld2q->mld2q_mca = *group;
 		csum = &mld2q->mld2q_cksum;
 		csum_start = (void *)mld2q;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH 0/4] net: bridge: mcast: add multicast exponential field encoding
  2026-03-26 15:07 [PATCH 0/4] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
                   ` (3 preceding siblings ...)
  2026-03-26 15:07 ` [PATCH 4/4] ipv6: mld: encode multicast exponential fields Ujjal Roy
@ 2026-03-27  6:41 ` Nikolay Aleksandrov
  2026-03-27 11:31   ` Nikolay Aleksandrov
  4 siblings, 1 reply; 26+ messages in thread
From: Nikolay Aleksandrov @ 2026-03-27  6:41 UTC (permalink / raw)
  To: Ujjal Roy, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Ido Schimmel, David Ahern
  Cc: Ujjal Roy, bridge, netdev, linux-kernel

On 26/03/2026 17:07, Ujjal Roy wrote:
> Description:
> This series addresses a mismatch in how multicast query
> intervals and response codes are handled across IPv4 (IGMPv3)
> and IPv6 (MLDv2). While decoding logic currently exists,
> the corresponding encoding logic is missing during query
> packet generation. This leads to incorrect intervals being
> transmitted when values exceed their linear thresholds.
> 
> The patches introduce a unified floating-point encoding
> approach based on RFC3376 and RFC3810, ensuring that large
> intervals are correctly represented in QQIC and MRC fields
> using the exponent-mantissa format.
> 
> Key Changes:
> * ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation
>    Removes legacy macros in favor of a cleaner, unified
>    calculation for retrieving intervals from encoded fields,
>    improving code maintainability.
> 
> * ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
>    Standardizes MLDv2 terminology by renaming mldv2_mrc()
>    to mldv2_mrd() (Maximum Response Delay) and introducing
>    a new API mldv2_qqi for QQI calculation, improving code
>    readability.
> 
> * ipv4: igmp: encode multicast exponential fields
>    Introduces the logic to dynamically calculate the exponent
>    and mantissa using bit-scan (fls). This ensures QQIC and
>    MRC fields (8-bit) are properly encoded when transmitting
>    query packets with intervals that exceed their respective
>    linear threshold value of 128 (for QQI/MRT).
> 
> * ipv6: mld: encode multicast exponential fields
>    Applies similar encoding logic for MLDv2. This ensures
>    QQIC (8-bit) and MRC (16-bit) fields are properly encoded
>    when transmitting query packets with intervals that exceed
>    their respective linear thresholds (128 for QQI; 32768
>    for MRD).
> 
> Impact:
> These changes ensure that multicast queriers and listeners
> stay synchronized on timing intervals, preventing protocol
> timeouts or premature group membership expiration caused
> by incorrectly formatted packet headers.
> 

Can you add selftests which cover these cases?



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 0/4] net: bridge: mcast: add multicast exponential field encoding
  2026-03-27  6:41 ` [PATCH 0/4] net: bridge: mcast: add multicast exponential field encoding Nikolay Aleksandrov
@ 2026-03-27 11:31   ` Nikolay Aleksandrov
  0 siblings, 0 replies; 26+ messages in thread
From: Nikolay Aleksandrov @ 2026-03-27 11:31 UTC (permalink / raw)
  To: Ujjal Roy, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Ido Schimmel, David Ahern
  Cc: Ujjal Roy, bridge, netdev, linux-kernel

On 27/03/2026 08:41, Nikolay Aleksandrov wrote:
> On 26/03/2026 17:07, Ujjal Roy wrote:
>> Description:
>> This series addresses a mismatch in how multicast query
>> intervals and response codes are handled across IPv4 (IGMPv3)
>> and IPv6 (MLDv2). While decoding logic currently exists,
>> the corresponding encoding logic is missing during query
>> packet generation. This leads to incorrect intervals being
>> transmitted when values exceed their linear thresholds.
>>
>> The patches introduce a unified floating-point encoding
>> approach based on RFC3376 and RFC3810, ensuring that large
>> intervals are correctly represented in QQIC and MRC fields
>> using the exponent-mantissa format.
>>
>> Key Changes:
>> * ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation
>>    Removes legacy macros in favor of a cleaner, unified
>>    calculation for retrieving intervals from encoded fields,
>>    improving code maintainability.
>>
>> * ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
>>    Standardizes MLDv2 terminology by renaming mldv2_mrc()
>>    to mldv2_mrd() (Maximum Response Delay) and introducing
>>    a new API mldv2_qqi for QQI calculation, improving code
>>    readability.
>>
>> * ipv4: igmp: encode multicast exponential fields
>>    Introduces the logic to dynamically calculate the exponent
>>    and mantissa using bit-scan (fls). This ensures QQIC and
>>    MRC fields (8-bit) are properly encoded when transmitting
>>    query packets with intervals that exceed their respective
>>    linear threshold value of 128 (for QQI/MRT).
>>
>> * ipv6: mld: encode multicast exponential fields
>>    Applies similar encoding logic for MLDv2. This ensures
>>    QQIC (8-bit) and MRC (16-bit) fields are properly encoded
>>    when transmitting query packets with intervals that exceed
>>    their respective linear thresholds (128 for QQI; 32768
>>    for MRD).
>>
>> Impact:
>> These changes ensure that multicast queriers and listeners
>> stay synchronized on timing intervals, preventing protocol
>> timeouts or premature group membership expiration caused
>> by incorrectly formatted packet headers.
>>
> 
> Can you add selftests which cover these cases?
> 
> 

Forgot to mention - for improvements and new features please target net-next.

Cheers,
  Nik


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 3/4] ipv4: igmp: encode multicast exponential fields
  2026-03-26 15:07 ` [PATCH 3/4] ipv4: igmp: encode multicast exponential fields Ujjal Roy
@ 2026-03-27 12:19   ` Nikolay Aleksandrov
  2026-03-30 19:16     ` [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
  0 siblings, 1 reply; 26+ messages in thread
From: Nikolay Aleksandrov @ 2026-03-27 12:19 UTC (permalink / raw)
  To: Ujjal Roy, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Ido Schimmel, David Ahern
  Cc: Ujjal Roy, bridge, netdev, linux-kernel

On 26/03/2026 17:07, Ujjal Roy wrote:
> In IGMP, QQIC and MRC fields are not currently encoded
> when generating query packets. Since the receiver of the
> query interprets these fields using the IGMPv3 floating-
> point decoding logic, any raw interval value that exceeds
> the linear threshold is currently parsed incorrectly as
> an exponential value, leading to an incorrect interval
> calculation.
> 
> Encode and assign the corresponding protocol fields during
> query generation. Introduce the logic to dynamically
> calculate the exponent and mantissa using bit-scan (fls).
> This ensures QQIC and MRC fields (8-bit) are properly
> encoded when transmitting query packets with intervals
> that exceed their respective linear threshold value of
> 128 (for QQI/MRT).
> 
> RFC 3376: if QQIC/MRC >= 128, the QQIC/MRC field represents
> a floating-point value as follows:
>       0 1 2 3 4 5 6 7
>      +-+-+-+-+-+-+-+-+
>      |1| exp | mant  |
>      +-+-+-+-+-+-+-+-+
> 
> Signed-off-by: Ujjal Roy <royujjal@gmail.com>
> ---
>   include/linux/igmp.h      | 80 +++++++++++++++++++++++++++++++++++++++
>   net/bridge/br_multicast.c | 14 +++----
>   2 files changed, 86 insertions(+), 8 deletions(-)
> 
[snip]
> diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
> index 1438c023db62..1de6242413e0 100644
> --- a/net/bridge/br_multicast.c
> +++ b/net/bridge/br_multicast.c
> @@ -939,7 +939,7 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
>   	struct sk_buff *skb;
>   	struct igmphdr *ih;
>   	struct ethhdr *eth;
> -	unsigned long lmqt;
> +	unsigned long lmqt, mrt;
>   	struct iphdr *iph;
>   	u16 lmqt_srcs = 0;

please keep reverse xmas tree order

>   
> @@ -1004,15 +1004,15 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
>   	skb_put(skb, 24);
>   
>   	skb_set_transport_header(skb, skb->len);
> +	mrt = group ? brmctx->multicast_last_member_interval :
> +		      brmctx->multicast_query_response_interval;
>   	*igmp_type = IGMP_HOST_MEMBERSHIP_QUERY;
>   
>   	switch (brmctx->multicast_igmp_version) {
>   	case 2:
>   		ih = igmp_hdr(skb);
>   		ih->type = IGMP_HOST_MEMBERSHIP_QUERY;
> -		ih->code = (group ? brmctx->multicast_last_member_interval :
> -				    brmctx->multicast_query_response_interval) /
> -			   (HZ / IGMP_TIMER_SCALE);
> +		ih->code = mrt / (HZ / IGMP_TIMER_SCALE);
>   		ih->group = group;
>   		ih->csum = 0;
>   		csum = &ih->csum;
> @@ -1021,11 +1021,9 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
>   	case 3:
>   		ihv3 = igmpv3_query_hdr(skb);
>   		ihv3->type = IGMP_HOST_MEMBERSHIP_QUERY;
> -		ihv3->code = (group ? brmctx->multicast_last_member_interval :
> -				      brmctx->multicast_query_response_interval) /
> -			     (HZ / IGMP_TIMER_SCALE);
> +		ihv3->code = igmpv3_mrc(mrt / (HZ / IGMP_TIMER_SCALE));
>   		ihv3->group = group;
> -		ihv3->qqic = brmctx->multicast_query_interval / HZ;
> +		ihv3->qqic = igmpv3_qqic(brmctx->multicast_query_interval / HZ);
>   		ihv3->nsrcs = htons(lmqt_srcs);
>   		ihv3->resv = 0;
>   		ihv3->suppress = sflag;


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/4] ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
  2026-03-26 15:07 ` [PATCH 2/4] ipv6: mld: rename mldv2_mrc() and add mldv2_qqi() Ujjal Roy
@ 2026-03-27 15:49   ` kernel test robot
  0 siblings, 0 replies; 26+ messages in thread
From: kernel test robot @ 2026-03-27 15:49 UTC (permalink / raw)
  To: Ujjal Roy, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Nikolay Aleksandrov, Ido Schimmel,
	David Ahern
  Cc: oe-kbuild-all, Ujjal Roy, bridge, netdev, linux-kernel

Hi Ujjal,

kernel test robot noticed the following build warnings:

[auto build test WARNING on net-next/main]
[also build test WARNING on net/main klassert-ipsec/master linus/master v7.0-rc5 next-20260326]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Ujjal-Roy/ipv4-igmp-get-rid-of-IGMPV3_-QQIC-MRC-and-simplify-calculation/20260327-083159
base:   net-next/main
patch link:    https://lore.kernel.org/r/20260326150742.50289-3-royujjal%40gmail.com
patch subject: [PATCH 2/4] ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
config: x86_64-randconfig-121-20260327 (https://download.01.org/0day-ci/archive/20260327/202603272318.WLQHm8jS-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
sparse: v0.6.5-rc1
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260327/202603272318.WLQHm8jS-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603272318.WLQHm8jS-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
   net/ipv6/mcast.c: note: in included file:
   include/net/mld.h:32:43: sparse: sparse: array of flexible structures
>> include/net/mld.h:147:30: sparse: sparse: cast to restricted __be16

vim +147 include/net/mld.h

    29	
    30	struct mld2_report {
    31		struct icmp6hdr		mld2r_hdr;
  > 32		struct mld2_grec	mld2r_grec[];
    33	};
    34	
    35	#define mld2r_type		mld2r_hdr.icmp6_type
    36	#define mld2r_resv1		mld2r_hdr.icmp6_code
    37	#define mld2r_cksum		mld2r_hdr.icmp6_cksum
    38	#define mld2r_resv2		mld2r_hdr.icmp6_dataun.un_data16[0]
    39	#define mld2r_ngrec		mld2r_hdr.icmp6_dataun.un_data16[1]
    40	
    41	/* MLDv2 Query */
    42	struct mld2_query {
    43		struct icmp6hdr		mld2q_hdr;
    44		struct in6_addr		mld2q_mca;
    45	#if defined(__LITTLE_ENDIAN_BITFIELD)
    46		__u8			mld2q_qrv:3,
    47					mld2q_suppress:1,
    48					mld2q_resv2:4;
    49	#elif defined(__BIG_ENDIAN_BITFIELD)
    50		__u8			mld2q_resv2:4,
    51					mld2q_suppress:1,
    52					mld2q_qrv:3;
    53	#else
    54	#error "Please fix <asm/byteorder.h>"
    55	#endif
    56		__u8			mld2q_qqic;
    57		__be16			mld2q_nsrcs;
    58		struct in6_addr		mld2q_srcs[];
    59	};
    60	
    61	#define mld2q_type		mld2q_hdr.icmp6_type
    62	#define mld2q_code		mld2q_hdr.icmp6_code
    63	#define mld2q_cksum		mld2q_hdr.icmp6_cksum
    64	#define mld2q_mrc		mld2q_hdr.icmp6_maxdelay
    65	#define mld2q_resv1		mld2q_hdr.icmp6_dataun.un_data16[1]
    66	
    67	/* RFC3810, 5.1.3. Maximum Response Code:
    68	 *
    69	 * If Maximum Response Code >= 32768, Maximum Response Code represents a
    70	 * floating-point value as follows:
    71	 *
    72	 *  0 1 2 3 4 5 6 7 8 9 A B C D E F
    73	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    74	 * |1| exp |          mant         |
    75	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    76	 */
    77	#define MLDV2_MRC_EXP(value)	(((value) >> 12) & 0x0007)
    78	#define MLDV2_MRC_MAN(value)	((value) & 0x0fff)
    79	
    80	/* RFC3810, 5.1.9. QQIC (Querier's Query Interval Code):
    81	 *
    82	 * If QQIC >= 128, QQIC represents a floating-point value as follows:
    83	 *
    84	 *  0 1 2 3 4 5 6 7
    85	 * +-+-+-+-+-+-+-+-+
    86	 * |1| exp | mant  |
    87	 * +-+-+-+-+-+-+-+-+
    88	 */
    89	#define MLDV2_QQIC_EXP(value)	(((value) >> 4) & 0x07)
    90	#define MLDV2_QQIC_MAN(value)	((value) & 0x0f)
    91	
    92	#define MLD_QQIC_MIN_THRESHOLD	128
    93	#define MLD_MRC_MIN_THRESHOLD	32768UL
    94	#define MLDV1_MRD_MAX_COMPAT	(MLD_MRC_MIN_THRESHOLD - 1)
    95	
    96	#define MLD_MAX_QUEUE		8
    97	#define MLD_MAX_SKBS		32
    98	
    99	/* V2 exponential field decoding */
   100	
   101	/* Calculate Maximum Response Delay from Maximum Response Code
   102	 *
   103	 * RFC3810, 5.1.3. defines the decoding formula:
   104	 *      0 1 2 3 4 5 6 7 8 9 A B C D E F
   105	 *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   106	 *     |1| exp |          mant         |
   107	 *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   108	 * Maximum Response Delay = (mant | 0x1000) << (exp+3)
   109	 */
   110	static inline unsigned long mldv2_mrd(const struct mld2_query *mlh2)
   111	{
   112		/* RFC3810, relevant sections:
   113		 *  - 5.1.3. Maximum Response Code
   114		 *  - 9.3. Query Response Interval
   115		 */
   116		unsigned long mc_mrc = ntohs(mlh2->mld2q_mrc);
   117	
   118		if (mc_mrc < MLD_MRC_MIN_THRESHOLD) {
   119			return mc_mrc;
   120		} else {
   121			unsigned long mc_man, mc_exp;
   122	
   123			mc_exp = MLDV2_MRC_EXP(mc_mrc);
   124			mc_man = MLDV2_MRC_MAN(mc_mrc);
   125	
   126			return ((mc_man | 0x1000) << (mc_exp + 3));
   127		}
   128	}
   129	
   130	/* Calculate Querier's Query Interval from Querier's Query Interval Code
   131	 *
   132	 * RFC3810, 5.1.9. defines the decoding formula:
   133	 *      0 1 2 3 4 5 6 7
   134	 *     +-+-+-+-+-+-+-+-+
   135	 *     |1| exp | mant  |
   136	 *     +-+-+-+-+-+-+-+-+
   137	 * QQI = (mant | 0x10) << (exp + 3)
   138	 */
   139	static inline unsigned long mldv2_qqi(const struct mld2_query *mlh2)
   140	{
   141		/* RFC3810, relevant sections:
   142		 *  - 5.1.9. QQIC (Querier's Query Interval Code)
   143		 *  - 9.2. Query Interval
   144		 *  - 9.12. Older Version Querier Present Timeout
   145		 *    (the [Query Interval] in the last Query received)
   146		 */
 > 147		unsigned long qqic = ntohs(mlh2->mld2q_qqic);
   148	
   149		if (qqic < MLD_QQIC_MIN_THRESHOLD) {
   150			return qqic;
   151		} else {
   152			unsigned long mc_man, mc_exp;
   153	
   154			mc_exp = MLDV2_QQIC_EXP(qqic);
   155			mc_man = MLDV2_QQIC_MAN(qqic);
   156	
   157			return ((mc_man | 0x10) << (mc_exp + 3));
   158		}
   159	}
   160	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding
  2026-03-27 12:19   ` Nikolay Aleksandrov
@ 2026-03-30 19:16     ` Ujjal Roy
  2026-03-30 19:16       ` [PATCH net-next v2 1/5] ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation Ujjal Roy
                         ` (6 more replies)
  0 siblings, 7 replies; 26+ messages in thread
From: Ujjal Roy @ 2026-03-30 19:16 UTC (permalink / raw)
  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, netdev, linux-kernel, linux-kselftest

Description:
This series addresses a mismatch in how multicast query
intervals and response codes are handled across IPv4 (IGMPv3)
and IPv6 (MLDv2). While decoding logic currently exists,
the corresponding encoding logic is missing during query
packet generation. This leads to incorrect intervals being
transmitted when values exceed their linear thresholds.

The patches introduce a unified floating-point encoding
approach based on RFC3376 and RFC3810, ensuring that large
intervals are correctly represented in QQIC and MRC fields
using the exponent-mantissa format.

Key Changes:
* ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation
  Removes legacy macros in favor of a cleaner, unified
  calculation for retrieving intervals from encoded fields,
  improving code maintainability.

* ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
  Standardizes MLDv2 terminology by renaming mldv2_mrc()
  to mldv2_mrd() (Maximum Response Delay) and introducing
  a new API mldv2_qqi for QQI calculation, improving code
  readability.

* ipv4: igmp: encode multicast exponential fields
  Introduces the logic to dynamically calculate the exponent
  and mantissa using bit-scan (fls). This ensures QQIC and
  MRC fields (8-bit) are properly encoded when transmitting
  query packets with intervals that exceed their respective
  linear threshold value of 128 (for QQI/MRT).

* ipv6: mld: encode multicast exponential fields
  Applies similar encoding logic for MLDv2. This ensures
  QQIC (8-bit) and MRC (16-bit) fields are properly encoded
  when transmitting query packets with intervals that exceed
  their respective linear thresholds (128 for QQI; 32768
  for MRD).

Impact:
These changes ensure that multicast queriers and listeners
stay synchronized on timing intervals, preventing protocol
timeouts or premature group membership expiration caused
by incorrectly formatted packet headers.

v2:
- Retargeted the series to net-next as suggested
- Fixed a compilation warning in the MLD rename change
- Kept reverse xmas tree order in IGMP exponential encoding change
- Added bridge selftests to validate IGMPv3 Query MRC and QQIC handling

Ujjal Roy (5):
  ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation
  ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
  ipv4: igmp: encode multicast exponential fields
  ipv6: mld: encode multicast exponential fields
  selftests: net: bridge: add tests for igmpv3 MRC and QQIC validation

 include/linux/igmp.h                          | 158 +++++++++++++++-
 include/net/mld.h                             | 179 +++++++++++++++++-
 net/bridge/br_multicast.c                     |  22 +--
 net/ipv4/igmp.c                               |   6 +-
 net/ipv6/mcast.c                              |  19 +-
 .../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 ++++
 10 files changed, 535 insertions(+), 48 deletions(-)
 create mode 100644 tools/testing/selftests/net/forwarding/mc_decode.c
 create mode 100644 tools/testing/selftests/net/forwarding/mc_encode.c


base-commit: cf0d9080c6f795bc6be08babbffa29b62c06e9b0
-- 
2.43.0


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH net-next v2 1/5] ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation
  2026-03-30 19:16     ` [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
@ 2026-03-30 19:16       ` Ujjal Roy
  2026-04-01  7:25         ` Ido Schimmel
  2026-03-30 19:16       ` [PATCH net-next v2 2/5] ipv6: mld: rename mldv2_mrc() and add mldv2_qqi() Ujjal Roy
                         ` (5 subsequent siblings)
  6 siblings, 1 reply; 26+ messages in thread
From: Ujjal Roy @ 2026-03-30 19:16 UTC (permalink / raw)
  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, netdev, linux-kernel, linux-kselftest

Get rid of the IGMPV3_MRC macro and use the igmpv3_mrt() API to
calculate the Max Resp Time from the Maximum Response Code.

Similarly, for IGMPV3_QQIC, use the igmpv3_qqi() API to calculate
the Querier's Query Interval from the QQIC field.

Signed-off-by: Ujjal Roy <royujjal@gmail.com>
---
 include/linux/igmp.h      | 78 +++++++++++++++++++++++++++++++++++----
 net/bridge/br_multicast.c |  2 +-
 net/ipv4/igmp.c           |  6 +--
 3 files changed, 74 insertions(+), 12 deletions(-)

diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index 073b30a9b850..3c12c0a63492 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -92,15 +92,77 @@ struct ip_mc_list {
 	struct rcu_head		rcu;
 };
 
+/* RFC3376, relevant sections:
+ *  - 4.1.1. Maximum Response Code
+ *  - 4.1.7. QQIC (Querier's Query Interval Code)
+ *
+ * If Max Resp Code >= 128, Max Resp Code represents a floating-point
+ * value as follows:
+ * If QQIC >= 128, QQIC represents a floating-point value as follows:
+ *
+ *  0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+
+ * |1| exp | mant  |
+ * +-+-+-+-+-+-+-+-+
+ */
+#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
+
 /* V3 exponential field decoding */
-#define IGMPV3_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value))
-#define IGMPV3_EXP(thresh, nbmant, nbexp, value) \
-	((value) < (thresh) ? (value) : \
-        ((IGMPV3_MASK(value, nbmant) | (1<<(nbmant))) << \
-         (IGMPV3_MASK((value) >> (nbmant), nbexp) + (nbexp))))
-
-#define IGMPV3_QQIC(value) IGMPV3_EXP(0x80, 4, 3, value)
-#define IGMPV3_MRC(value) IGMPV3_EXP(0x80, 4, 3, value)
+
+/*
+ * IGMPv3 QQI/MRT 8-bit exponential field decode.
+ *
+ * RFC3376, 4.1.1 & 4.1.7. defines the decoding formula:
+ *      0 1 2 3 4 5 6 7
+ *     +-+-+-+-+-+-+-+-+
+ *     |1| exp | mant  |
+ *     +-+-+-+-+-+-+-+-+
+ * Max Resp Time = (mant | 0x10) << (exp + 3)
+ * QQI = (mant | 0x10) << (exp + 3)
+ */
+static inline unsigned long igmpv3_exp_field_decode(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 = IGMPV3_FP_EXP(code);
+		mc_man = IGMPV3_FP_MAN(code);
+
+		return ((mc_man | 0x10) << (mc_exp + 3));
+	}
+}
+
+/* Calculate Max Resp Time from Maximum Response Code */
+static inline unsigned long igmpv3_mrt(const struct igmpv3_query *ih3)
+{
+	/* RFC3376, relevant sections:
+	 *  - 4.1.1. Maximum Response Code
+	 *  - 8.3. Query Response Interval
+	 */
+	return igmpv3_exp_field_decode(ih3->code);
+}
+
+/* Calculate Querier's Query Interval from Querier's Query Interval Code */
+static inline unsigned long igmpv3_qqi(const struct igmpv3_query *ih3)
+{
+	/* RFC3376, relevant sections:
+	 *  - 4.1.7. QQIC (Querier's Query Interval Code)
+	 *  - 8.2. Query Interval
+	 *  - 8.12. Older Version Querier Present Timeout
+	 *    (the [Query Interval] in the last Query received)
+	 */
+	return igmpv3_exp_field_decode(ih3->qqic);
+}
 
 static inline int ip_mc_may_pull(struct sk_buff *skb, unsigned int len)
 {
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 881d866d687a..9fec76e887bc 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -3518,7 +3518,7 @@ static void br_ip4_multicast_query(struct net_bridge_mcast *brmctx,
 			goto out;
 
 		max_delay = ih3->code ?
-			    IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
+			    igmpv3_mrt(ih3) * (HZ / IGMP_TIMER_SCALE) : 1;
 	} else {
 		goto out;
 	}
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index a674fb44ec25..8c6102737096 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -991,7 +991,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
 		 * different encoding. We use the v3 encoding as more likely
 		 * to be intended in a v3 query.
 		 */
-		max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
+		max_delay = igmpv3_mrt(ih3)*(HZ/IGMP_TIMER_SCALE);
 		if (!max_delay)
 			max_delay = 1;	/* can't mod w/ 0 */
 	} else { /* v3 */
@@ -1006,7 +1006,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
 			ih3 = igmpv3_query_hdr(skb);
 		}
 
-		max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
+		max_delay = igmpv3_mrt(ih3)*(HZ/IGMP_TIMER_SCALE);
 		if (!max_delay)
 			max_delay = 1;	/* can't mod w/ 0 */
 		WRITE_ONCE(in_dev->mr_maxdelay, max_delay);
@@ -1016,7 +1016,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
 		 * configured value.
 		 */
 		in_dev->mr_qrv = ih3->qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
-		in_dev->mr_qi = IGMPV3_QQIC(ih3->qqic)*HZ ?: IGMP_QUERY_INTERVAL;
+		in_dev->mr_qi = igmpv3_qqi(ih3)*HZ ?: IGMP_QUERY_INTERVAL;
 
 		/* RFC3376, 8.3. Query Response Interval:
 		 * The number of seconds represented by the [Query Response
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH net-next v2 2/5] ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
  2026-03-30 19:16     ` [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
  2026-03-30 19:16       ` [PATCH net-next v2 1/5] ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation Ujjal Roy
@ 2026-03-30 19:16       ` Ujjal Roy
  2026-04-01  7:26         ` Ido Schimmel
  2026-03-30 19:16       ` [PATCH net-next v2 3/5] ipv4: igmp: encode multicast exponential fields Ujjal Roy
                         ` (4 subsequent siblings)
  6 siblings, 1 reply; 26+ messages in thread
From: Ujjal Roy @ 2026-03-30 19:16 UTC (permalink / raw)
  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, netdev, linux-kernel, linux-kselftest

Rename mldv2_mrc() to mldv2_mrd() as it used to calculate
the Maximum Response Delay from the Maximum Response Code.

Introduce a new API mldv2_qqi() to define the existing
calculation logic of QQI from QQIC. This also organizes
the existing mld_update_qi() API.

Added by e3f5b1704 ("net: ipv6: mld: get rid of MLDV2_MRC and simplify calculation").

Signed-off-by: Ujjal Roy <royujjal@gmail.com>
---
 include/net/mld.h         | 64 +++++++++++++++++++++++++++++++++------
 net/bridge/br_multicast.c |  2 +-
 net/ipv6/mcast.c          | 19 ++----------
 3 files changed, 58 insertions(+), 27 deletions(-)

diff --git a/include/net/mld.h b/include/net/mld.h
index c07359808493..5e093f9c75d9 100644
--- a/include/net/mld.h
+++ b/include/net/mld.h
@@ -89,29 +89,73 @@ struct mld2_query {
 #define MLDV2_QQIC_EXP(value)	(((value) >> 4) & 0x07)
 #define MLDV2_QQIC_MAN(value)	((value) & 0x0f)
 
-#define MLD_EXP_MIN_LIMIT	32768UL
-#define MLDV1_MRD_MAX_COMPAT	(MLD_EXP_MIN_LIMIT - 1)
+#define MLD_QQIC_MIN_THRESHOLD	128
+#define MLD_MRC_MIN_THRESHOLD	32768UL
+#define MLDV1_MRD_MAX_COMPAT	(MLD_MRC_MIN_THRESHOLD - 1)
 
 #define MLD_MAX_QUEUE		8
 #define MLD_MAX_SKBS		32
 
-static inline unsigned long mldv2_mrc(const struct mld2_query *mlh2)
-{
-	/* RFC3810, 5.1.3. Maximum Response Code */
-	unsigned long ret, mc_mrc = ntohs(mlh2->mld2q_mrc);
+/* V2 exponential field decoding */
 
-	if (mc_mrc < MLD_EXP_MIN_LIMIT) {
-		ret = mc_mrc;
+/* Calculate Maximum Response Delay from Maximum Response Code
+ *
+ * RFC3810, 5.1.3. defines the decoding formula:
+ *      0 1 2 3 4 5 6 7 8 9 A B C D E F
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *     |1| exp |          mant         |
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Maximum Response Delay = (mant | 0x1000) << (exp+3)
+ */
+static inline unsigned long mldv2_mrd(const struct mld2_query *mlh2)
+{
+	/* RFC3810, relevant sections:
+	 *  - 5.1.3. Maximum Response Code
+	 *  - 9.3. Query Response Interval
+	 */
+	unsigned long mc_mrc = ntohs(mlh2->mld2q_mrc);
+
+	if (mc_mrc < MLD_MRC_MIN_THRESHOLD) {
+		return mc_mrc;
 	} else {
 		unsigned long mc_man, mc_exp;
 
 		mc_exp = MLDV2_MRC_EXP(mc_mrc);
 		mc_man = MLDV2_MRC_MAN(mc_mrc);
 
-		ret = (mc_man | 0x1000) << (mc_exp + 3);
+		return ((mc_man | 0x1000) << (mc_exp + 3));
 	}
+}
 
-	return ret;
+/* Calculate Querier's Query Interval from Querier's Query Interval Code
+ *
+ * RFC3810, 5.1.9. defines the decoding formula:
+ *      0 1 2 3 4 5 6 7
+ *     +-+-+-+-+-+-+-+-+
+ *     |1| exp | mant  |
+ *     +-+-+-+-+-+-+-+-+
+ * QQI = (mant | 0x10) << (exp + 3)
+ */
+static inline unsigned long mldv2_qqi(const struct mld2_query *mlh2)
+{
+	/* RFC3810, relevant sections:
+	 *  - 5.1.9. QQIC (Querier's Query Interval Code)
+	 *  - 9.2. Query Interval
+	 *  - 9.12. Older Version Querier Present Timeout
+	 *    (the [Query Interval] in the last Query received)
+	 */
+	unsigned long qqic = mlh2->mld2q_qqic;
+
+	if (qqic < MLD_QQIC_MIN_THRESHOLD) {
+		return qqic;
+	} else {
+		unsigned long mc_man, mc_exp;
+
+		mc_exp = MLDV2_QQIC_EXP(qqic);
+		mc_man = MLDV2_QQIC_MAN(qqic);
+
+		return ((mc_man | 0x10) << (mc_exp + 3));
+	}
 }
 
 #endif
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 9fec76e887bc..1438c023db62 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -3606,7 +3606,7 @@ static int br_ip6_multicast_query(struct net_bridge_mcast *brmctx,
 		    mld2q->mld2q_suppress)
 			goto out;
 
-		max_delay = max(msecs_to_jiffies(mldv2_mrc(mld2q)), 1UL);
+		max_delay = max(msecs_to_jiffies(mldv2_mrd(mld2q)), 1UL);
 	}
 
 	is_general_query = group && ipv6_addr_any(group);
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 3330adcf26db..6ddc18ac59b9 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1315,20 +1315,7 @@ static void mld_update_qi(struct inet6_dev *idev,
 	 *  - 9.12. Older Version Querier Present Timeout
 	 *    (the [Query Interval] in the last Query received)
 	 */
-	unsigned long mc_qqi;
-
-	if (mlh2->mld2q_qqic < 128) {
-		mc_qqi = mlh2->mld2q_qqic;
-	} else {
-		unsigned long mc_man, mc_exp;
-
-		mc_exp = MLDV2_QQIC_EXP(mlh2->mld2q_qqic);
-		mc_man = MLDV2_QQIC_MAN(mlh2->mld2q_qqic);
-
-		mc_qqi = (mc_man | 0x10) << (mc_exp + 3);
-	}
-
-	idev->mc_qi = mc_qqi * HZ;
+	idev->mc_qi = mldv2_qqi(mlh2) * HZ;
 }
 
 static void mld_update_qri(struct inet6_dev *idev,
@@ -1338,7 +1325,7 @@ static void mld_update_qri(struct inet6_dev *idev,
 	 *  - 5.1.3. Maximum Response Code
 	 *  - 9.3. Query Response Interval
 	 */
-	idev->mc_qri = msecs_to_jiffies(mldv2_mrc(mlh2));
+	idev->mc_qri = msecs_to_jiffies(mldv2_mrd(mlh2));
 }
 
 static int mld_process_v1(struct inet6_dev *idev, struct mld_msg *mld,
@@ -1390,7 +1377,7 @@ static int mld_process_v1(struct inet6_dev *idev, struct mld_msg *mld,
 static void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld,
 			   unsigned long *max_delay)
 {
-	*max_delay = max(msecs_to_jiffies(mldv2_mrc(mld)), 1UL);
+	*max_delay = max(msecs_to_jiffies(mldv2_mrd(mld)), 1UL);
 
 	mld_update_qrv(idev, mld);
 	mld_update_qi(idev, mld);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH net-next v2 3/5] ipv4: igmp: encode multicast exponential fields
  2026-03-30 19:16     ` [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
  2026-03-30 19:16       ` [PATCH net-next v2 1/5] ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation Ujjal Roy
  2026-03-30 19:16       ` [PATCH net-next v2 2/5] ipv6: mld: rename mldv2_mrc() and add mldv2_qqi() Ujjal Roy
@ 2026-03-30 19:16       ` Ujjal Roy
  2026-04-01  7:27         ` Ido Schimmel
  2026-03-30 19:16       ` [PATCH net-next v2 4/5] ipv6: mld: " Ujjal Roy
                         ` (3 subsequent siblings)
  6 siblings, 1 reply; 26+ messages in thread
From: Ujjal Roy @ 2026-03-30 19:16 UTC (permalink / raw)
  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, netdev, linux-kernel, linux-kselftest

In IGMP, QQIC and MRC fields are not currently encoded
when generating query packets. Since the receiver of the
query interprets these fields using the IGMPv3 floating-
point decoding logic, any raw interval value that exceeds
the linear threshold is currently parsed incorrectly as
an exponential value, leading to an incorrect interval
calculation.

Encode and assign the corresponding protocol fields during
query generation. Introduce the logic to dynamically
calculate the exponent and mantissa using bit-scan (fls).
This ensures QQIC and MRC fields (8-bit) are properly
encoded when transmitting query packets with intervals
that exceed their respective linear threshold value of
128 (for QQI/MRT).

RFC 3376: if QQIC/MRC >= 128, the QQIC/MRC field represents
a floating-point value as follows:
     0 1 2 3 4 5 6 7
    +-+-+-+-+-+-+-+-+
    |1| exp | mant  |
    +-+-+-+-+-+-+-+-+

Signed-off-by: Ujjal Roy <royujjal@gmail.com>
---
 include/linux/igmp.h      | 80 +++++++++++++++++++++++++++++++++++++++
 net/bridge/br_multicast.c | 14 +++----
 2 files changed, 86 insertions(+), 8 deletions(-)

diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index 3c12c0a63492..3a4e9cec3959 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -110,6 +110,86 @@ struct ip_mc_list {
 
 /* IGMPV3 floating-point exponential field threshold */
 #define IGMPV3_EXP_MIN_THRESHOLD	128
+/* Max representable (mant = 0xF, exp = 7) -> 31744 */
+#define IGMPV3_EXP_MAX_THRESHOLD	31744
+
+/* V3 exponential field encoding */
+
+/*
+ * IGMPv3 QQIC/MRC 8-bit exponential field encode.
+ *
+ * RFC3376 defines only the decoding formula:
+ * QQI/MRT = (mant | 0x10) << (exp + 3)
+ *
+ * but does NOT define the encoding procedure. To derive exponent:
+ *
+ * For any value of mantissa and exponent, the decoding formula
+ * indicates that the "hidden bit" (0x10) is shifted 4 bits left
+ * to sit above the 4-bit mantissa. The RFC again shifts this
+ * entire block left by (exp + 3) to reconstruct the value.
+ * So, 'hidden bit' is the MSB which is shifted by (4 + exp + 3).
+ *
+ * Total left shift of the 'hidden bit' = 4 + (exp + 3) = exp + 7.
+ * This is the MSB at the 0-based bit position: (exp + 7).
+ * Since fls() is 1-based, fls(value) - 1 = exp + 7.
+ *
+ * Therefore:
+ *     exp  = fls(value) - 8
+ *     mant = (value >> (exp + 3)) & 0x0F
+ *
+ * Final encoding formula:
+ *     0x80 | (exp << 4) | mant
+ *
+ * Example (value = 3200):
+ *  0               1
+ *  0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0| (value = 3200)
+ * |        ^-^-mant^ ^..(exp+3)..^| exp = 4, mant = 9
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Encoded:
+ *   0x80 | (4 << 4) | 9 = 0xC9
+ */
+static inline u8 igmpv3_exp_field_encode(unsigned long value)
+{
+	u8 mc_exp, mc_man;
+
+	/* RFC3376: QQIC/MRC < 128 is literal */
+	if (value < IGMPV3_EXP_MIN_THRESHOLD)
+		return (u8)value;
+
+	/* Saturate at max representable (mant = 0xF, exp = 7) -> 31744 */
+	if (value >= IGMPV3_EXP_MAX_THRESHOLD)
+		return 0xFF;
+
+	mc_exp  = (u8)(fls(value) - 8);
+	mc_man = (u8)((value >> (mc_exp + 3)) & 0x0F);
+
+	return 0x80 | (mc_exp << 4) | mc_man;
+}
+
+/* Calculate Maximum Response Code from Max Resp Time */
+static inline u8 igmpv3_mrc(unsigned long mrt)
+{
+	/* RFC3376, relevant sections:
+	 *  - 4.1.1. Maximum Response Code
+	 *  - 8.3. Query Response Interval
+	 */
+	return igmpv3_exp_field_encode(mrt);
+}
+
+/* Calculate Querier's Query Interval Code from Query Interval */
+static inline u8 igmpv3_qqic(unsigned long qi)
+{
+	/* RFC3376, relevant sections:
+	 *  - 4.1.7. QQIC (Querier's Query Interval Code)
+	 *  - 8.2. Query Interval
+	 *  - 8.12. Older Version Querier Present Timeout
+	 *    (the [Query Interval] in the last Query received)
+	 */
+	return igmpv3_exp_field_encode(qi);
+}
 
 /* V3 exponential field decoding */
 
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 1438c023db62..27010744d7ae 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -934,12 +934,12 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
 	size_t pkt_size, igmp_hdr_size;
 	unsigned long now = jiffies;
 	struct igmpv3_query *ihv3;
+	unsigned long lmqt, mrt;
 	void *csum_start = NULL;
 	__sum16 *csum = NULL;
 	struct sk_buff *skb;
 	struct igmphdr *ih;
 	struct ethhdr *eth;
-	unsigned long lmqt;
 	struct iphdr *iph;
 	u16 lmqt_srcs = 0;
 
@@ -1004,15 +1004,15 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
 	skb_put(skb, 24);
 
 	skb_set_transport_header(skb, skb->len);
+	mrt = group ? brmctx->multicast_last_member_interval :
+		      brmctx->multicast_query_response_interval;
 	*igmp_type = IGMP_HOST_MEMBERSHIP_QUERY;
 
 	switch (brmctx->multicast_igmp_version) {
 	case 2:
 		ih = igmp_hdr(skb);
 		ih->type = IGMP_HOST_MEMBERSHIP_QUERY;
-		ih->code = (group ? brmctx->multicast_last_member_interval :
-				    brmctx->multicast_query_response_interval) /
-			   (HZ / IGMP_TIMER_SCALE);
+		ih->code = mrt / (HZ / IGMP_TIMER_SCALE);
 		ih->group = group;
 		ih->csum = 0;
 		csum = &ih->csum;
@@ -1021,11 +1021,9 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
 	case 3:
 		ihv3 = igmpv3_query_hdr(skb);
 		ihv3->type = IGMP_HOST_MEMBERSHIP_QUERY;
-		ihv3->code = (group ? brmctx->multicast_last_member_interval :
-				      brmctx->multicast_query_response_interval) /
-			     (HZ / IGMP_TIMER_SCALE);
+		ihv3->code = igmpv3_mrc(mrt / (HZ / IGMP_TIMER_SCALE));
 		ihv3->group = group;
-		ihv3->qqic = brmctx->multicast_query_interval / HZ;
+		ihv3->qqic = igmpv3_qqic(brmctx->multicast_query_interval / HZ);
 		ihv3->nsrcs = htons(lmqt_srcs);
 		ihv3->resv = 0;
 		ihv3->suppress = sflag;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH net-next v2 4/5] ipv6: mld: encode multicast exponential fields
  2026-03-30 19:16     ` [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
                         ` (2 preceding siblings ...)
  2026-03-30 19:16       ` [PATCH net-next v2 3/5] ipv4: igmp: encode multicast exponential fields Ujjal Roy
@ 2026-03-30 19:16       ` Ujjal Roy
  2026-04-01  7:29         ` Ido Schimmel
  2026-03-30 19:16       ` [PATCH net-next v2 5/5] selftests: net: bridge: add tests for igmpv3 MRC and QQIC validation Ujjal Roy
                         ` (2 subsequent siblings)
  6 siblings, 1 reply; 26+ messages in thread
From: Ujjal Roy @ 2026-03-30 19:16 UTC (permalink / raw)
  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, netdev, linux-kernel, linux-kselftest

In MLD, QQIC and MRC fields are not currently encoded when
generating query packets. Since the receiver of the query
interprets these fields using the MLDv2 floating-point
decoding logic, any raw interval value that exceeds the
linear threshold is currently parsed incorrectly as an
exponential value, leading to an incorrect interval
calculation.

Encode and assign the corresponding protocol fields during
query generation. Introduce the logic to dynamically
calculate the exponent and mantissa using bit-scan (fls).
This ensures QQIC (8-bit) and MRC (16-bit) fields are
properly encoded when transmitting query packets with
intervals that exceed their respective linear thresholds
(128 for QQI; 32768 for MRD).

RFC3810: If QQIC >= 128, the QQIC field represents a
floating-point value as follows:
     0 1 2 3 4 5 6 7
    +-+-+-+-+-+-+-+-+
    |1| exp | mant  |
    +-+-+-+-+-+-+-+-+

RFC3810: If Maximum Response Code >= 32768, the Maximum
Response Code field represents a floating-point value as
follows:
     0 1 2 3 4 5 6 7 8 9 A B C D E F
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |1| exp |          mant         |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Signed-off-by: Ujjal Roy <royujjal@gmail.com>
---
 include/net/mld.h         | 117 ++++++++++++++++++++++++++++++++++++++
 net/bridge/br_multicast.c |   4 +-
 2 files changed, 119 insertions(+), 2 deletions(-)

diff --git a/include/net/mld.h b/include/net/mld.h
index 5e093f9c75d9..1ee7d3f68a33 100644
--- a/include/net/mld.h
+++ b/include/net/mld.h
@@ -90,12 +90,129 @@ struct mld2_query {
 #define MLDV2_QQIC_MAN(value)	((value) & 0x0f)
 
 #define MLD_QQIC_MIN_THRESHOLD	128
+/* Max representable (mant = 0xF, exp = 7) -> 31744 */
+#define MLD_QQIC_MAX_THRESHOLD	31744
 #define MLD_MRC_MIN_THRESHOLD	32768UL
+/* Max representable (mant = 0xFFF, exp = 7) -> 8387584 */
+#define MLD_MRC_MAX_THRESHOLD	8387584
 #define MLDV1_MRD_MAX_COMPAT	(MLD_MRC_MIN_THRESHOLD - 1)
 
 #define MLD_MAX_QUEUE		8
 #define MLD_MAX_SKBS		32
 
+/* V2 exponential field encoding */
+
+/*
+ * Calculate Maximum Response Code from Maximum Response Delay
+ *
+ * MLDv2 Maximum Response Code 16-bit encoding (RFC3810).
+ *
+ * RFC3810 defines only the decoding formula:
+ * Maximum Response Delay = (mant | 0x1000) << (exp + 3)
+ *
+ * but does NOT define the encoding procedure. To derive exponent:
+ *
+ * For the 16-bit MRC, the "hidden bit" (0x1000) is left shifted by 12
+ * to sit above the 12-bit mantissa. The RFC then shifts this entire
+ * block left by (exp + 3) to reconstruct the value.
+ * So, 'hidden bit' is the MSB which is shifted by (12 + exp + 3).
+ *
+ * - Total left shift of the hidden bit = 12 + (exp + 3) = exp + 15.
+ * - This is the MSB at the 0-based bit position: (exp + 15).
+ * - Since fls() is 1-based, fls(value) - 1 = exp + 15.
+ *
+ * Therefore:
+ *     exp  = fls(value) - 16
+ *     mant = (value >> (exp + 3)) & 0x0FFF
+ *
+ * Final encoding formula:
+ *     0x8000 | (exp << 12) | mant
+ *
+ * Example (value = 1311744):
+ *  0               1               2               3
+ *  0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0| 1311744
+ * |                      ^-^--------mant---------^ ^...(exp+3)...^| exp=5
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Encoded:
+ *   0x8000 | (5 << 12) | 0x404 = 0xD404
+ */
+static inline u16 mldv2_mrc(unsigned long mrd)
+{
+	u16 mc_man, mc_exp;
+
+	/* RFC3810: MRC < 32768 is literal */
+	if (mrd < MLD_MRC_MIN_THRESHOLD)
+		return (u16)mrd;
+
+	/* Saturate at max representable (mant = 0xFFF, exp = 7) -> 8387584 */
+	if (mrd >= MLD_MRC_MAX_THRESHOLD)
+		return 0xFFFF;
+
+	mc_exp = (u16)(fls(mrd) - 16);
+	mc_man = (u16)((mrd >> (mc_exp + 3)) & 0x0FFF);
+
+	return (0x8000 | (mc_exp << 12) | mc_man);
+}
+
+/*
+ * Calculate Querier's Query Interval Code from Query Interval
+ *
+ * MLDv2 QQIC 8-bit floating-point encoding (RFC3810).
+ *
+ * RFC3810 defines only the decoding formula:
+ * QQI = (mant | 0x10) << (exp + 3)
+ *
+ * but does NOT define the encoding procedure. To derive exponent:
+ *
+ * For any value of mantissa and exponent, the decoding formula
+ * indicates that the "hidden bit" (0x10) is shifted 4 bits left
+ * to sit above the 4-bit mantissa. The RFC again shifts this
+ * entire block left by (exp + 3) to reconstruct the value.
+ * So, 'hidden bit' is the MSB which is shifted by (4 + exp + 3).
+ *
+ * Total left shift of the 'hidden bit' = 4 + (exp + 3) = exp + 7.
+ * This is the MSB at the 0-based bit position: (exp + 7).
+ * Since fls() is 1-based, fls(value) - 1 = exp + 7.
+ *
+ * Therefore:
+ *     exp  = fls(value) - 8
+ *     mant = (value >> (exp + 3)) & 0x0F
+ *
+ * Final encoding formula:
+ *     0x80 | (exp << 4) | mant
+ *
+ * Example (value = 3200):
+ *  0               1
+ *  0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0| (value = 3200)
+ * |        ^-^-mant^ ^..(exp+3)..^| exp = 4, mant = 9
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Encoded:
+ *   0x80 | (4 << 4) | 9 = 0xC9
+ */
+static inline u8 mldv2_qqic(unsigned long value)
+{
+	u8 mc_man, mc_exp;
+
+	/* RFC3810: QQIC < 128 is literal */
+	if (value < MLD_QQIC_MIN_THRESHOLD)
+		return (u8)value;
+
+	/* Saturate at max representable (mant = 0xF, exp = 7) -> 31744 */
+	if (value >= MLD_QQIC_MAX_THRESHOLD)
+		return 0xFF;
+
+	mc_exp  = (u8)(fls(value) - 8);
+	mc_man = (u8)((value >> (mc_exp + 3)) & 0x0F);
+
+	return (0x80 | (mc_exp << 4) | mc_man);
+}
+
 /* V2 exponential field decoding */
 
 /* Calculate Maximum Response Delay from Maximum Response Code
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 27010744d7ae..c2d144f6a86e 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -1181,7 +1181,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
 		break;
 	case 2:
 		mld2q = (struct mld2_query *)icmp6_hdr(skb);
-		mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(interval));
+		mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(mldv2_mrc(interval)));
 		mld2q->mld2q_type = ICMPV6_MGM_QUERY;
 		mld2q->mld2q_code = 0;
 		mld2q->mld2q_cksum = 0;
@@ -1190,7 +1190,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
 		mld2q->mld2q_suppress = sflag;
 		mld2q->mld2q_qrv = 2;
 		mld2q->mld2q_nsrcs = htons(llqt_srcs);
-		mld2q->mld2q_qqic = brmctx->multicast_query_interval / HZ;
+		mld2q->mld2q_qqic = mldv2_qqic(brmctx->multicast_query_interval / HZ);
 		mld2q->mld2q_mca = *group;
 		csum = &mld2q->mld2q_cksum;
 		csum_start = (void *)mld2q;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH net-next v2 5/5] selftests: net: bridge: add tests for igmpv3 MRC and QQIC validation
  2026-03-30 19:16     ` [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
                         ` (3 preceding siblings ...)
  2026-03-30 19:16       ` [PATCH net-next v2 4/5] ipv6: mld: " Ujjal Roy
@ 2026-03-30 19:16       ` Ujjal Roy
  2026-03-31 14:13         ` Ido Schimmel
  2026-03-30 19:39       ` [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
  2026-03-31 14:16       ` Ido Schimmel
  6 siblings, 1 reply; 26+ messages in thread
From: Ujjal Roy @ 2026-03-30 19:16 UTC (permalink / raw)
  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, netdev, linux-kernel, linux-kselftest

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 <royujjal@gmail.com>
---
 .../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/testing/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/testing/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
 
+top_srcdir = ../../../../..
+
+CFLAGS += -Wall -Wl,--no-as-needed -O2 -g -I$(top_srcdir)/usr/include $(KHDR_INCLUDES)
+CFLAGS += -I$(top_srcdir)/tools/include
+
+TEST_GEN_FILES := \
+	mc_encode \
+	mc_decode \
+# end of TEST_GEN_FILES
+
 TEST_PROGS := \
 	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
 
 ALL_TESTS="
+	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
 
 	ip link del dev br0
 }
@@ -116,6 +119,112 @@ cleanup()
 	vrf_cleanup
 }
 
+check_binary()
+{
+	local cmd=$1; shift
+	local args=$@
+
+	if [[ ! -x "$(command -v "$cmd")" ]]; then
+		log_test_skip "$args $cmd not found"
+		return $EXIT_STATUS
+	fi
+}
+
+tcpdump_show_with_filter()
+{
+	local if_name=$1; shift
+	local filter=$@
+
+	tcpdump -e -n -r ${capfile[$if_name]} "$filter" 2>&1
+}
+
+validate_query()
+{
+	local if_name=$1; shift
+	local test="$1"; shift
+	local value="$1"; shift
+	local pattern=""
+	local field_val=""
+	local pos=""
+	local msg="IGMPv3 query: verify $test"
+	check_command tshark || return 1
+	check_binary "./mc_encode" $msg || return 1
+
+	if [ "$test" = "MRC" ]; then
+		pos=1 # MRC field offset within IGMP header
+		field_val=$(tshark -r ${capfile[$if_name]} -Y "igmp.type==0x11" \
+			   -V 2>/dev/null | grep "Max Resp Time")
+	elif [ "$test" = "QQIC" ]; then
+		pos=9 # QQIC field offset within IGMP header
+		field_val=$(tshark -r ${capfile[$if_name]} -Y "igmp.type==0x11" \
+			   -V 2>/dev/null | grep "QQIC")
+	fi
+
+	local enc_val=$(./mc_encode $value)
+	pattern="ip proto 2 and igmp[0] == 0x11 and igmp[$pos] == $enc_val"
+	local opt_str=""
+	tcpdump_show_with_filter $if_name $pattern | grep -q "igmp query v3"
+	ret=$?
+	if [ "$field_val" != "" -a $ret -ne 0 ]; then
+		opt_str="Bad $test value in IGMP packet: $field_val"
+	fi
+	check_err $ret "$opt_str"
+
+	log_test "$msg" "configured=$value, expected=$enc_val"
+}
+
+v3query_mrc_test()
+{
+	RET=0
+	local qri_val=160
+	local br_qri=$((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=0
+	local qqi_val=160
+	local br_qqi=$((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=0
diff --git a/tools/testing/selftests/net/forwarding/mc_decode.c b/tools/testing/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 <stdio.h>
+#include <stdlib.h>
+#include <linux/bitops.h>
+
+#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 = IGMPV3_FP_EXP(code);
+        mc_man = IGMPV3_FP_MAN(code);
+        return ((mc_man | 0x10) << (mc_exp + 3));
+    }
+}
+
+int main(int argc, char *argv[])
+{
+    uint8_t qqic = 0;
+    if (argc >= 2)
+        qqic = atoi(argv[1]);
+    unsigned long qqi = decode_field(qqic);
+
+    printf("%lu\n", qqi);
+
+    return 0;
+}
diff --git a/tools/testing/selftests/net/forwarding/mc_encode.c b/tools/testing/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 <stdio.h>
+#include <stdlib.h>
+#include <linux/bitops.h>
+
+/* IGMPV3 floating-point exponential field threshold */
+#define IGMPV3_EXP_MIN_THRESHOLD        128
+/* Max representable (mant = 0xF, exp = 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 = 0xF, exp = 7) -> 31744 */
+    if (value >= IGMPV3_EXP_MAX_THRESHOLD)
+        return 0xFF;
+
+    mc_exp  = (uint8_t)(fls(value) - 8);
+    mc_man = (uint8_t)((value >> (mc_exp + 3)) & 0x0F);
+
+    return 0x80 | (mc_exp << 4) | mc_man;
+}
+
+int main(int argc, char *argv[])
+{
+    unsigned int qqi = 0;
+    if (argc >= 2)
+        qqi = atoi(argv[1]);
+
+    uint8_t qqic = encode_field(qqi);
+
+    printf("%u\n", qqic);
+
+    return 0;
+}
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding
  2026-03-30 19:16     ` [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
                         ` (4 preceding siblings ...)
  2026-03-30 19:16       ` [PATCH net-next v2 5/5] selftests: net: bridge: add tests for igmpv3 MRC and QQIC validation Ujjal Roy
@ 2026-03-30 19:39       ` Ujjal Roy
  2026-03-31 14:16       ` Ido Schimmel
  6 siblings, 0 replies; 26+ messages in thread
From: Ujjal Roy @ 2026-03-30 19:39 UTC (permalink / raw)
  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, netdev, linux-kernel, linux-kselftest

On Tue, Mar 31, 2026 at 12:46 AM Ujjal Roy <royujjal@gmail.com> wrote:
>
> Description:
> This series addresses a mismatch in how multicast query
> intervals and response codes are handled across IPv4 (IGMPv3)
> and IPv6 (MLDv2). While decoding logic currently exists,
> the corresponding encoding logic is missing during query
> packet generation. This leads to incorrect intervals being
> transmitted when values exceed their linear thresholds.
>
> The patches introduce a unified floating-point encoding
> approach based on RFC3376 and RFC3810, ensuring that large
> intervals are correctly represented in QQIC and MRC fields
> using the exponent-mantissa format.
>
> Key Changes:
> * ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation
>   Removes legacy macros in favor of a cleaner, unified
>   calculation for retrieving intervals from encoded fields,
>   improving code maintainability.
>
> * ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
>   Standardizes MLDv2 terminology by renaming mldv2_mrc()
>   to mldv2_mrd() (Maximum Response Delay) and introducing
>   a new API mldv2_qqi for QQI calculation, improving code
>   readability.
>
> * ipv4: igmp: encode multicast exponential fields
>   Introduces the logic to dynamically calculate the exponent
>   and mantissa using bit-scan (fls). This ensures QQIC and
>   MRC fields (8-bit) are properly encoded when transmitting
>   query packets with intervals that exceed their respective
>   linear threshold value of 128 (for QQI/MRT).
>
> * ipv6: mld: encode multicast exponential fields
>   Applies similar encoding logic for MLDv2. This ensures
>   QQIC (8-bit) and MRC (16-bit) fields are properly encoded
>   when transmitting query packets with intervals that exceed
>   their respective linear thresholds (128 for QQI; 32768
>   for MRD).
>
> Impact:
> These changes ensure that multicast queriers and listeners
> stay synchronized on timing intervals, preventing protocol
> timeouts or premature group membership expiration caused
> by incorrectly formatted packet headers.
>
> v2:
> - Retargeted the series to net-next as suggested
> - Fixed a compilation warning in the MLD rename change
> - Kept reverse xmas tree order in IGMP exponential encoding change
> - Added bridge selftests to validate IGMPv3 Query MRC and QQIC handling
>
> Ujjal Roy (5):
>   ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation
>   ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
>   ipv4: igmp: encode multicast exponential fields
>   ipv6: mld: encode multicast exponential fields
>   selftests: net: bridge: add tests for igmpv3 MRC and QQIC validation
>
>  include/linux/igmp.h                          | 158 +++++++++++++++-
>  include/net/mld.h                             | 179 +++++++++++++++++-
>  net/bridge/br_multicast.c                     |  22 +--
>  net/ipv4/igmp.c                               |   6 +-
>  net/ipv6/mcast.c                              |  19 +-
>  .../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 ++++
>  10 files changed, 535 insertions(+), 48 deletions(-)
>  create mode 100644 tools/testing/selftests/net/forwarding/mc_decode.c
>  create mode 100644 tools/testing/selftests/net/forwarding/mc_encode.c
>
>
> base-commit: cf0d9080c6f795bc6be08babbffa29b62c06e9b0
> --
> 2.43.0
>

Forgot to attach test result using bridge_igmp.sh selftest.
First two test cases are added newly v3query_mrc_test and v3query_qqic_test.

Without these patches:
tools/testing/selftests/net/forwarding# ./bridge_igmp.sh
TEST: IGMPv3 query: verify MRC (configured=160, expected=132)       [FAIL]
        Bad MRC value in IGMP packet:     Max Resp Time: 51.2 sec (0xa0)
TEST: IGMPv3 query: verify QQIC (configured=160, expected=132)      [FAIL]
        Bad QQIC value in IGMP packet:     QQIC: 160
TEST: IGMPv2 report 239.10.10.10                                    [ OK ]
...

With these patches:
tools/testing/selftests/net/forwarding# ./bridge_igmp.sh
TEST: IGMPv3 query: verify MRC (configured=160, expected=132)       [ OK ]
TEST: IGMPv3 query: verify QQIC (configured=160, expected=132)      [ OK ]
TEST: IGMPv2 report 239.10.10.10                                    [ OK ]
...

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH net-next v2 5/5] selftests: net: bridge: add tests for igmpv3 MRC and QQIC validation
  2026-03-30 19:16       ` [PATCH net-next v2 5/5] selftests: net: bridge: add tests for igmpv3 MRC and QQIC validation Ujjal Roy
@ 2026-03-31 14:13         ` Ido Schimmel
  2026-04-03  7:50           ` Ujjal Roy
  0 siblings, 1 reply; 26+ messages in thread
From: Ido Schimmel @ 2026-03-31 14:13 UTC (permalink / raw)
  To: Ujjal Roy
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, David Ahern, Shuah Khan,
	Andy Roulin, Yong Wang, Petr Machata, Ujjal Roy, bridge, netdev,
	linux-kernel, linux-kselftest

On Mon, Mar 30, 2026 at 07:16:11PM +0000, Ujjal Roy wrote:
> 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.

Please cover both IGMPv3 and MLDv2 since the patchset touches both. You
can add the MLDv2 tests in bridge_mld.sh.

Also, I think you can simplify the test by simply matching on the
expected values of MRC and QQIC using tc-u32.

For both IGMPv3 and MLDv2 please test both:

* MRC and QQIC in linear range.
* MRC and QQIC in non-linear range.

And please make sure the new test cases don't add new shellcheck
warnings / errors or it will fail in the CI. You can ignore existing
ones.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding
  2026-03-30 19:16     ` [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
                         ` (5 preceding siblings ...)
  2026-03-30 19:39       ` [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
@ 2026-03-31 14:16       ` Ido Schimmel
  2026-04-04 17:25         ` Ujjal Roy
  6 siblings, 1 reply; 26+ messages in thread
From: Ido Schimmel @ 2026-03-31 14:16 UTC (permalink / raw)
  To: Ujjal Roy
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, David Ahern, Shuah Khan,
	Andy Roulin, Yong Wang, Petr Machata, Ujjal Roy, bridge, netdev,
	linux-kernel, linux-kselftest

On Mon, Mar 30, 2026 at 07:16:06PM +0000, Ujjal Roy wrote:
> Description:
> This series addresses a mismatch in how multicast query
> intervals and response codes are handled across IPv4 (IGMPv3)
> and IPv6 (MLDv2). While decoding logic currently exists,
> the corresponding encoding logic is missing during query
> packet generation. This leads to incorrect intervals being
> transmitted when values exceed their linear thresholds.

Thanks for the patches. I will take a look, but please read:

https://docs.kernel.org/process/maintainer-netdev.html

In particular:

"The new version of patches should be posted as a separate thread, not
as a reply to the previous posting."

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH net-next v2 1/5] ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation
  2026-03-30 19:16       ` [PATCH net-next v2 1/5] ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation Ujjal Roy
@ 2026-04-01  7:25         ` Ido Schimmel
  0 siblings, 0 replies; 26+ messages in thread
From: Ido Schimmel @ 2026-04-01  7:25 UTC (permalink / raw)
  To: Ujjal Roy
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, David Ahern, Shuah Khan,
	Andy Roulin, Yong Wang, Petr Machata, Ujjal Roy, bridge, netdev,
	linux-kernel, linux-kselftest

On Mon, Mar 30, 2026 at 07:16:07PM +0000, Ujjal Roy wrote:
> Get rid of the IGMPV3_MRC macro and use the igmpv3_mrt() API to
> calculate the Max Resp Time from the Maximum Response Code.
> 
> Similarly, for IGMPV3_QQIC, use the igmpv3_qqi() API to calculate
> the Querier's Query Interval from the QQIC field.
> 
> Signed-off-by: Ujjal Roy <royujjal@gmail.com>
> ---
>  include/linux/igmp.h      | 78 +++++++++++++++++++++++++++++++++++----
>  net/bridge/br_multicast.c |  2 +-
>  net/ipv4/igmp.c           |  6 +--
>  3 files changed, 74 insertions(+), 12 deletions(-)
> 
> diff --git a/include/linux/igmp.h b/include/linux/igmp.h
> index 073b30a9b850..3c12c0a63492 100644
> --- a/include/linux/igmp.h
> +++ b/include/linux/igmp.h
> @@ -92,15 +92,77 @@ struct ip_mc_list {
>  	struct rcu_head		rcu;
>  };
>  
> +/* RFC3376, relevant sections:
> + *  - 4.1.1. Maximum Response Code
> + *  - 4.1.7. QQIC (Querier's Query Interval Code)
> + *
> + * If Max Resp Code >= 128, Max Resp Code represents a floating-point
> + * value as follows:
> + * If QQIC >= 128, QQIC represents a floating-point value as follows:

You have "as follows:" twice. Reword it to make it clear that both
fields are encoded the same.

Please also mention that MRT is in units of 100ms (0.1 seconds) and that
QQIC is in units of seconds.

> + *
> + *  0 1 2 3 4 5 6 7
> + * +-+-+-+-+-+-+-+-+
> + * |1| exp | mant  |
> + * +-+-+-+-+-+-+-+-+
> + */
> +#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
> +
>  /* V3 exponential field decoding */
> -#define IGMPV3_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value))
> -#define IGMPV3_EXP(thresh, nbmant, nbexp, value) \
> -	((value) < (thresh) ? (value) : \
> -        ((IGMPV3_MASK(value, nbmant) | (1<<(nbmant))) << \
> -         (IGMPV3_MASK((value) >> (nbmant), nbexp) + (nbexp))))
> -
> -#define IGMPV3_QQIC(value) IGMPV3_EXP(0x80, 4, 3, value)
> -#define IGMPV3_MRC(value) IGMPV3_EXP(0x80, 4, 3, value)
> +
> +/*
> + * IGMPv3 QQI/MRT 8-bit exponential field decode.
> + *
> + * RFC3376, 4.1.1 & 4.1.7. defines the decoding formula:
> + *      0 1 2 3 4 5 6 7
> + *     +-+-+-+-+-+-+-+-+
> + *     |1| exp | mant  |
> + *     +-+-+-+-+-+-+-+-+
> + * Max Resp Time = (mant | 0x10) << (exp + 3)
> + * QQI = (mant | 0x10) << (exp + 3)
> + */

You are mixing two different styles of comments. Please use netdev style
comments like you have above:

/* IGMPv3 QQI/MRT 8-bit exponential field decode.
 * [...]
 */

Same in other places throughout the patchset.

> +static inline unsigned long igmpv3_exp_field_decode(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;

return code;

> +	} else {
> +		unsigned long mc_man, mc_exp;
> +
> +		mc_exp = IGMPV3_FP_EXP(code);
> +		mc_man = IGMPV3_FP_MAN(code);
> +
> +		return ((mc_man | 0x10) << (mc_exp + 3));

return (mc_man | 0x10) << (mc_exp + 3);

> +	}
> +}
> +
> +/* Calculate Max Resp Time from Maximum Response Code */
> +static inline unsigned long igmpv3_mrt(const struct igmpv3_query *ih3)
> +{
> +	/* RFC3376, relevant sections:
> +	 *  - 4.1.1. Maximum Response Code
> +	 *  - 8.3. Query Response Interval
> +	 */
> +	return igmpv3_exp_field_decode(ih3->code);
> +}
> +
> +/* Calculate Querier's Query Interval from Querier's Query Interval Code */
> +static inline unsigned long igmpv3_qqi(const struct igmpv3_query *ih3)
> +{
> +	/* RFC3376, relevant sections:
> +	 *  - 4.1.7. QQIC (Querier's Query Interval Code)
> +	 *  - 8.2. Query Interval
> +	 *  - 8.12. Older Version Querier Present Timeout
> +	 *    (the [Query Interval] in the last Query received)
> +	 */
> +	return igmpv3_exp_field_decode(ih3->qqic);
> +}
>  
>  static inline int ip_mc_may_pull(struct sk_buff *skb, unsigned int len)
>  {
> diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
> index 881d866d687a..9fec76e887bc 100644
> --- a/net/bridge/br_multicast.c
> +++ b/net/bridge/br_multicast.c
> @@ -3518,7 +3518,7 @@ static void br_ip4_multicast_query(struct net_bridge_mcast *brmctx,
>  			goto out;
>  
>  		max_delay = ih3->code ?
> -			    IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
> +			    igmpv3_mrt(ih3) * (HZ / IGMP_TIMER_SCALE) : 1;
>  	} else {
>  		goto out;
>  	}
> diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
> index a674fb44ec25..8c6102737096 100644
> --- a/net/ipv4/igmp.c
> +++ b/net/ipv4/igmp.c
> @@ -991,7 +991,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
>  		 * different encoding. We use the v3 encoding as more likely
>  		 * to be intended in a v3 query.
>  		 */
> -		max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
> +		max_delay = igmpv3_mrt(ih3)*(HZ/IGMP_TIMER_SCALE);

Given you are modifying the line, adjust it to:

max_delay = igmpv3_mrt(ih3) * (HZ / IGMP_TIMER_SCALE);

Same in other places where you are already modifying a line.

>  		if (!max_delay)
>  			max_delay = 1;	/* can't mod w/ 0 */
>  	} else { /* v3 */
> @@ -1006,7 +1006,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
>  			ih3 = igmpv3_query_hdr(skb);
>  		}
>  
> -		max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
> +		max_delay = igmpv3_mrt(ih3)*(HZ/IGMP_TIMER_SCALE);
>  		if (!max_delay)
>  			max_delay = 1;	/* can't mod w/ 0 */
>  		WRITE_ONCE(in_dev->mr_maxdelay, max_delay);
> @@ -1016,7 +1016,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
>  		 * configured value.
>  		 */
>  		in_dev->mr_qrv = ih3->qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
> -		in_dev->mr_qi = IGMPV3_QQIC(ih3->qqic)*HZ ?: IGMP_QUERY_INTERVAL;
> +		in_dev->mr_qi = igmpv3_qqi(ih3)*HZ ?: IGMP_QUERY_INTERVAL;
>  
>  		/* RFC3376, 8.3. Query Response Interval:
>  		 * The number of seconds represented by the [Query Response
> -- 
> 2.43.0
> 

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH net-next v2 2/5] ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
  2026-03-30 19:16       ` [PATCH net-next v2 2/5] ipv6: mld: rename mldv2_mrc() and add mldv2_qqi() Ujjal Roy
@ 2026-04-01  7:26         ` Ido Schimmel
  0 siblings, 0 replies; 26+ messages in thread
From: Ido Schimmel @ 2026-04-01  7:26 UTC (permalink / raw)
  To: Ujjal Roy
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, David Ahern, Shuah Khan,
	Andy Roulin, Yong Wang, Petr Machata, Ujjal Roy, bridge, netdev,
	linux-kernel, linux-kselftest

On Mon, Mar 30, 2026 at 07:16:08PM +0000, Ujjal Roy wrote:
> Rename mldv2_mrc() to mldv2_mrd() as it used to calculate

s/as it used/as it is used/

> the Maximum Response Delay from the Maximum Response Code.
> 
> Introduce a new API mldv2_qqi() to define the existing
> calculation logic of QQI from QQIC. This also organizes
> the existing mld_update_qi() API.
> 
> Added by e3f5b1704 ("net: ipv6: mld: get rid of MLDV2_MRC and simplify calculation").

I don't see a point in mentioning this.

> 
> Signed-off-by: Ujjal Roy <royujjal@gmail.com>
> ---
>  include/net/mld.h         | 64 +++++++++++++++++++++++++++++++++------
>  net/bridge/br_multicast.c |  2 +-
>  net/ipv6/mcast.c          | 19 ++----------
>  3 files changed, 58 insertions(+), 27 deletions(-)

This is mostly similar to the previous patch, so the same comments apply
here.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH net-next v2 3/5] ipv4: igmp: encode multicast exponential fields
  2026-03-30 19:16       ` [PATCH net-next v2 3/5] ipv4: igmp: encode multicast exponential fields Ujjal Roy
@ 2026-04-01  7:27         ` Ido Schimmel
  0 siblings, 0 replies; 26+ messages in thread
From: Ido Schimmel @ 2026-04-01  7:27 UTC (permalink / raw)
  To: Ujjal Roy
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, David Ahern, Shuah Khan,
	Andy Roulin, Yong Wang, Petr Machata, Ujjal Roy, bridge, netdev,
	linux-kernel, linux-kselftest

On Mon, Mar 30, 2026 at 07:16:09PM +0000, Ujjal Roy wrote:
> In IGMP, QQIC and MRC fields are not currently encoded

s/currently/correctly/ ?

> when generating query packets. Since the receiver of the
> query interprets these fields using the IGMPv3 floating-
> point decoding logic, any raw interval value that exceeds
> the linear threshold is currently parsed incorrectly as
> an exponential value, leading to an incorrect interval
> calculation.
> 
> Encode and assign the corresponding protocol fields during
> query generation. Introduce the logic to dynamically
> calculate the exponent and mantissa using bit-scan (fls).
> This ensures QQIC and MRC fields (8-bit) are properly
> encoded when transmitting query packets with intervals
> that exceed their respective linear threshold value of
> 128 (for QQI/MRT).
> 
> RFC 3376: if QQIC/MRC >= 128, the QQIC/MRC field represents
> a floating-point value as follows:
>      0 1 2 3 4 5 6 7
>     +-+-+-+-+-+-+-+-+
>     |1| exp | mant  |
>     +-+-+-+-+-+-+-+-+
> 
> Signed-off-by: Ujjal Roy <royujjal@gmail.com>

[...]

> +static inline u8 igmpv3_exp_field_encode(unsigned long value)
> +{
> +	u8 mc_exp, mc_man;
> +
> +	/* RFC3376: QQIC/MRC < 128 is literal */
> +	if (value < IGMPV3_EXP_MIN_THRESHOLD)
> +		return (u8)value;

return value;

> +
> +	/* Saturate at max representable (mant = 0xF, exp = 7) -> 31744 */
> +	if (value >= IGMPV3_EXP_MAX_THRESHOLD)
> +		return 0xFF;
> +
> +	mc_exp  = (u8)(fls(value) - 8);
> +	mc_man = (u8)((value >> (mc_exp + 3)) & 0x0F);

Drop the casts?

Same in other places throughout the patchset.

> +
> +	return 0x80 | (mc_exp << 4) | mc_man;
> +}

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH net-next v2 4/5] ipv6: mld: encode multicast exponential fields
  2026-03-30 19:16       ` [PATCH net-next v2 4/5] ipv6: mld: " Ujjal Roy
@ 2026-04-01  7:29         ` Ido Schimmel
  0 siblings, 0 replies; 26+ messages in thread
From: Ido Schimmel @ 2026-04-01  7:29 UTC (permalink / raw)
  To: Ujjal Roy
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, David Ahern, Shuah Khan,
	Andy Roulin, Yong Wang, Petr Machata, Ujjal Roy, bridge, netdev,
	linux-kernel, linux-kselftest

On Mon, Mar 30, 2026 at 07:16:10PM +0000, Ujjal Roy wrote:
> In MLD, QQIC and MRC fields are not currently encoded when

s/currently/correctly/

> generating query packets. Since the receiver of the query
> interprets these fields using the MLDv2 floating-point
> decoding logic, any raw interval value that exceeds the
> linear threshold is currently parsed incorrectly as an
> exponential value, leading to an incorrect interval
> calculation.

[...]

> +static inline u16 mldv2_mrc(unsigned long mrd)
> +{
> +	u16 mc_man, mc_exp;
> +
> +	/* RFC3810: MRC < 32768 is literal */
> +	if (mrd < MLD_MRC_MIN_THRESHOLD)
> +		return (u16)mrd;
> +
> +	/* Saturate at max representable (mant = 0xFFF, exp = 7) -> 8387584 */
> +	if (mrd >= MLD_MRC_MAX_THRESHOLD)
> +		return 0xFFFF;
> +
> +	mc_exp = (u16)(fls(mrd) - 16);
> +	mc_man = (u16)((mrd >> (mc_exp + 3)) & 0x0FFF);
> +
> +	return (0x8000 | (mc_exp << 12) | mc_man);
> +}

[...]

> diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
> index 27010744d7ae..c2d144f6a86e 100644
> --- a/net/bridge/br_multicast.c
> +++ b/net/bridge/br_multicast.c
> @@ -1181,7 +1181,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
>  		break;
>  	case 2:
>  		mld2q = (struct mld2_query *)icmp6_hdr(skb);
> -		mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(interval));
> +		mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(mldv2_mrc(interval)));

This looks wrong. mldv2_mrc() is supposed to receive the maximum
response delay in milliseconds, but you are passing jiffies.

>  		mld2q->mld2q_type = ICMPV6_MGM_QUERY;
>  		mld2q->mld2q_code = 0;
>  		mld2q->mld2q_cksum = 0;
> @@ -1190,7 +1190,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
>  		mld2q->mld2q_suppress = sflag;
>  		mld2q->mld2q_qrv = 2;
>  		mld2q->mld2q_nsrcs = htons(llqt_srcs);
> -		mld2q->mld2q_qqic = brmctx->multicast_query_interval / HZ;
> +		mld2q->mld2q_qqic = mldv2_qqic(brmctx->multicast_query_interval / HZ);
>  		mld2q->mld2q_mca = *group;
>  		csum = &mld2q->mld2q_cksum;
>  		csum_start = (void *)mld2q;
> -- 
> 2.43.0
> 

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH net-next v2 5/5] selftests: net: bridge: add tests for igmpv3 MRC and QQIC validation
  2026-03-31 14:13         ` Ido Schimmel
@ 2026-04-03  7:50           ` Ujjal Roy
  2026-04-03 10:19             ` Ido Schimmel
  0 siblings, 1 reply; 26+ messages in thread
From: Ujjal Roy @ 2026-04-03  7:50 UTC (permalink / raw)
  To: Ido Schimmel
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, David Ahern, Shuah Khan,
	Andy Roulin, Yong Wang, Petr Machata, Ujjal Roy, bridge, netdev,
	linux-kernel, linux-kselftest

On Tue, Mar 31, 2026 at 7:44 PM Ido Schimmel <idosch@nvidia.com> wrote:
>
> On Mon, Mar 30, 2026 at 07:16:11PM +0000, Ujjal Roy wrote:
> > 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.
>
> Please cover both IGMPv3 and MLDv2 since the patchset touches both. You
> can add the MLDv2 tests in bridge_mld.sh.
>
> Also, I think you can simplify the test by simply matching on the
> expected values of MRC and QQIC using tc-u32.
>
> For both IGMPv3 and MLDv2 please test both:
>
> * MRC and QQIC in linear range.
> * MRC and QQIC in non-linear range.
>
> And please make sure the new test cases don't add new shellcheck
> warnings / errors or it will fail in the CI. You can ignore existing
> ones.

I will upgrade the existing test cases in file bridge_vlan_mcast.sh
instead of the previous file (bridge_igmp/mld.sh).

I have some doubts on test case logs reporting. Below are the logs
without my patchset. API used vlmc_query_intvl_test() and
vlmc_query_response_intvl_test().
TEST: Vlan mcast_query_interval global option default value         [ OK ]
TEST: Vlan 10 mcast_query_interval option changed to 200            [ OK ]
TEST: Vlan mcast_query_response_interval global option default value   [ OK ]
TEST: Vlan 10 mcast_query_response_interval option changed to 200   [ OK ]

I am expecting some suggestions from the logs below. And are these
covers both IGMPv3 and MLDv2 for QQIC and MRC?
Actually mcast_query_interval=QQIC and
mcast_query_response_interval=MRC we already know.

TEST: Vlan mcast_query_interval global option default value         [ OK ]
TEST: Vlan 10 mcast_query_interval option changed to 200            [ OK ]
TEST: Vlan 10 mcast_query_interval number of IGMPv2 general query   [
OK ] -> old case added log
TEST: Vlan 10 mcast_query_interval option changed to 6000           [ OK ]
TEST: Vlan 10 mcast_query_interval in IGMPv3 is 60                  [ OK ]
TEST: Vlan 10 mcast_query_interval in MLDv2 is 60                   [ OK ]
TEST: Vlan 10 mcast_query_interval option changed to 16000          [ OK ]
TEST: Vlan 10 mcast_query_interval in IGMPv3 is 160                 [ OK ]
TEST: Vlan 10 mcast_query_interval in MLDv2 is 160                  [ OK ]
TEST: Vlan mcast_query_response_interval global option default value   [ OK ]
TEST: Vlan 10 mcast_query_response_interval option changed to 600   [ OK ]
TEST: Vlan 10 mcast_query_response_interval in IGMPv3 is 60         [ OK ]
TEST: Vlan 10 mcast_query_response_interval option changed to 1600   [ OK ]
TEST: Vlan 10 mcast_query_response_interval in IGMPv3 is 160        [ OK ]
TEST: Vlan 10 mcast_query_response_interval option changed to 3000   [ OK ]
TEST: Vlan 10 mcast_query_response_interval in MLDv2 is 30000       [ OK ]
TEST: Vlan 10 mcast_query_response_interval option changed to 6000   [ OK ]
TEST: Vlan 10 mcast_query_response_interval in MLDv2 is 60000       [ OK ]

Or is it better to name them properly instead of mcast option names, like below?

Vlan 10 mcast_query_interval:
TEST: Number of tagged IGMPv2 general query                         [ OK ]
TEST: IGMPv3 QQIC linear value 60                                   [ OK ]
TEST: IGMPv3 QQIC non linear value 160                              [ OK ]
TEST: MLDv2 QQIC linear value 60                                    [ OK ]
TEST: MLDv2 QQIC non linear value 160                               [ OK ]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH net-next v2 5/5] selftests: net: bridge: add tests for igmpv3 MRC and QQIC validation
  2026-04-03  7:50           ` Ujjal Roy
@ 2026-04-03 10:19             ` Ido Schimmel
  2026-04-03 10:31               ` Ujjal Roy
  0 siblings, 1 reply; 26+ messages in thread
From: Ido Schimmel @ 2026-04-03 10:19 UTC (permalink / raw)
  To: Ujjal Roy
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, David Ahern, Shuah Khan,
	Andy Roulin, Yong Wang, Petr Machata, Ujjal Roy, bridge, netdev,
	linux-kernel, linux-kselftest

On Fri, Apr 03, 2026 at 01:20:49PM +0530, Ujjal Roy wrote:
> On Tue, Mar 31, 2026 at 7:44 PM Ido Schimmel <idosch@nvidia.com> wrote:
> >
> > On Mon, Mar 30, 2026 at 07:16:11PM +0000, Ujjal Roy wrote:
> > > 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.
> >
> > Please cover both IGMPv3 and MLDv2 since the patchset touches both. You
> > can add the MLDv2 tests in bridge_mld.sh.
> >
> > Also, I think you can simplify the test by simply matching on the
> > expected values of MRC and QQIC using tc-u32.
> >
> > For both IGMPv3 and MLDv2 please test both:
> >
> > * MRC and QQIC in linear range.
> > * MRC and QQIC in non-linear range.
> >
> > And please make sure the new test cases don't add new shellcheck
> > warnings / errors or it will fail in the CI. You can ignore existing
> > ones.
> 
> I will upgrade the existing test cases in file bridge_vlan_mcast.sh
> instead of the previous file (bridge_igmp/mld.sh).

Is there a reason to place them in bridge_vlan_mcast.sh given they are
not specific to the per-VLAN multicast context? bridge_{igmp,mld}.sh
looks like a better fit.

> 
> I have some doubts on test case logs reporting. Below are the logs
> without my patchset. API used vlmc_query_intvl_test() and
> vlmc_query_response_intvl_test().
> TEST: Vlan mcast_query_interval global option default value         [ OK ]
> TEST: Vlan 10 mcast_query_interval option changed to 200            [ OK ]
> TEST: Vlan mcast_query_response_interval global option default value   [ OK ]
> TEST: Vlan 10 mcast_query_response_interval option changed to 200   [ OK ]
> 
> I am expecting some suggestions from the logs below. And are these
> covers both IGMPv3 and MLDv2 for QQIC and MRC?
> Actually mcast_query_interval=QQIC and
> mcast_query_response_interval=MRC we already know.

Not sure what you are asking. My request was to have test cases for both
MRC and QQIC, for both IGMPv3 and MLDv2.

> 
> TEST: Vlan mcast_query_interval global option default value         [ OK ]
> TEST: Vlan 10 mcast_query_interval option changed to 200            [ OK ]
> TEST: Vlan 10 mcast_query_interval number of IGMPv2 general query   [
> OK ] -> old case added log
> TEST: Vlan 10 mcast_query_interval option changed to 6000           [ OK ]
> TEST: Vlan 10 mcast_query_interval in IGMPv3 is 60                  [ OK ]
> TEST: Vlan 10 mcast_query_interval in MLDv2 is 60                   [ OK ]
> TEST: Vlan 10 mcast_query_interval option changed to 16000          [ OK ]
> TEST: Vlan 10 mcast_query_interval in IGMPv3 is 160                 [ OK ]
> TEST: Vlan 10 mcast_query_interval in MLDv2 is 160                  [ OK ]
> TEST: Vlan mcast_query_response_interval global option default value   [ OK ]
> TEST: Vlan 10 mcast_query_response_interval option changed to 600   [ OK ]
> TEST: Vlan 10 mcast_query_response_interval in IGMPv3 is 60         [ OK ]
> TEST: Vlan 10 mcast_query_response_interval option changed to 1600   [ OK ]
> TEST: Vlan 10 mcast_query_response_interval in IGMPv3 is 160        [ OK ]
> TEST: Vlan 10 mcast_query_response_interval option changed to 3000   [ OK ]
> TEST: Vlan 10 mcast_query_response_interval in MLDv2 is 30000       [ OK ]
> TEST: Vlan 10 mcast_query_response_interval option changed to 6000   [ OK ]
> TEST: Vlan 10 mcast_query_response_interval in MLDv2 is 60000       [ OK ]
> 
> Or is it better to name them properly instead of mcast option names, like below?
> 
> Vlan 10 mcast_query_interval:
> TEST: Number of tagged IGMPv2 general query                         [ OK ]
> TEST: IGMPv3 QQIC linear value 60                                   [ OK ]
> TEST: IGMPv3 QQIC non linear value 160                              [ OK ]
> TEST: MLDv2 QQIC linear value 60                                    [ OK ]
> TEST: MLDv2 QQIC non linear value 160                               [ OK ]

I find the latter clearer.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH net-next v2 5/5] selftests: net: bridge: add tests for igmpv3 MRC and QQIC validation
  2026-04-03 10:19             ` Ido Schimmel
@ 2026-04-03 10:31               ` Ujjal Roy
  0 siblings, 0 replies; 26+ messages in thread
From: Ujjal Roy @ 2026-04-03 10:31 UTC (permalink / raw)
  To: Ido Schimmel
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, David Ahern, Shuah Khan,
	Andy Roulin, Yong Wang, Petr Machata, Ujjal Roy, bridge, netdev,
	linux-kernel, linux-kselftest

On Fri, Apr 3, 2026 at 3:49 PM Ido Schimmel <idosch@nvidia.com> wrote:
>
[...]
>
> Is there a reason to place them in bridge_vlan_mcast.sh given they are
> not specific to the per-VLAN multicast context? bridge_{igmp,mld}.sh
> looks like a better fit.

I could see the framework is ready to validate using
bridge_vlan_mcast.sh script. Just had to apply a new tc rule to
validate QEURY+MRC/QQIC fields. Something like this.

                tc filter add dev $dev egress pref 10 prot 802.1Q \
                        flower vlan_id 10 vlan_ethtype ipv4 dst_ip
224.0.0.1 ip_proto 2 \
+                       action continue
+               # AND Type 0x11 (Query) at offset 24 after IP
+               # IP (20 byte IP + 4 bytes Option)
+               tc filter add dev $dev egress pref 20 prot 802.1Q u32 \
+                       match u8 0x11 0xff at 24 $intvl_match \
                        action pass

And writing a few lines will validate the test for IGMPv3 QQIC non
linear value of 160s.
+       RET=0
+       bridge vlan global set vid 10 dev br0 mcast_snooping 1
mcast_query_interval 16000
+       check_err $? "Could not set mcast_query_interval in vlan 10"
+       log_test "Vlan 10 mcast_query_interval option changed to 16000"
+       # 1 is sent immediately, IGMPv3 QQIC should match with non
linear value 160s
+       vlmc_check_query igmp 3 $swp1 1 1 160
+       check_err $? "Wrong QQIC in sent tagged IGMPv3 general queries"
+       log_test "Vlan 10 mcast_query_interval in IGMPv3 is 160"

>
[...]
> > Vlan 10 mcast_query_interval:
> > TEST: Number of tagged IGMPv2 general query                         [ OK ]
> > TEST: IGMPv3 QQIC linear value 60                                   [ OK ]
> > TEST: IGMPv3 QQIC non linear value 160                              [ OK ]
> > TEST: MLDv2 QQIC linear value 60                                    [ OK ]
> > TEST: MLDv2 QQIC non linear value 160                               [ OK ]
>
> I find the latter clearer.

I thought so, hence asked which would be a better version of log.
Thanks for clearing it.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding
  2026-03-31 14:16       ` Ido Schimmel
@ 2026-04-04 17:25         ` Ujjal Roy
  0 siblings, 0 replies; 26+ messages in thread
From: Ujjal Roy @ 2026-04-04 17:25 UTC (permalink / raw)
  To: Ido Schimmel
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, David Ahern, Shuah Khan,
	Andy Roulin, Yong Wang, Petr Machata, Ujjal Roy, bridge, Kernel,
	Kernel, linux-kselftest

On Tue, Mar 31, 2026, 7:47 PM Ido Schimmel <idosch@nvidia.com> wrote:
>
> On Mon, Mar 30, 2026 at 07:16:06PM +0000, Ujjal Roy wrote:
> > Description:
> > This series addresses a mismatch in how multicast query
> > intervals and response codes are handled across IPv4 (IGMPv3)
> > and IPv6 (MLDv2). While decoding logic currently exists,
> > the corresponding encoding logic is missing during query
> > packet generation. This leads to incorrect intervals being
> > transmitted when values exceed their linear thresholds.
>
> Thanks for the patches. I will take a look, but please read:
>
> https://docs.kernel.org/process/maintainer-netdev.html
>
> In particular:
>
> "The new version of patches should be posted as a separate thread, not
> as a reply to the previous posting."

I have updated the cover letter title and sent the new version as a
separate thread. The same can be found here in this link:
https://lore.kernel.org/all/20260403150050.1235-1-royujjal@gmail.com/

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2026-04-04 17:25 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-26 15:07 [PATCH 0/4] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
2026-03-26 15:07 ` [PATCH 1/4] ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation Ujjal Roy
2026-03-26 15:07 ` [PATCH 2/4] ipv6: mld: rename mldv2_mrc() and add mldv2_qqi() Ujjal Roy
2026-03-27 15:49   ` kernel test robot
2026-03-26 15:07 ` [PATCH 3/4] ipv4: igmp: encode multicast exponential fields Ujjal Roy
2026-03-27 12:19   ` Nikolay Aleksandrov
2026-03-30 19:16     ` [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
2026-03-30 19:16       ` [PATCH net-next v2 1/5] ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation Ujjal Roy
2026-04-01  7:25         ` Ido Schimmel
2026-03-30 19:16       ` [PATCH net-next v2 2/5] ipv6: mld: rename mldv2_mrc() and add mldv2_qqi() Ujjal Roy
2026-04-01  7:26         ` Ido Schimmel
2026-03-30 19:16       ` [PATCH net-next v2 3/5] ipv4: igmp: encode multicast exponential fields Ujjal Roy
2026-04-01  7:27         ` Ido Schimmel
2026-03-30 19:16       ` [PATCH net-next v2 4/5] ipv6: mld: " Ujjal Roy
2026-04-01  7:29         ` Ido Schimmel
2026-03-30 19:16       ` [PATCH net-next v2 5/5] selftests: net: bridge: add tests for igmpv3 MRC and QQIC validation Ujjal Roy
2026-03-31 14:13         ` Ido Schimmel
2026-04-03  7:50           ` Ujjal Roy
2026-04-03 10:19             ` Ido Schimmel
2026-04-03 10:31               ` Ujjal Roy
2026-03-30 19:39       ` [PATCH net-next v2 0/5] net: bridge: mcast: add multicast exponential field encoding Ujjal Roy
2026-03-31 14:16       ` Ido Schimmel
2026-04-04 17:25         ` Ujjal Roy
2026-03-26 15:07 ` [PATCH 4/4] ipv6: mld: encode multicast exponential fields Ujjal Roy
2026-03-27  6:41 ` [PATCH 0/4] net: bridge: mcast: add multicast exponential field encoding Nikolay Aleksandrov
2026-03-27 11:31   ` Nikolay Aleksandrov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox