1c2e0b604SKristof Provost# 2c2e0b604SKristof Provost# SPDX-License-Identifier: BSD-2-Clause 3c2e0b604SKristof Provost# 4c2e0b604SKristof Provost# Copyright (c) 2023 Rubicon Communications, LLC (Netgate) 5c2e0b604SKristof Provost# 6c2e0b604SKristof Provost# Redistribution and use in source and binary forms, with or without 7c2e0b604SKristof Provost# modification, are permitted provided that the following conditions 8c2e0b604SKristof Provost# are met: 9c2e0b604SKristof Provost# 1. Redistributions of source code must retain the above copyright 10c2e0b604SKristof Provost# notice, this list of conditions and the following disclaimer. 11c2e0b604SKristof Provost# 2. Redistributions in binary form must reproduce the above copyright 12c2e0b604SKristof Provost# notice, this list of conditions and the following disclaimer in the 13c2e0b604SKristof Provost# documentation and/or other materials provided with the distribution. 14c2e0b604SKristof Provost# 15c2e0b604SKristof Provost# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16c2e0b604SKristof Provost# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17c2e0b604SKristof Provost# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18c2e0b604SKristof Provost# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19c2e0b604SKristof Provost# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20c2e0b604SKristof Provost# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21c2e0b604SKristof Provost# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22c2e0b604SKristof Provost# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23c2e0b604SKristof Provost# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24c2e0b604SKristof Provost# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25c2e0b604SKristof Provost# SUCH DAMAGE. 26c2e0b604SKristof Provost# 27c2e0b604SKristof Provostimport pytest 28c2e0b604SKristof Provostfrom atf_python.sys.net.tools import ToolsHelper 29c2e0b604SKristof Provostfrom atf_python.sys.net.vnet import VnetTestTemplate 30c2e0b604SKristof Provostimport os 31c2e0b604SKristof Provostimport socket 32c2e0b604SKristof Provostimport struct 33c2e0b604SKristof Provostimport sys 34c2e0b604SKristof Provostimport logging 35c2e0b604SKristof Provostlogging.getLogger("scapy").setLevel(logging.CRITICAL) 36c2e0b604SKristof Provostcurdir = os.path.dirname(os.path.realpath(__file__)) 37c2e0b604SKristof Provostnetpfil_common = curdir + "/../netpfil/common" 38c2e0b604SKristof Provostsys.path.append(netpfil_common) 39c2e0b604SKristof Provost 40c2e0b604SKristof Provostsc = None 41c2e0b604SKristof Provostsp = None 42c2e0b604SKristof Provost 43c2e0b604SKristof Provostdef check_igmpv3(args, pkt): 44c2e0b604SKristof Provost igmp = pkt.getlayer(sc.igmpv3.IGMPv3) 45c2e0b604SKristof Provost if igmp is None: 46c2e0b604SKristof Provost return False 47c2e0b604SKristof Provost 48c2e0b604SKristof Provost igmpmr = pkt.getlayer(sc.igmpv3.IGMPv3mr) 49c2e0b604SKristof Provost if igmpmr is None: 50c2e0b604SKristof Provost return False 51c2e0b604SKristof Provost 52c2e0b604SKristof Provost for r in igmpmr.records: 53c2e0b604SKristof Provost if r.maddr != args["group"]: 54c2e0b604SKristof Provost return False 55c2e0b604SKristof Provost if args["type"] == "join": 56c2e0b604SKristof Provost if r.rtype != 4: 57c2e0b604SKristof Provost return False 58c2e0b604SKristof Provost elif args["type"] == "leave": 59c2e0b604SKristof Provost if r.rtype != 3: 60c2e0b604SKristof Provost return False 61c2e0b604SKristof Provost r.show() 62c2e0b604SKristof Provost 63c2e0b604SKristof Provost return True 64c2e0b604SKristof Provost 65c2e0b604SKristof Provostclass TestIGMP(VnetTestTemplate): 66c2e0b604SKristof Provost REQUIRED_MODULES = [] 67c2e0b604SKristof Provost TOPOLOGY = { 68c2e0b604SKristof Provost "vnet1": { "ifaces": [ "if1" ] }, 69c2e0b604SKristof Provost "if1": { "prefixes4": [ ("192.0.2.1/24", "192.0.2.2/24" ) ] }, 70c2e0b604SKristof Provost } 71c2e0b604SKristof Provost 72c2e0b604SKristof Provost def setup_method(self, method): 73c2e0b604SKristof Provost global sc 74c2e0b604SKristof Provost if sc is None: 75c2e0b604SKristof Provost import scapy.contrib as _sc 76c2e0b604SKristof Provost import scapy.contrib.igmp 77c2e0b604SKristof Provost import scapy.contrib.igmpv3 78c2e0b604SKristof Provost import scapy.all as _sp 79c2e0b604SKristof Provost sc = _sc 80c2e0b604SKristof Provost sp = _sp 81c2e0b604SKristof Provost super().setup_method(method) 82c2e0b604SKristof Provost 83*65cc5af1SJose Luis Duran @pytest.mark.require_progs(["scapy"]) 84c2e0b604SKristof Provost def test_igmp3_join_leave(self): 85c2e0b604SKristof Provost "Test that we send the expected join/leave IGMPv2 messages" 86c2e0b604SKristof Provost 87c2e0b604SKristof Provost if1 = self.vnet.iface_alias_map["if1"] 88c2e0b604SKristof Provost 89c2e0b604SKristof Provost # Start a background sniff 9050f18a9bSJose Luis Duran from sniffer import Sniffer 91c2e0b604SKristof Provost expected_pkt = { "type": "join", "group": "230.0.0.1" } 92c2e0b604SKristof Provost sniffer = Sniffer(expected_pkt, check_igmpv3, if1.name, timeout=10) 93c2e0b604SKristof Provost 94c2e0b604SKristof Provost # Now join a multicast group, and see if we're getting the igmp packet we expect 95c2e0b604SKristof Provost s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 96c2e0b604SKristof Provost mreq = struct.pack("4sl", socket.inet_aton('230.0.0.1'), socket.INADDR_ANY) 97c2e0b604SKristof Provost s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) 98c2e0b604SKristof Provost 99c2e0b604SKristof Provost # Wait for the sniffer to see the join packet 100c2e0b604SKristof Provost sniffer.join() 101c2e0b604SKristof Provost assert(sniffer.correctPackets > 0) 102c2e0b604SKristof Provost 103c2e0b604SKristof Provost # Now leave, check for the packet 104c2e0b604SKristof Provost expected_pkt = { "type": "leave", "group": "230.0.0.1" } 105c2e0b604SKristof Provost sniffer = Sniffer(expected_pkt, check_igmpv3, if1.name) 106c2e0b604SKristof Provost 107c2e0b604SKristof Provost s.close() 108c2e0b604SKristof Provost sniffer.join() 109c2e0b604SKristof Provost assert(sniffer.correctPackets > 0) 110