def _find_peer_address(base: 'Router', peer: str, v6=False) \ -> Tuple[Optional[str], Optional['Router']]: """Return the IP address that base should try to contact to establish a peering""" visited = set() # type: Set[IPIntf] to_visit = {i.name: i for i in realIntfList(base)} prio_queue = [(0, i) for i in to_visit.keys()] #heapq.heapify(prio_queue) # Explore all interfaces in base ASN recursively, until we find one # connected to the peer iterations = 0 while to_visit: path_cost, i = prio_queue.pop(0) if i in visited: continue i = to_visit.pop(i) visited.add(i) for n in i.broadcast_domain.routers: if n.node.name == peer: if not v6: return n.ip, n.node if n.ip6 and not ip_address(n.ip6).is_link_local: return n.ip6, n.node return None, None if n.node.asn == base.asn or not n.node.asn: for i in realIntfList(n.node): to_visit[i.name] = i prio_queue += [ (path_cost + i.igp_metric, i.name), ] iterations += 1 return None, None
def compute_routerid(self): """Computes the default router id for all daemons. If a router ids were explicitly set for some of its daemons, the router id set to the daemon with the highest priority is chosen as the global router id. Otherwise if it has IPv4 addresses, it returns the most-visible one among its router interfaces. If both conditions are wrong, it generates a unique router id.""" for d in self.daemons: if d.options.routerid: return d.options.routerid ip_list = sorted((ip for itf in realIntfList(self._node) for ip in itf.ips()), cmp=address_comparator) if len(ip_list) == 0: to_visit = realIntfList(self._node) # Explore all routers to check that none has the same router id while to_visit: self.incr_last_routerid() visited = set() while to_visit: i = to_visit.pop() if i in visited: continue visited.add(i) for n in i.broadcast_domain.routers: if self._equal_routerid(n.node): break # We need to change the router id to_visit.extend(realIntfList(n.node)) to_visit = realIntfList(self._node) if to_visit else [] return last_routerid.compressed else: id = ip_list.pop().ip.compressed return id
def compute_routerid(self): """Computes the default router id for all daemons. If a router ids were explicitly set for some of its daemons, the router id set to the daemon with the highest priority is chosen as the global router id. Otherwise if it has IPv4 addresses, it returns the most-visible one among its router interfaces. If both conditions are wrong, it generates a unique router id.""" for d in self.daemons: if d.options.routerid: return d.options.routerid ip_list = sorted( (ip for itf in realIntfList(self._node) for ip in itf.ips()), cmp=address_comparator) if len(ip_list) == 0: to_visit = realIntfList(self._node) # Explore all routers to check that none has the same router id while to_visit: self.incr_last_routerid() visited = set() while to_visit: i = to_visit.pop() if i in visited: continue visited.add(i) for n in i.broadcast_domain.routers: if self._equal_routerid(n.node): break # We need to change the router id to_visit.extend(realIntfList(n.node)) to_visit = realIntfList(self._node) if to_visit else [] return last_routerid.compressed else: id = ip_list.pop().ip.compressed return id
def _fill_rdnss_addresses(self): for itf in realIntfList(self._node): for rdnss in itf.rdnss_list: if rdnss.ips is None: rdnss.ips = [] dns = find_node(self._node, rdnss.node).node for dns_itf in realIntfList(dns): for ip in dns_itf.ip6s(exclude_lls=True): rdnss.ips.append(ip.ip.compressed)
def _find_peer_address(base: 'Router', peer: str, v6=False) \ -> Tuple[Optional[str], Optional['Router'], Optional[str]]: """ Finds the IP address of the peer from the base router to typically configure the BGP session between the two routers. :param base: The router from which the peer's address must be found :param peer: the name of the node for which we are looking for the IP address :param v6: if set to True, the function seeks the IPv6 address. Otherwise, if set to False, it looks for the IPv4 address :return: a 3-Tuple <peer IP, peer Router object, local base IP> peer IP: the IP address set on the peer interface peer Router object: the node object related to the peer local base IP: the local IP address used on base router to establish the connection with the peer """ visited = set() # type: Set[str] to_visit = {i.name: i for i in realIntfList(base)} prio_queue: List['Peer.PQNode'] = [ Peer.PQNode((0, i), to_visit[i]) for i in to_visit.keys() ] heapq.heapify(prio_queue) # Explore all interfaces in base ASN recursively, until we find one # connected to the peer while to_visit: node = heapq.heappop(prio_queue) path_cost = node.key[0] i = node.key[1] my_interface = node.value if i in visited: continue visited.add( i) # putting the string representation of the interface i = to_visit.pop(i) for n in i.broadcast_domain.routers: if n.node.name == peer: if not v6: return n.ip, n.node, my_interface.ip if n.ip6 and not ip_address(n.ip6).is_link_local: return n.ip6, n.node, my_interface.ip6 return None, None, None if n.node.asn == base.asn or not n.node.asn: for i in realIntfList(n.node): to_visit[i.name] = i heapq.heappush( prio_queue, Peer.PQNode((path_cost + i.igp_metric, i.name), my_interface)) return None, None, None
def build(self): cfg = super(OSPF, self).build() cfg.redistribute = self.options.redistribute interfaces = [itf for itf in realIntfList(self._node)] cfg.interfaces = self._build_interfaces(interfaces) cfg.networks = self._build_networks(interfaces) return cfg
def createDefaultRoutes(self): if 'defaultRoute' in self.params: return True # The first router we find will become the default gateway found = False for itf in realIntfList(self): for r in itf.broadcast_domain.routers: if self.use_v4 and self.use_v4 and len(r.addresses[4]) > 0: self.setDefaultRoute("dev {} via {}".format(itf.name, r.ip), v6=False) found = True if self.use_v6 and self.use_v6 and len(r.addresses[6]) > 0 \ and len(r.ra_prefixes) == 0: # We define a default route only if router # advertisements are not activated. If we call the same # function, the route created above might be deleted self.setDefaultRoute("dev {} via {}".format(itf.name, r.ip6), v6=True) found = True break if found: return True return False
def _equal_routerid(self, n): # Router id of 'n' already set if n.config.routerid: return str(n.config.routerid) != str(last_routerid) # Check that a router id explicitly set # in any other daemon is not in conflict # with the current router id for d in n.config.daemons: if d != self \ and d.options.routerid \ and str(d.options.routerid) == str(last_routerid): return True # Check that the most-visible IPv4 address is not in conflict # with the current router id ip_list = sorted((ip for itf in realIntfList(n) for ip in itf.ips()), cmp=address_comparator) if len(ip_list) != 0 \ and str(ip_list.pop().ip) == str(last_routerid): return True return False
def _equal_routerid(self, n): # Router id of 'n' already set if n.config.routerid: return str(n.config.routerid) != str(last_routerid) # Check that a router id explicitly set # in any other daemon is not in conflict # with the current router id for d in n.config.daemons: if d != self \ and d.options.routerid \ and str(d.options.routerid) == str(last_routerid): return True # Check that the most-visible IPv4 address is not in conflict # with the current router id ip_list = sorted((ip for itf in realIntfList(n) for ip in itf.ips()), key=OrderedAddress) if len(ip_list) != 0 \ and str(ip_list.pop().ip) == str(last_routerid): return True return False
def build_zone(self, zone: 'DNSZone') -> ConfigDict: master_ips = [] for s_name in zone.servers + [zone.dns_master] + zone.dns_slaves: server_itf = find_node(self._node, s_name) if server_itf is None: lg.error("Cannot find the server node {name} of DNS zone" " {zone}. Are you sure that they are connected to " "the current node {current}?".format( name=s_name, zone=zone.name, current=self._node.name)) continue server = server_itf.node for itf in realIntfList(server): for ip in itf.ips(): if ".arpa" not in zone.name: # Not a Reverse zone zone.soa_record.add_record( ARecord(s_name, ip.ip.compressed)) if s_name == zone.dns_master: master_ips.append(ip.ip.compressed) for ip6 in itf.ip6s(exclude_lls=True): if ".arpa" not in zone.name: # Not a Reverse zone zone.soa_record.add_record( AAAARecord(s_name, ip6.ip.compressed)) if s_name == zone.dns_master: master_ips.append(ip6.ip.compressed) return ConfigDict(name=zone.soa_record.domain_name, soa_record=zone.soa_record, records=zone.soa_record.records, master=self._node.name == zone.dns_master, master_ips=master_ips)
def build(self): cfg = super(Zebra, self).build() # Update with preset defaults cfg.update(self.options) # Track interfaces cfg.interfaces = (ConfigDict(name=itf.name, description=itf.describe) for itf in realIntfList(self._node)) return cfg
def build(self): cfg = super(PIMD, self).build() cfg.update(self.options) cfg.interfaces = [ConfigDict( name=itf.name, ssm=itf.get('multicast_ssm', self.options.multicast_ssm), igmp=itf.get('multicast_igmp', self.options.multicast_igmp)) for itf in realIntfList(self._node) if itf.enable_multicast] return cfg
def build(self): cfg = super().build() cfg.update(self.options) cfg.node_name = self._node.name interfaces = realIntfList(self._node) cfg.interfaces = self._build_interfaces(interfaces) cfg.networks = self._build_networks(interfaces) cfg.prefixes = self._build_prefixes(interfaces) return cfg
def build(self): cfg = super(Openr, self).build() cfg.update(self.options) cfg.node_name = self._node.name interfaces = [itf for itf in realIntfList(self._node)] cfg.interfaces = self._build_interfaces(interfaces) cfg.networks = self._build_networks(interfaces) cfg.prefixes = self._build_prefixes(interfaces) return cfg
def build(self): cfg = super().build() self.options.redistribute_ifaces = \ ','.join([intf.name for intf in self._node.intfList()]) cfg.update(self.options) interfaces = realIntfList(self._node) cfg.interfaces = self._build_interfaces(interfaces) cfg.networks = self._build_networks(interfaces) cfg.prefixes = self._build_prefixes(interfaces) return cfg
def build(self): cfg = super(RADVD, self).build() # Update with preset defaults cfg.update(self.options) # Track interfaces cfg.interfaces = (ConfigDict(name=itf.name, description=itf.describe, ra_prefixes=itf.ra_prefixes, rdnss_list=itf.rdnss_list) for itf in realIntfList(self._node) if itf.ra_prefixes) return cfg
def _find_peer_address(base, peer): """Return the IP address that base should try to contact to establish a peering""" visited = set() to_visit = realIntfList(base) # Explore all interfaces in base ASN recursively, until we find one # connected to the peer while to_visit: i = to_visit.pop() if i in visited: continue visited.add(i) for n in i.broadcast_domain.routers: if n.node.name == peer: ip = n.ip return (ip, n.node) if ip else ( n.ip6, n.node) elif n.node.asn == base.asn or not n.node.asn: to_visit.extend(realIntfList(n.node)) return None, None
def build(self): cfg = super(RIPng, self).build() cfg.redistribute = self.options.redistribute cfg.split_horizon = self.options.split_horizon cfg.split_horizon_with_poison = self.options.split_horizon_with_poison cfg.timers = self._build_timers(self.options.update_timer, self.options.timeout_timer, self.options.garbage_timer) interfaces = [itf for itf in realIntfList(self._node)] cfg.interfaces = self._build_interfaces(interfaces) cfg.networks = self._build_networks(interfaces) return cfg
def build(self): cfg = super(RADVD, self).build() # Update with preset defaults cfg.update(self.options) # Track interfaces cfg.interfaces = (ConfigDict(name=itf.name, description=itf.describe, ra_prefixes=itf.ra_prefixes, rdnss_list=itf.rdnss_list) for itf in realIntfList(self._node) if itf.ra_prefixes) # Fill AdvConnectedPrefix prefixes self._fill_connected_prefixes() # Fill AdvRDNSS IP addresses self._fill_rdnss_addresses() return cfg
def post_build(self, net): # Run the failure plan and then, restore the links failure_plan = [("r1", "r2"), ("r3", "r4")] interfaces_down = net.runFailurePlan(failure_plan) net.restoreIntfs(interfaces_down) # Run a random failure with 2 link to be downed and then, restore them interfaces_down = net.randomFailure(2) net.restoreIntfs(interfaces_down) # Run a 1 link Failure Random based on a given list of link # and then, restore the link links = list(map(lambda x: x.link, realIntfList(net["r1"]))) interfaces_down = net.randomFailure(1, weak_links=links) net.restoreIntfs(interfaces_down) super().post_build(net)
def network_ips(self) -> Dict[str, List[str]]: """Return all the addresses of the nodes connected directly or not to this node""" ips = {} # type: Dict[str, List[str]] visited = set() # type: Set[str] to_visit = [self] while to_visit: node = to_visit.pop() if node.name in visited: continue visited.add(node.name) if isinstance(node, (Host, IPNode)): for i in node.intfList(): for ip in list(i.ips()) \ + list(i.ip6s(exclude_lls=True)): ips.setdefault(node.name, []).append(ip.ip.compressed) for i in realIntfList(node): adj_i = otherIntf(i) if adj_i is not None: to_visit.append(adj_i.node) return ips
def test_topologydb(topology: Type[IPTopo]): net = IPNet(topo=topology()) try: db = TopologyDB(net=net) db_path = "/tmp/.test_topologydb.json" if os.path.exists(db_path): os.unlink(db_path) db.save(db_path) assert os.path.exists(db_path), \ "TopologyDB did not write the JSON database" db = TopologyDB(db=db_path) for node in net.routers + net.hosts + net.switches: assert node.name in db._network, \ "The node {} in the network is not in the DB file".format(node) for node, node_info in db._network.items(): assert node in net, \ "The node {} in the DB file is not in the network".format(node) # Check type assert "type" in node_info, \ "No info on the type of node {}".format(node) node_type = node_info["type"] if node_type == "host": assert type(net[node]) == IPHost, "The node {} is not an " \ "host".format(node) elif node_type == "router": assert type(net[node]) == Router, "The node {} is not a " \ "router".format(node) elif node_type == "switch": assert type(net[node]) == IPSwitch, "The node {} is not a " \ "switch".format(node) else: pytest.fail("The node type {} of node {} is invalid".format( node_type, node)) # Check interfaces assert "interfaces" in node_info, \ "No information about interfaces of node {}".format(node) real_intfs = {itf.name for itf in realIntfList(net[node])} assert real_intfs == set(node_info["interfaces"]), \ "The interface list is not the same on node {}".format(node) for info_key, info_value in node_info.items(): if info_key == "type" or info_key == "interfaces": continue try: intf = net[node].intf(info_key) # info_key is a interface name except KeyError: # info_key is a node name assert info_key in net, \ "{} is neither a node nor an interface nor a special " \ "key of node {}".format(info_key, node) intf = net[node].intf(info_value["name"]) # Checks that the node is a neighbor assert otherIntf(intf).node.name == info_key, \ "The node {} has no neighbor node {}".format(node, info_key) # Checks the IP address assert info_value["ip"] == '%s/%s' % (intf.ip, intf.prefixLen), \ "The IP address of the record {} of node {} does not " \ "match".format(info_key, node) # Checks the IP prefixes prefixes = { ip.with_prefixlen for ip in itertools.chain(intf.ips(), intf.ip6s()) } assert set(info_value["ips"]) == prefixes, \ "The IP prefixes of the record {} of node {} do not " \ "match".format(info_key, node) finally: net.stop()
def _fill_connected_prefixes(self): for itf in realIntfList(self._node): for ra_prefix in itf.ra_prefixes: if isinstance(ra_prefix, AdvConnectedPrefix): for ip in itf.ip6s(exclude_lls=True): ra_prefix.prefixes.append(ip.network.with_prefixlen)