def __init__(self, list_id, access, as_paths): assert isinstance(list_id, int) assert isinstance(as_paths, Iterable) assert isinstance(access, Access) if not is_empty(as_paths): for as_path in as_paths: assert is_empty(as_path) or isinstance(as_path, int) self._list_id = list_id self._access = access self._as_paths = as_paths
def __init__(self, prefix, peer, origin, as_path, as_path_len, next_hop, local_pref, med, communities, permitted, prev_announcement=None): """ :param prefix: the prefix that's being announced :param peer: the peer from whom that prefix has been received (this is not technically in the BGP attributes set) :param origin: See BGP_ATTRS_ORIGIN :param as_path: List of AS numbers :param as_path_len: int :param next_hop: 1. If the BGP Peers are in different AS then the next_hop IP address that will be sent in the update message will be the IP address of the advertising router. 2. If the BGP peers are in the same AS (IBGP Peers), and the destination network being advertised in the update message is also in the same AS, then the next_hop IP address that will be sent in the update message will be the IP address of the advertising router 3. If the BGP peers are in the same AS (IBGP Peers), and the destination network being advertised in the update message is in an external AS, then the next_hop IP address that will be sent in the update message will be the IP address of the external peer router which sent the advertisement to this AS. :param local_pref: is only used in updates sent to the IBGP Peers, It is not passed on to the BGP peers in other autonomous systems. :param med: MED value, int :param communities: dict Community values: Community->True/False :param permitted: Access.permit or Access.deny :param prev_announcement: keep track of the announcement that generated this one """ #if isinstance(as_path, list): # if not is_symbolic(as_path_len) and not is_empty(as_path_len): # assert len(as_path) == as_path_len if prev_announcement: assert isinstance(prev_announcement, Announcement) assert is_symbolic or not is_empty(as_path) if not is_symbolic(as_path): for asnum in as_path: assert not is_empty(asnum) self.prefix = prefix self.peer = peer self.origin = origin self.as_path = as_path self.as_path_len = as_path_len self.next_hop = next_hop self.local_pref = local_pref self.med = med self.communities = communities self.permitted = permitted self.prev_announcement = prev_announcement self.__setattr__ = self._disable_mutations
def gen_route_map(self, node, routemap): assert isinstance(routemap, RouteMap) config = '' name = routemap.name for line in routemap.lines: if is_empty(line.lineno) or is_empty(line.access): continue no = line.lineno access = line.access.value if hasattr(line.access, 'value') else Access.permit config += "route-map %s %s %s\n" % (name, access, no) for match in line.matches: config += " %s\n" % self.gen_route_map_match(node, match) for action in line.actions: config += " %s\n" % self.gen_route_map_action(action) return config
def set_bgp_router_id(self, node, router_id): """Sets the BGP router ID of a given router""" assert self.is_bgp_enabled(node) if not is_empty(router_id) and not is_symbolic(router_id): assert isinstance(router_id, (int, ipaddress.IPv4Address)) if isinstance(router_id, int): assert router_id > 0 self.get_bgp_attrs(node)['router_id'] = router_id
def synthesize_connection(self, src, dst): """Synthesize connection between two routers""" err = "Routers (%s, %s) are not directly connected" % (src, dst) assert self.network_graph.has_edge(src, dst), err iface1 = self.network_graph.get_edge_iface(src, dst) iface2 = self.network_graph.get_edge_iface(dst, src) # Make sure interfaces are up if self.full and self.network_graph.is_iface_shutdown(src, iface1): raise InterfaceIsDownError(src, iface1) if self.full and self.network_graph.is_iface_shutdown(dst, iface2): raise InterfaceIsDownError(src, iface2) addr1 = self.network_graph.get_iface_addr(src, iface1) addr2 = self.network_graph.get_iface_addr(dst, iface2) err1 = "Address not set and not a hole for iface: %s-%s" % (src, iface1) err2 = "Address not set and not a hole for iface: %s-%s" % (dst, iface2) assert addr1, err1 assert addr2, err2 # Get the subnet for both ends of the line if is_empty(addr1) and is_empty(addr2): # No initial config is given # Then synthesize completely new subnet net1 = self._next_net self._next_net = self.get_next_net(net1) net2 = net1 elif is_empty(addr1) or is_empty(addr2): # Only one side is concrete net = addr1.network if not is_empty(addr1) else addr2.network net1 = net net2 = net else: # Both sides are concrete net1 = addr1.network net2 = addr2.network # The two interfaces must have the same network if net1 != net2: raise NotValidSubnetsError(src, iface1, net1, dst, iface2, net2) # Assign IP addresses to the first interface (if needed) if is_empty(addr1): for host in net1.hosts(): addr = ip_interface(u"%s/%d" % (host, net1.prefixlen)) if addr == addr2: continue addr1 = addr self.network_graph.set_iface_addr(src, iface1, addr) break # Assign IP addresses to the second interface (if needed) if is_empty(addr2): for host in net2.hosts(): addr = ip_interface(u"%s/%d" % (host, net2.prefixlen)) if addr != addr1: addr2 = addr self.network_graph.set_iface_addr(dst, iface2, addr) break # The interfaces must have unique IP addresses if addr1 == addr2: raise DuplicateAddressError(src, iface1, addr1, dst, iface2, addr2) assert iface1
def __init__(self, communities, additive=True): """ Set a list of communities to a route :param communities: list of Community :param additive: """ assert isinstance(communities, Iterable) for community in communities: assert is_empty(community) or isinstance(community, Community) self._communities = communities self._additive = additive
def _check_announce_prefix(self, node, prefix, protocol): if protocol == Protocols.OSPF: if is_empty(prefix): self.log.warn("Using unknown prefix to node %s", node) return True if not self.topo.is_ospf_enabled(node): return False if prefix not in self.topo.get_ospf_networks(node): return False else: raise ValueError("Unknown protocol value {}".format(protocol)) return True
def compute_bgp_peerings(self): for node in self.network_graph.routers_iter(): if not self.network_graph.get_bgp_asnum(node): # BGP is not configured on this router continue neighbors = self.network_graph.get_bgp_neighbors(node) for neighbor in neighbors: iface = self.network_graph.get_bgp_neighbor_iface(node, neighbor) if is_empty(iface): iface = self.synthesize_next_hop(node, neighbor) self.network_graph.set_bgp_neighbor_iface(node, neighbor, iface) assert iface, "Synthesize connected first"
def add_static_route(self, node, prefix, next_hop): """ Set a static route :param node: Router :param prefix: Prefixed to be routed :param next_hop: Router :return: """ attrs = self.get_static_routes(node) if is_empty(attrs): self.node[node]['static'] = {} self.node[node]['static'][prefix] = next_hop
def set_loopback_addr(self, node, loopback, addr): """ Assigns an IP address to a loopback interface :param node: name of the router :param loopback: name of loopback interface. e.graph., lo0, lo1, etc.. :param addr: an instance of ipaddress.IPv4Interface or ipaddress.IPv6Interface :return: None """ assert is_valid_add(addr) loopbacks = self.get_loopback_interfaces(node) if loopback not in loopbacks: loopbacks[loopback] = {} assert is_empty(loopbacks[loopback].get('addr', None)), loopbacks[loopback].get('addr', None) loopbacks[loopback]['addr'] = addr
def assign_loop_back_addresses(self): for node in sorted(self.network_graph.routers_iter()): loopbacks = self.network_graph.get_loopback_interfaces(node) for loopback in loopbacks: if not is_empty(self.network_graph.get_loopback_addr(node, loopback)): continue net = self._next_lo_net self._next_lo_net = self.get_next_net(net) ipaddr = None for ipaddr in net.hosts(): break if not ipaddr: ipaddr = net.network_address addr = ip_interface(ipaddr) self.network_graph.set_loopback_addr(node, loopback, addr)
def gen_route_map_match(self, node, match): config = '' if isinstance(match, MatchCommunitiesList): if match.match.list_id in self._empty_community_lists.get( node, []): err = "Community list id {} is used in a match but it's empty".format( match.match.list_id) assert False, err config += 'match community %d' % match.match.list_id elif isinstance(match, MatchIpPrefixListList): name = match.match.name ips = self.g.get_ip_preflix_lists(node) err = "IP list '%s' is not registered at Node '%s': %s" % ( name, node, ips) assert name in ips, err if not all([is_empty(p) for p in ips[match.match.name].networks]): config += 'match ip address prefix-list %s' % match.match.name elif isinstance(match, MatchAsPath): list_no = None for tmp in self.g.get_as_path_list(node).values(): if tmp.as_paths == match.match: list_no = tmp.list_id if not list_no: list_no = next(self._next_as_paths[node]) as_path = ASPathList(list_id=list_no, access=Access.permit, as_paths=match.match) self.g.add_as_path_list(node, as_path) config += 'match as-path %s' % list_no elif isinstance(match, MatchNextHop): next_hop = match.match parsed = None if isinstance(next_hop, basestring): if '_DASH_' in next_hop: parsed = next_hop.split('_DASH_') else: parsed = next_hop.split('-') if parsed and self.g.has_node(parsed[0]): router = parsed[0] iface = '/'.join(parsed[1:]) next_hop = self.g.get_interface_loop_addr(router, iface) self.prefix_map[match.match] = next_hop if hasattr(next_hop, 'ip'): next_hop = next_hop.ip config += 'match ip next-hop %s' % next_hop else: raise ValueError('Unknown match type %s' % match) return config
def gen_static_routes(self, node): config = "" static_routes = self.g.get_static_routes(node) if not static_routes or is_empty(static_routes): return config for prefix, next_hop in static_routes.iteritems(): net = self.prefix_lookup(prefix) if self.g.is_router(next_hop): iface = self.g.get_edge_iface(next_hop, node) addr = self.g.get_iface_addr(next_hop, iface) nhop_addr = addr.ip else: nhop_addr = next_hop config += "ip route %s %s %s\n" % (net.network_address, net.netmask, nhop_addr) config += "!\n" return config
def __init__(self, matches, actions, access, lineno=None): if matches is None: matches = [] if actions is None: actions = [] assert isinstance(matches, Iterable) assert isinstance(actions, Iterable) for match in matches: assert isinstance(match, Match), "Expected a match but found %s" % match for action in actions: assert isinstance(action, Action) assert is_empty(access) or isinstance(access, Access) self._matches = matches self._actions = actions self._access = access self._lineno = lineno
def gen_acl(self, node): config = "" acl_lists = self.g.get_acls(node) # print("acl lists", acl_lists) if not acl_lists or is_empty(acl_lists): return config for inface in acl_lists: for acl_num in acl_lists[inface]: acl_content = acl_lists[inface][acl_num] for item in acl_content[1:]: # could be extended later for more acl formats if len(item) == 5: #item = (state, src_ip, src_mask, dst_ip, src_mask) config += "access-list " + str(acl_num) + " " + str(item[0]) + " ip " + str(item[1]) + " " + str(item[2]) \ + " " + str(item[3]) + " " + str(item[4]) + "\n" config += "access-list " + str(acl_num) + " permit any\n" return config
def gen_community_list(self, node, community_list): """ Generate config lines for community list :param community_list: :return: configs string """ assert isinstance(community_list, CommunityList) config = '' list_id = community_list.list_id access = community_list.access.value communities = [c.value for c in community_list.communities if not is_empty(c)] if communities: config += "ip community-list %d %s %s\n" % (list_id, access, ' '.join(communities)) else: if node not in self._empty_community_lists: self._empty_community_lists[node] = [] self._empty_community_lists[node].append(list_id) return config
def add_acl(self, node, interface, inout, acl_number=None): """ add Access_List :param node: Router :param interface: interface name :param inout: 'in' or 'out' """ attrs = self.get_acls(node) if is_empty(attrs): self.node[node]['acls'] = {} if interface not in self.node[node]['acls']: self.node[node]['acls'][interface] = {} if not acl_number: if not self.node[node]['acls'][interface]: acl_number = random.randint(0, 20) else: max_num = max(self.node[node]['acls'][interface], key=self.node[node]['acls'][interface].get) acl_number = max_num + 1 self.node[node]['acls'][interface][acl_number] = [inout] return acl_number
def test_one_side_concrete(self): # Arrange graph = self.get_two_nodes() addr1 = ip_interface(u"192.168.0.1/24") # Set Iface for R1 to R2 iface = 'Fa0/0' graph.add_iface('R1', iface, is_shutdown=False) graph.set_iface_addr('R1', iface, addr1) graph.set_edge_iface('R1', 'R2', iface) graph.set_iface_description('R1', iface, '' "To {}" ''.format('R2')) # Set Iface for R2 to R1 iface = 'Fa0/0' graph.add_iface('R2', iface, is_shutdown=False) graph.set_iface_addr('R2', iface, VALUENOTSET) graph.set_edge_iface('R2', 'R1', iface) graph.set_iface_description('R2', iface, '' "To {}" ''.format('R1')) # Act syn = ConnectedSyn(graph, full=True) syn.synthesize() # Assert self.assertFalse(is_empty(graph.get_iface_addr('R2', iface)))
def gen_ip_prefix_list(self, node, prefix_list): """ Generate config lines for ip prefix-list :param prefix_list: :return: configs string """ assert isinstance(prefix_list, IpPrefixList) config = '' access = prefix_list.access.value networks = prefix_list.networks name = prefix_list.name for i, network in enumerate(networks): if is_empty(network): continue network = self.prefix_lookup(network) lineno = (i + 1) * 10 network = ip_network(str(network)) addr = str(getattr(network, 'network_address', network)) prefixlen = getattr(network, 'prefixlen', 32) config += "ip prefix-list %s seq %d %s %s/%d\n" % ( name, lineno, access, addr, prefixlen) return config
def _check_ospf_announced(self, router, iface): """Return True if the address is announced over OSPF""" addr = self.topo.get_interface_loop_addr(router, iface) assert not is_empty(addr) routers = [(router, iface)] # Maybe the neighbor is announcing it for neighbor in self.topo.neighbors(router): if not self.topo.is_router(neighbor): continue if iface != self.topo.get_edge_iface(router, neighbor): continue routers.append( (neighbor, self.topo.get_edge_iface(neighbor, router))) for router, iface in routers: if not self.topo.is_ospf_enabled(router): continue for network in self.topo.get_ospf_networks(router): if not isinstance(network, (IPv4Network, IPv6Network)): if iface == network: return True elif addr in network: return True return False
def gen_all_interface_configs(self, node): """ Iterate over all interfaces (including loopbacks) to generate their configs :param node: router name :return: string configs """ config = '' for neighbor in self.g.neighbors(node): iface = self.g.get_edge_iface(node, neighbor) acl_declare = None acl_lists = self.g.get_acls(node) if acl_lists and not is_empty(acl_lists): if iface in acl_lists: acl_declare = [] for acl_num in acl_lists[iface]: print("acl_num", acl_num) acl_content = acl_lists[iface][acl_num] acl_declare.append((str(acl_num), acl_content[0])) #addr = self.graph.get_edge_addr(node, neighbor) addr = self.g.get_iface_addr(node, iface) desc = self.g.get_iface_description(node, iface) if self.g.is_ospf_enabled(node) and self.g.is_ospf_enabled( neighbor): ospf_cost = self.g.get_edge_ospf_cost(node, neighbor) else: ospf_cost = None config += self.gen_iface_config(node, iface, addr, desc, False, ospf_cost, acl_declare) # Loop back interface for lo in sorted(self.g.get_loopback_interfaces(node)): addr = self.g.get_loopback_addr(node, lo) desc = self.g.get_loopback_description(node, lo) config += self.gen_iface_config(node, lo, addr, desc, True, None) return config
def __init__(self, as_path): assert is_empty(as_path) or isinstance(as_path, Iterable) self._value = as_path
def __init__(self, next_hop): assert is_empty(next_hop) or isinstance(next_hop, basestring) self._value = next_hop
def __init__(self, prefix): assert is_empty(prefix) or isinstance(prefix, basestring) self._value = prefix
def access(self, value): """Set to drop to allow (see Access)""" if not is_empty(self._access): raise ValueError("Access already set to %s" % self._access) assert isinstance(value, Access) self._access = value
def __init__(self, as_path_len): assert is_empty(as_path_len) or isinstance(as_path_len, int) self._value = as_path_len
def __eq__(self, other): if is_empty(self._value): return False return self.value == getattr(other, 'value', None)
def value(self, value): if not is_empty(self._value): raise ValueError("Value already set to %s" % str(self._value)) self._value = value
def __init__(self, med): assert is_empty(med) or isinstance(med, int) self._value = med
def __init__(self, peer): assert is_empty(peer) or isinstance(peer, basestring) self._value = peer