Commit bfbed92ea8 for frr

commit bfbed92ea8b7a23edb8dbd6e88d875ffd4d5b789
Author: Jafar Al-Gharaibeh <jafar@atcorp.com>
Date:   Mon Oct 5 13:18:01 2026 -0500

    tests: build IGMPv3 reports for current scapy

    Scapy 2.7 turned IGMPv3() into a query type byte, so the stacked report
    never installed a group and retries sent the same query again. Send one
    membership report, with ttl 1, and exit if the send fails.

    Signed-off-by: Jafar Al-Gharaibeh <jafar@atcorp.com>

diff --git a/tests/topotests/pim_igmp_specific_query_frag/igmp_send.py b/tests/topotests/pim_igmp_specific_query_frag/igmp_send.py
index 10fcf6eb63..8c271ac58d 100644
--- a/tests/topotests/pim_igmp_specific_query_frag/igmp_send.py
+++ b/tests/topotests/pim_igmp_specific_query_frag/igmp_send.py
@@ -13,28 +13,45 @@ import sys
 import argparse
 import json

-from scapy.main import load_contrib
 from scapy.layers.l2 import Ether
 from scapy.layers.inet import IP, IPOption_Router_Alert
 from scapy.sendrecv import sendp

 # pylint: disable=C0413
-load_contrib("igmpv3")
-from scapy.contrib.igmpv3 import IGMPv3, IGMPv3mr, IGMPv3gr
+# Names from the other scapy live only on one import path. Bind them up
+# front so the fallback report is not a use before assignment.
+IGMPv3 = IGMPv3mr = IGMPv3gr = None
+IGMPv3_MR = IGMPv3_MR_Group = None
+try:
+    # Current scapy. IGMPv3_MR is the whole report. Stacking a separate
+    # IGMPv3() in front of it puts a query type byte on the wire, and pimd
+    # never installs the group.
+    from scapy.layers.igmp import IGMPv3_MR, IGMPv3_MR_Group
+except ImportError:
+    from scapy.contrib.igmpv3 import IGMPv3, IGMPv3mr, IGMPv3gr
+
+
+def igmp_report(rtype, group, sources):
+    "One IGMPv3 membership report for a single group record."
+
+    if IGMPv3_MR is not None:
+        return IGMPv3_MR(
+            records=[IGMPv3_MR_Group(rtype=rtype, maddr=group, srcaddrs=sources)]
+        )
+
+    return IGMPv3() / IGMPv3mr(
+        records=[IGMPv3gr(rtype=rtype, maddr=group, srcaddrs=sources)]
+    )


 def get_packet(rtype, group, src_ip, sources):
     "Builds a single Ethernet/IP/IGMPv3 Membership Report packet"

-    record = IGMPv3gr(rtype=rtype, maddr=group, srcaddrs=sources)
-
-    pkt = (
+    return (
         Ether(dst="01:00:5e:00:00:16")
-        / IP(src=src_ip, dst="224.0.0.22", options=[IPOption_Router_Alert()])
-        / IGMPv3()
-        / IGMPv3mr(records=[record])
+        / IP(src=src_ip, dst="224.0.0.22", ttl=1, options=[IPOption_Router_Alert()])
+        / igmp_report(rtype, group, sources)
     )
-    return pkt


 def main():
@@ -84,8 +101,9 @@ def main():
         sendp(packets, iface=args.iface, verbose=False)
     except Exception as e:
         print(f"Send error: {e}")
+        sys.exit(1)

-    print(f"Success")
+    print("Success")


 if __name__ == "__main__":