From nobody Thu Apr 2 22:00:03 2026 Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2F7E7402440 for ; Thu, 26 Mar 2026 15:08:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774537688; cv=none; b=TafomexN3oN+wEUVICu/CEQJtTc0RlFO+JaFsEwZ8C7cMQQgq2SUM0q4/yh/Rbi0uULzO2gI6LUb1jiMwC1afWHzuQX6ThAY36f4o6sjzX9hG0egsBq0vPDbrmrLrayXoYCnvlFQsr9KkRzU5qRDhq92dK2iX0JBw7NKbJtTi04= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774537688; c=relaxed/simple; bh=SrJ8v2JXUP4BQ/htK1TW22nfXu8yKqHl2EOzmrry8MI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kwlbRaGbZQ6A8a9PIPRpJnobUNUqImVMDJmSrUYcLTv3gE347u8VLbV+N+Ks3WkEB/cjbqehiJY1slJCWdllDGCRDmsNfo4mGgbUrElcql4fTLXUvTRgkzp42vrP241IZ5Qo3vuEWwcgxliM8mzSA/VKm2HK9y44GOFZqUDDwgM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=K8JmEa61; arc=none smtp.client-ip=209.85.214.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="K8JmEa61" Received: by mail-pl1-f170.google.com with SMTP id d9443c01a7336-2adff872068so4538565ad.1 for ; Thu, 26 Mar 2026 08:08:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774537684; x=1775142484; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=htKXcnTzcKDeqjMpn8hCX3YfwiSQQ8z5MdKwPZHhth8=; b=K8JmEa61X/hHCAZKr8D26W7Ag72I8qjEWApS1FeDkX9VBEY06XBpymHYLnAFhyOts3 feb1Vzo8gPs0WLQ50hutkkV1KbGsDRf2oHLt85BUj0fOu/+kAebZeaCOh5yyrnIAasEA tyeAut7Fu/ZHMAHJm/Mk1lNFLECxLy1BCn1/g/dBTVHiE5C23CEgbHvX3o1ZiSUDaSkC +8uG4JbzNPu6yCBautg0VRFTLg66jqLFOwXRe9V+btCYxCXgtJe8m9v7FQhhI12IOqC/ AXrMKLJSqUSNcDSBvCOYpRpXwAnvUZGkamVQEDSdYNFnN0y1t9VVlmTrRD4kDVUYCLmg f08A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774537684; x=1775142484; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=htKXcnTzcKDeqjMpn8hCX3YfwiSQQ8z5MdKwPZHhth8=; b=sZCvWJhmi6n1SAbrumwmNW9hYfRkiccdsaqW0wGNlZ2UXvuuBaOO5mqRMAG5sa2/81 JJn1nRcdiCux/8mDRFBkdg3W3/89iI3wPyUcZw83xGHAnuU2H6x5fTr+dR2GZDXLiBP6 qAEd530XQGISFbYLDwY2hH2yTL2Y0mbTVrvtnMTFodqHhXJKa/4x5trOevafpGdNZY7p A83RsrlMLz9dii1c6G6OnhuqkSeG8PcFWQbEc5/3Ztj1FW99Sll2raxmSz54n8pBoPdD Wl5fo6qUYgAHHAGN5enZNl1RWgjnNgflmrr8XEKXfdP1LGPDhdcxarZJQ7MDBNaZEzIr 5ZRA== X-Forwarded-Encrypted: i=1; AJvYcCUNo0pqPBhelL4sn7sD4HhHjNAYjZNOVfkPl2EsRIPen+GFccPzEqKjeX0K2lOkq62EYmbF5Zgq3YDl9xg=@vger.kernel.org X-Gm-Message-State: AOJu0Yy4FeoLk7TaxpCjwcjXa/Ob74Qcxsy+3ofzs0JHLrXExO/RwAP0 gBq96oB6JdMvBTlRTcX5S+HMPodBoCIjD1YVwAKkLwTqiz6bXB2Bu6Mv X-Gm-Gg: ATEYQzwsYWyQlchYayUihtzTjGfIWwTqt5fHBkG4m+YF79SgwFstp+m9Ag/DF/ganr0 Gpq99D/h/Z66mT1hKkdyMJEN1BsuM2zi24WG4CoHBJme4IPDxalOTa+zxRQVi7+xe3Ms1HKP7eB eSluA05LGdcR+dMWp93htXEPPlQGWwmy0hl0AXMLNpeBnlFoQ57/SUAyqPy8FDAzLVw523OPn1A eVP8fJ6G3sU9hkjEECpLxgQoprZn43WrsGRVD3JOhltNnj8tSPqrtiZnbjyr73A258Scveesknj FOSYVPO42/8eltHjSiW+ki5X0dFPYPsJukMR4lXYXumXDnr2F0cZj2cgXOylNHade/RD6IwGCvD yAGKjjQQ/WBS7neGXcDusYS5vGvTuu38JLZQkKi3HUpqgP8DiKMyijKbdXiSlQiHZOsNAgUdEPy 60uBWIHmLrGKIhKBXdfgapv+VerzMkMcSLJji9S9Pl X-Received: by 2002:a17:903:3884:b0:2ae:5655:b16 with SMTP id d9443c01a7336-2b0b0a140d0mr86962365ad.21.1774537684401; Thu, 26 Mar 2026 08:08:04 -0700 (PDT) Received: from DESKTOP-82PPU4A.localdomain ([202.8.116.170]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2b0bc8bb5fasm33146375ad.59.2026.03.26.08.08.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 26 Mar 2026 08:08:03 -0700 (PDT) From: Ujjal Roy To: "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Nikolay Aleksandrov , Ido Schimmel , David Ahern Cc: Ujjal Roy , bridge@lists.linux.dev, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 3/4] ipv4: igmp: encode multicast exponential fields Date: Thu, 26 Mar 2026 15:07:41 +0000 Message-ID: <20260326150742.50289-4-royujjal@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260326150742.50289-1-royujjal@gmail.com> References: <20260326150742.50289-1-royujjal@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 >=3D 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 --- 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 { =20 /* IGMPV3 floating-point exponential field threshold */ #define IGMPV3_EXP_MIN_THRESHOLD 128 +/* Max representable (mant =3D 0xF, exp =3D 7) -> 31744 */ +#define IGMPV3_EXP_MAX_THRESHOLD 31744 + +/* V3 exponential field encoding */ + +/* + * IGMPv3 QQIC/MRC 8-bit exponential field encode. + * + * RFC3376 defines only the decoding formula: + * QQI/MRT =3D (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' =3D 4 + (exp + 3) =3D exp + 7. + * This is the MSB at the 0-based bit position: (exp + 7). + * Since fls() is 1-based, fls(value) - 1 =3D exp + 7. + * + * Therefore: + * exp =3D fls(value) - 8 + * mant =3D (value >> (exp + 3)) & 0x0F + * + * Final encoding formula: + * 0x80 | (exp << 4) | mant + * + * Example (value =3D 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 =3D 3200) + * | ^-^-mant^ ^..(exp+3)..^| exp =3D 4, mant =3D 9 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Encoded: + * 0x80 | (4 << 4) | 9 =3D 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 =3D 0xF, exp =3D 7) -> 31744 */ + if (value >=3D IGMPV3_EXP_MAX_THRESHOLD) + return 0xFF; + + mc_exp =3D (u8)(fls(value) - 8); + mc_man =3D (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); +} =20 /* V3 exponential field decoding */ =20 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(str= uct 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 =3D 0; =20 @@ -1004,15 +1004,15 @@ static struct sk_buff *br_ip4_multicast_alloc_query= (struct net_bridge_mcast *brm skb_put(skb, 24); =20 skb_set_transport_header(skb, skb->len); + mrt =3D group ? brmctx->multicast_last_member_interval : + brmctx->multicast_query_response_interval; *igmp_type =3D IGMP_HOST_MEMBERSHIP_QUERY; =20 switch (brmctx->multicast_igmp_version) { case 2: ih =3D igmp_hdr(skb); ih->type =3D IGMP_HOST_MEMBERSHIP_QUERY; - ih->code =3D (group ? brmctx->multicast_last_member_interval : - brmctx->multicast_query_response_interval) / - (HZ / IGMP_TIMER_SCALE); + ih->code =3D mrt / (HZ / IGMP_TIMER_SCALE); ih->group =3D group; ih->csum =3D 0; csum =3D &ih->csum; @@ -1021,11 +1021,9 @@ static struct sk_buff *br_ip4_multicast_alloc_query(= struct net_bridge_mcast *brm case 3: ihv3 =3D igmpv3_query_hdr(skb); ihv3->type =3D IGMP_HOST_MEMBERSHIP_QUERY; - ihv3->code =3D (group ? brmctx->multicast_last_member_interval : - brmctx->multicast_query_response_interval) / - (HZ / IGMP_TIMER_SCALE); + ihv3->code =3D igmpv3_mrc(mrt / (HZ / IGMP_TIMER_SCALE)); ihv3->group =3D group; - ihv3->qqic =3D brmctx->multicast_query_interval / HZ; + ihv3->qqic =3D igmpv3_qqic(brmctx->multicast_query_interval / HZ); ihv3->nsrcs =3D htons(lmqt_srcs); ihv3->resv =3D 0; ihv3->suppress =3D sflag; --=20 2.43.0