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;