Re: [PATCH net-next v3 4/5] ipv6: mld: encode multicast exponential fields

Nikolay Aleksandrov <[email protected]> Tue, 7 Apr 2026 17:09:01 +0300
Newsgroups dev.linux.lists.bridge,org.kernel.vger.linux-kernel,org.kernel.vger.linux-kselftest,org.kernel.vger.netdev
Message-ID <[email protected]>
On 03/04/2026 18:00, Ujjal Roy wrote:
> In MLD, QQIC and MRC fields are not correctly encoded when
> generating query packets. Since the receiver of the query
> interprets these fields using the MLDv2 floating-point
> decoding logic, any value that exceeds the linear threshold
> is incorrectly parsed 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 <[email protected]>
> ---
>   include/net/mld.h         | 122 ++++++++++++++++++++++++++++++++++++++
>   net/bridge/br_multicast.c |   4 +-
>   2 files changed, 124 insertions(+), 2 deletions(-)
> 
> diff --git a/include/net/mld.h b/include/net/mld.h
> index da3299545ebd..147e8c44eb28 100644
> --- a/include/net/mld.h
> +++ b/include/net/mld.h
> @@ -90,12 +90,134 @@ 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
> + *
> + * MRC represents the 16-bit encoded form of Maximum Response
> + * Delay (MRD); once decoded, the resulting value is in
> + * milliseconds.
> + *
> + * 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 mrd;
> +
> +	/* Saturate at max representable (mant = 0xFFF, exp = 7) -> 8387584 */
> +	if (mrd >= MLD_MRC_MAX_THRESHOLD)
> +		return 0xFFFF;
> +
> +	mc_exp = fls(mrd) - 16;
> +	mc_man = (mrd >> (mc_exp + 3)) & 0x0FFF;
> +
> +	return 0x8000 | (mc_exp << 12) | mc_man;
> +}
> +
> +/*
> + * Calculate Querier's Query Interval Code from Query Interval
> + *
> + * QQIC represents the 8-bit encoded form of Query Interval (QQI);
> + * once decoded, the resulting value is in seconds.
> + *
> + * 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 value;
> +
> +	/* Saturate at max representable (mant = 0xF, exp = 7) -> 31744 */
> +	if (value >= MLD_QQIC_MAX_THRESHOLD)
> +		return 0xFF;
> +
> +	mc_exp  = fls(value) - 8;
> +	mc_man = (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..a22e44c4fa48 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)mldv2_mrc(jiffies_to_msecs(interval)));

you've defined mldv2_mrc as u16, no need to cast it again here

>   		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;