/* IGMPv3 floating-point exponential field min threshold */
#define IGMPV3_EXP_MIN_THRESHOLD 128
+/* IGMPv3 FP max threshold (mant = 0xF, exp = 7) -> 31744 */
+#define IGMPV3_EXP_MAX_THRESHOLD 31744
+
+/* V3 exponential field encoding */
+
+/* IGMPv3 MRC/QQIC 8-bit exponential field encode
+ *
+ * RFC3376, 4.1.1 & 4.1.7. defines only the decoding formula:
+ * MRT/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 igmpv3_exp_field_encode(unsigned long value)
+{
+ u8 mc_exp, mc_man;
+
+ /* MRC/QQIC < 128 is literal */
+ if (value < IGMPV3_EXP_MIN_THRESHOLD)
+ return value;
+
+ /* Saturate at max representable (mant = 0xF, exp = 7) -> 31744 */
+ if (value >= IGMPV3_EXP_MAX_THRESHOLD)
+ return 0xFF;
+
+ mc_exp = fls(value) - 8;
+ mc_man = (value >> (mc_exp + 3)) & 0x0F;
+
+ return 0x80 | (mc_exp << 4) | mc_man;
+}
+
+/* Calculate Maximum Response Code from Max Resp Time
+ *
+ * RFC3376, relevant sections:
+ * - 4.1.1. Maximum Response Code
+ * - 8.3. Query Response Interval
+ *
+ * MRC represents the encoded form of Max Resp Time (MRT); once
+ * decoded, the resulting value is in units of 0.1 seconds (100 ms).
+ */
+static inline u8 igmpv3_mrc(unsigned long mrt)
+{
+ return igmpv3_exp_field_encode(mrt);
+}
+
+/* Calculate Querier's Query Interval Code from Querier's Query Interval
+ *
+ * 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)
+ *
+ * QQIC represents the encoded form of Querier's Query Interval (QQI);
+ * once decoded, the resulting value is in units of seconds.
+ */
+static inline u8 igmpv3_qqic(unsigned long qi)
+{
+ return igmpv3_exp_field_encode(qi);
+}
/* V3 exponential field decoding */
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;
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;
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;