Ejemplo n.º 1
0
def traceroute(net: IPNet, src: str, dst_ip: str, timeout=300) -> List[str]:
    require_cmd("traceroute", help_str="traceroute is required to run tests")

    t = 0
    old_path_ips = []  # type: List[str]
    same_path_count = 0
    white_space = re.compile(r" +")
    while t != timeout / 5.:
        out = net[src].cmd(["traceroute", "-w", "0.05", "-q", "1", "-n",
                            "-m", len(net.routers) + len(net.hosts), dst_ip])
        lines = out.split("\n")[1:-1]
        if "*" not in out and "!" not in out and "unreachable" not in out:
            path_ips = [str(white_space.split(line)[2]) for line in lines]
            if len(path_ips) > 0 and path_ips[-1] == str(dst_ip) \
                    and old_path_ips == path_ips:
                same_path_count += 1
                if same_path_count > 2:
                    # Network has converged
                    return path_ips
            else:
                same_path_count = 0

            old_path_ips = path_ips
        else:
            same_path_count = 0
            old_path_ips = []
        time.sleep(5)
        t += 1
    return []
Ejemplo n.º 2
0
def assert_dns_record(node: IPNode, dns_server_address: str, record: DNSRecord,
                      port=53, timeout=60):
    require_cmd("dig", help_str="dig is required to run tests")

    server_cmd = "dig @{address} -p {port} -t {rtype} {domain_name}"\
        .format(address=dns_server_address, rtype=record.rtype,
                domain_name=record.domain_name, port=port)
    out_regex = re.compile(r" *{name}[ \t]+{ttl}[ \t]+IN[ \t]+{rtype}[ \t]+"
                           r"{rdata}"
                           .format(rtype=record.rtype, ttl=record.ttl,
                                   name=record.domain_name,
                                   rdata=record.rdata))

    t = 0
    out = node.cmd(server_cmd.split(" "))
    got_answer, match = search_dns_reply(out, out_regex)
    while t < timeout * 2 and match is None:
        t += 1
        time.sleep(.5)
        out = node.cmd(server_cmd.split(" "))
        got_answer, match = search_dns_reply(out, out_regex)

    assert got_answer, "No answer was received in %s" \
                       " from server %s in the reply of '%s':\n%s" \
                       % (node.name, dns_server_address, server_cmd, out)

    assert match is not None, "The expected data '%s' cannot be found " \
                              "in the DNS reply of '%s' received by %s from " \
                              "%s:\n%s" % (out_regex.pattern, server_cmd,
                                           node.name, dns_server_address, out)
Ejemplo n.º 3
0
def check_tcp_connectivity(client: IPNode, server: IPNode, v6=False,
                           server_port=80, server_itf=None, timeout=300) \
        -> Tuple[int, bytes, bytes]:
    require_cmd("nc", help_str="nc is required to run tests")

    if server_itf is None:
        server_itf = server.defaultIntf()
    server_ip = server_itf.ip6 if v6 else server_itf.ip
    server_cmd = "nc %s -l %d" % ("-6" if v6 else "-4", server_port)
    server_p = server.popen(server_cmd.split(" "))

    t = 0
    client_cmd = "nc -z -w 1 -v %s %d" % (server_ip, server_port)

    client_p = client.popen(client_cmd.split(" "))
    while t != timeout * 2 and client_p.wait() != 0:
        t += 1
        if server_p.poll() is not None:
            out, err = server_p.communicate()
            assert False, \
                "The netcat server used to check TCP connectivity failed" \
                " with the output:\n[stdout]\n%s\n[stderr]\n%s" % (out, err)
        time.sleep(.5)
        client_p = client.popen(client_cmd.split(" "))
    out, err = client_p.communicate()
    code = client_p.poll()
    server_p.send_signal(signal.SIGINT)
    server_p.wait()
    return code, out, err
Ejemplo n.º 4
0
def test_require_cmd(cmd, present):
    try:
        utils.require_cmd(cmd)
        assert present, "The command [%s] was found" \
                        " while it is not present" % cmd
    except RuntimeError:
        assert not present, "The command [%s] could not be found" \
                            " while it is present" % cmd
Ejemplo n.º 5
0
    def start(self, _controllers):
        """Start Linux bridge"""
        require_cmd("brctl", help_str="You need brctl to use %s objects"
                                      % self.__class__)

        self.cmd('ifconfig', self, 'down')
        self.cmd('brctl delbr', self)
        self.cmd('brctl addbr', self)
        if self.hub:
            self.cmd('brctl setageing 0', self)
        if self.stp:
            self.cmd('brctl setbridgeprio', self, self.prio)
            self.cmd('brctl stp', self, 'on')
        for i in self.intfList():
            if self.name in i.name:
                self.cmd('brctl addif', self, i)
                self.cmd('brctl setpathcost'
                         ' %s %s %d' % (self.name, i.name,
                                        i.params.get('stp_cost', 1)))
        self.cmd('ifconfig', self, 'up')
Ejemplo n.º 6
0
def assert_stp_state(switch: IPSwitch,
                     expected_states: Dict[str, str],
                     timeout=60):
    """
    :param switch: The switch to test
    :param expected_states: Dictionary mapping an interface name to
                            its expected state
    :param timeout: Time to wait for the stp convergence
    :return:
    """
    require_cmd("brctl", help_str="brctl is required to run tests")

    partial_cmd = "brctl showstp"
    possible_states = "listening|learning|forwarding|blocking"
    # In these states the STP has not converged
    ignore_state = "listening", "learning"
    cmd = ("%s %s" % (partial_cmd, switch.name))
    out = switch.cmd(cmd)
    states = re.findall(possible_states, out)
    # wait for the ports to be bounded
    count = 0
    while any(item in states for item in ignore_state):
        if count == timeout:
            pytest.fail("Timeout of %d seconds while waiting for the spanning"
                        " tree to be computed" % timeout)
        time.sleep(1)
        count += 1
        out = switch.cmd(cmd)
        states = re.findall(possible_states, out)

    interfaces = re.findall(switch.name + r"-eth[0-9]+", out)
    state_map = {interfaces[i]: states[i] for i in range(len(states))}
    for itf, _ in expected_states.items():
        assert itf in state_map,\
            "The port %s of switch %s was not mentioned in the output of " \
            "'brctl showstp':\n%s" % (itf, switch.name, out)
        assert state_map[itf] == expected_states[itf],\
            "The state of port %s of switch %s wasn't correct: excepted '%s' " \
            "got '%s'"\
            % (itf, switch.name, expected_states[itf], state_map[itf])
Ejemplo n.º 7
0
    def register_daemon(self, cls, **daemon_opts):
        """Add a new daemon to this configuration

        :param cls: Daemon class or object, or a 2-tuple (Daemon, dict)
        :param daemon_opts: Options to set on the daemons"""
        try:
            cls, kw = cls
            daemon_opts.update(kw)
        except TypeError:
            pass
        if cls.NAME in self._daemons:
            return
        if not isinstance(cls, Daemon):
            if issubclass(cls, Daemon):
                cls = cls(self._node, **daemon_opts)
            else:
                raise TypeError('Expected an object or a subclass of '
                                'Daemon, got %s instead' % cls)
        else:
            cls.options.update(daemon_opts)
        self._daemons[cls.NAME] = cls
        require_cmd(cls.NAME, 'Could not find an executable for a daemon!')
Ejemplo n.º 8
0
    def register_daemon(self, cls, **daemon_opts):
        """Add a new daemon to this configuration

        :param cls: Daemon class or object, or a 2-tuple (Daemon, dict)
        :param daemon_opts: Options to set on the daemons"""
        try:
            cls, kw = cls
            daemon_opts.update(kw)
        except TypeError:
            pass
        if cls.NAME in self._daemons:
            return
        if not isinstance(cls, Daemon):
            if issubclass(cls, Daemon):
                cls = cls(self._node, **daemon_opts)
            else:
                raise TypeError('Expected an object or a subclass of '
                                'Daemon, got %s instead' % cls)
        else:
            cls.options.update(daemon_opts)
        self._daemons[cls.NAME] = cls
        require_cmd(cls.NAME, 'Could not find an executable for a daemon!')
Ejemplo n.º 9
0
def host_connected(net: IPNet, v6=False, timeout=0.5, translate_address=True) \
        -> bool:
    require_cmd("nmap", help_str="nmap is required to run tests")

    for src in net.hosts:
        for dst in net.hosts:
            if src != dst:
                dst.defaultIntf().updateIP()
                dst.defaultIntf().updateIP6()
                if translate_address:
                    dst_ip = dst.defaultIntf().ip6 if v6 \
                        else dst.defaultIntf().ip
                else:
                    dst_ip = dst
                cmd = "nmap%s -sn -n --max-retries 5 --max-rtt-timeout %dms %s"\
                      % (" -6" if v6 else "", int(timeout * 1000), dst_ip)
                out = src.cmd(cmd.split(" "))
                if "0 hosts up" in out:
                    return False
                # In case of flooding, hosts might not answer
                # So, we wait a bit before testing the next pair of hosts
                time.sleep(0.1)
    return True
Ejemplo n.º 10
0
def sr_path(net: IPNet, src: str, dst_ip: str, timeout=1, through=()) \
        -> List[str]:
    require_cmd("tshark", help_str="tshark is required to run tests")
    require_cmd("nmap", help_str="nmap is required to run tests")

    # Check connectivity
    ping_cmd = "ping -6 -c 1 -W %d %s" % (int(timeout), dst_ip)
    out = net[src].cmd(shlex.split(ping_cmd))
    if ", 0% packet loss" not in out:
        return []

    # Start captures of SYN packets to port 80
    tsharks = []
    try:
        for n in net.routers + net.hosts:
            p = n.popen(shlex.split("tshark -n -i any -f 'ip6'"
                                    " -w /tmp/{}.pcap".format(n.name)))
            tsharks.append(p)
        time.sleep(15)  # Wait for tshark to start

        # Launch ping
        out = net[src].cmd(shlex.split(ping_cmd))
        assert "100% packet loss" not in out, \
            "Connectivity from %s to %s is not ensured," \
            " so we cannot infer the path." % (src, dst_ip)
        time.sleep(1)  # Wait for tshark to register the info

        for p in tsharks:
            assert p.poll() is None, "tshark stopped unexpectedly:" \
                                     "stderr '{}'".format(p.stderr.read())
    finally:
        # Stop captures
        for p in tsharks:
            p.send_signal(signal.SIGINT)
            p.wait()

    # Retrieve packet captures
    captures = {}  # type: Dict[str, List[Tuple[float, str]]]
    for n in net.routers + net.hosts:
        out = n.cmd(shlex.split("tshark -n -r /tmp/{}.pcap -T fields "
                                "-E separator=, "
                                "-e icmpv6.type -e ipv6.dst -e frame.time_epoch"
                                .format(n.name)))
        data = out.split("\n")[1:-1]
        for line in data:
            values = line.strip().split(",")
            if len(values) < 3:
                continue
            icmp_type = values[0]
            data_dst = values[1]
            data_time = values[-1]
            if icmp_type == "128":
                captures.setdefault(n.name, []) \
                    .append((float(data_time), data_dst))

    # Analyze results

    packet_received = {}  # type: Dict[str, List[Tuple[float, str]]]
    for n, packets in captures.items():
        for data_time, destination in packets:
            packet_received.setdefault(destination, []).append((data_time, n))

    sub_paths = {}  # type: Dict[str, List[str]]
    for dest in packet_received:
        ordered_reception = sorted(packet_received[dest])

        sub_paths[dest] = []
        for _, n in ordered_reception:
            if len(sub_paths[dest]) == 0 or sub_paths[dest][-1] != n:
                sub_paths[dest].append(n)

    # Order sub paths with the trough list
    path = []  # type: List[str]
    for intermediate in through:
        found = False

        try:
            intermediate_ips = [ip.ip.compressed
                                for itf in net[intermediate].intfList()
                                for ip in itf.ip6s(exclude_lls=True)]
        except KeyError:
            intermediate_ips = [intermediate]

        for ip in intermediate_ips:
            if ip in sub_paths:
                found = True
                path.extend(sub_paths[ip])
                break
        if not found:
            return path
    path.extend(sub_paths[dst_ip])

    # Remove duplicates
    compressed_path = []  # type: List[str]
    for n in path:
        if len(compressed_path) == 0 or n != compressed_path[-1]:
            compressed_path.append(n)

    # Remove source to get a similar output to traceroute
    path = [net[n].intf().ip6 for n in compressed_path][1:]
    return path