def scan_netdevs(): scan = [] ipr = IPRoute() try: for part in ipr.get_links(): new_link = {} new_link["netlink_msg"] = part new_link["index"] = part["index"] new_link["name"] = part.get_attr("IFLA_IFNAME") hwaddr = part.get_attr("IFLA_ADDRESS") if hwaddr: new_link["hwaddr"] = normalize_hwaddr(hwaddr) else: new_link["hwaddr"] = None addrs = ipr.get_addr(index=new_link["index"]) new_link["ip_addrs"] = addrs scan.append(new_link) except: raise finally: ipr.close() return scan
def del_addr(self, ip): iproute= IPRoute() br = iproute.link_lookup(ifname=BRIDGE_INTERFACE_NAME)[0] if not self.check_exists(ip, iproute, br): iproute.addr('delete', br, address=ip, mask=30) iproute.close() iproute = None
class BasicTest(object): def setup(self): require_user('root') self.ip = IPRoute() self.ip.link('add', index=0, ifname='dummyX', linkinfo={'attrs': [['IFLA_INFO_KIND', 'dummy']]}) self.interface = self.ip.link_lookup(ifname='dummyX')[0] def teardown(self): self.ip.link('delete', index=self.interface) self.ip.close() def get_qdiscs(self): return [x for x in self.ip.get_qdiscs() if x['index'] == self.interface] def get_qdisc(self): # get qdiscs list and filter out our interface qds = self.get_qdiscs() if qds: return qds[0] else: return None
def set_net(lease): ipr = IPRoute() try: index = ipr.link_lookup(ifname=lease.interface)[0] except IndexError as e: logger.error('Interface %s not found, can not set IP.', lease.interface) try: ipr.addr('add', index, address=lease.address, mask=int(lease.subnet_mask_cidr)) except NetlinkError as e: if ipr.get_addr(index=index)[0].\ get_attrs('IFA_ADDRESS')[0] == lease.address: logger.debug('Interface %s is already set to IP %s' % (lease.interface, lease.address)) else: logger.error(e) else: logger.debug('Interface %s set to IP %s' % (lease.interface, lease.address)) try: ipr.route('add', dst='0.0.0.0', gateway=lease.router, oif=index) except NetlinkError as e: if ipr.get_routes(table=254)[0].\ get_attrs('RTA_GATEWAY')[0] == lease.router: logger.debug('Default gateway is already set to %s' % (lease.router)) else: logger.error(e) else: logger.debug('Default gateway set to %s', lease.router) ipr.close() set_dns(lease)
def scan_netdevs(): scan = [] ipr = IPRoute() try: for part in ipr.get_links(): new_link = {} new_link["netlink_msg"] = part new_link["index"] = part["index"] new_link["name"] = part.get_attr("IFLA_IFNAME") # # FIXME: # # nlmsg.get_attr() returns None if there is no # such attribute in the NLA chain; if hwaddr is None, # normalize_hwaddr(hwaddr) will raise AttributeError(), # since None has no upper(). The issue is that the # AttributeError() will be a bit unrelated to the # root cause, and since that it will be confusing. # hwaddr = part.get_attr("IFLA_ADDRESS") new_link["hwaddr"] = normalize_hwaddr(hwaddr) scan.append(new_link) except: raise finally: ipr.close() return scan
def test_close(self): ip = IPRoute() ip.get_links() ip.close() # Shouldn't be able to use the socket after closing with assert_raises(socket.error): ip.get_links()
def test_iproute(self): ip = IPRoute() try: assert len(ip.get_links()) > 1 except: raise finally: ip.close()
class LoopBackAddress(object): def __init__(self, logger, config): self.config = config self._label = self.config.get("balancer.agent.plugins.loopback", "label") self._prefix = self.config.getint("balancer.agent.plugins.loopback", "prefix") self._ip = IPRoute() self._idx = self._ip.link_lookup(ifname="lo")[0] self.logger = logger @property def configured_ips(self): ips = set() for link in self._ip.get_addr(index=self._idx): if not "attrs" in link: continue interface = None ip = None for key, value in link["attrs"]: if key == "IFA_ADDRESS": ip = value elif key == "IFA_LABEL": interface = value elif interface == self._label and ip not in ["127.0.0.1", None]: ips.add(ip) return ips def close(self): if self._ip: self._ip.close() def __disable__(self, ip): self.logger("Disabling {}/{} on {}".format(ip, self._prefix, self._label)) self._ip.addr("del", index=self._idx, address=ip, prefixlen=self._prefix, label=self._label) def __enable__(self, ip): self.logger("Enabling {}/{} on {}".format(ip, self._prefix, self._label)) self._ip.addr("add", index=self._idx, address=ip, prefixlen=self._prefix, label=self._label) def apply(self, frontends): configured_ips = self.configured_ips ips = set() for frontend, ips_and_ports in frontends.items(): for ip, port in ips_and_ports: ips.add(ip) to_disabled = configured_ips.difference(ips) to_enabled = ips.difference(configured_ips) for ip in to_disabled: self.__disable__(ip) for ip in to_enabled: self.__enable__(ip)
def getInterfaceState(ifname): try: ip = IPRoute() state = ip.get_links(ip.link_lookup(ifname=ifname))[0].get_attr('IFLA_OPERSTATE') ip.close() except Exception as e: raise Exception("getInterfaceState: Collecting interface status for %s failed: %s" % (ifname,str(e))) else: if state == "UP": return True return False
def __init__(self): ip = IPRoute() pppLinksIds = ip.link_lookup(ifname=INTERFACE) if len(pppLinksIds) > 0: state = ip.get_links(pppLinksIds)[0].get_attr('IFLA_OPERSTATE') if state == 'UP': self.connect = True self.details = INTERFACE + " interface UP" else: self.connect = False self.details = INTERFACE + " exist but no UP" ip.close() else: self.connect = False self.details = INTERFACE + " interface no exist" super(StatusSchema, self).__init__()
def Server(cmdch, brdch): try: ipr = IPRoute() lock = ipr._sproxy.lock ipr._s_channel = brdch poll = select.poll() poll.register(ipr, select.POLLIN | select.POLLPRI) poll.register(cmdch, select.POLLIN | select.POLLPRI) except Exception as e: cmdch.send({'stage': 'init', 'error': e}) return 255 # all is OK so far cmdch.send({'stage': 'init', 'error': None}) # 8<------------------------------------------------------------- while True: events = poll.poll() for (fd, event) in events: if fd == ipr.fileno(): bufsize = ipr.getsockopt(SOL_SOCKET, SO_RCVBUF) // 2 with lock: brdch.send({'stage': 'broadcast', 'data': ipr.recv(bufsize)}) elif fd == cmdch.fileno(): cmd = cmdch.recv() if cmd['stage'] == 'shutdown': poll.unregister(ipr) poll.unregister(cmdch) ipr.close() return elif cmd['stage'] == 'command': error = None try: ret = getattr(ipr, cmd['name'])(*cmd['argv'], **cmd['kwarg']) except Exception as e: error = e error.tb = traceback.format_exc() cmdch.send({'stage': 'command', 'error': error, 'return': ret, 'cookie': cmd['cookie']})
class TestMisc(object): def setup(self): self.ip = IPRoute() def teardown(self): self.ip.close() def test_get_policy_map(self): assert isinstance(self.ip.get_policy_map(), dict) def test_register_policy(self): self.ip.register_policy(100, nlmsg) self.ip.register_policy({101: nlmsg}) self.ip.register_policy(102, nlmsg) assert self.ip.get_policy_map()[100] == nlmsg assert self.ip.get_policy_map(101)[101] == nlmsg assert self.ip.get_policy_map([102])[102] == nlmsg self.ip.unregister_policy(100) self.ip.unregister_policy([101]) self.ip.unregister_policy({102: nlmsg}) assert 100 not in self.ip.get_policy_map() assert 101 not in self.ip.get_policy_map() assert 102 not in self.ip.get_policy_map() def test_addrpool_expand(self): # see coverage for i in range(100): self.ip.get_addr() def test_nla_compare(self): lvalue = self.ip.get_links() rvalue = self.ip.get_links() assert lvalue is not rvalue if lvalue == rvalue: pass if lvalue != rvalue: pass assert lvalue != 42
def test_isinstance(self): from pyroute2 import IPRSocket from pyroute2 import IPRoute from pyroute2.iproute import IPRoute as IPRoute_real from pyroute2.netlink.rtnl.iprsocket import IPRSocket as IPRSocket_real ipr1 = IPRoute() ipr2 = IPRoute_real() ips1 = IPRSocket() ips2 = IPRSocket_real() # positive assert isinstance(ips1, IPRSocket) assert isinstance(ips2, IPRSocket) assert isinstance(ips1, IPRSocket_real) assert isinstance(ips2, IPRSocket_real) assert isinstance(ipr1, IPRoute) assert isinstance(ipr2, IPRoute) assert isinstance(ipr1, IPRoute_real) assert isinstance(ipr2, IPRoute_real) # negative assert not isinstance(ips1, IPRoute) assert not isinstance(ips2, IPRoute) assert not isinstance(ips1, IPRoute_real) assert not isinstance(ips2, IPRoute_real) # this must succeed -- IPRoute is a subclass of IPRSocket assert isinstance(ipr1, IPRSocket) assert isinstance(ipr2, IPRSocket) assert isinstance(ipr1, IPRSocket_real) assert isinstance(ipr2, IPRSocket_real) ips1.close() ips2.close() ipr1.close() ipr2.close()
class TestMisc(object): def setup(self): self.ip = IPRoute() def teardown(self): self.ip.close() def test_addrpool_expand(self): # see coverage for i in range(100): self.ip.get_addr() def test_nla_compare(self): lvalue = self.ip.get_links() rvalue = self.ip.get_links() assert lvalue is not rvalue if lvalue == rvalue: pass if lvalue != rvalue: pass assert lvalue != 42
def test_fileno(self): require_python(3) ip1 = IPRoute() ip2 = IPRoute(fileno=ip1.fileno()) ip2.bind() try: ip1.bind() except OSError as e: if e.errno != 22: # bind -> Invalid argument raise ip1.close() try: ip2.get_links() except OSError as e: if e.errno != 9: # sendto -> Bad file descriptor raise try: ip2.close() except OSError as e: if e.errno != 9: # close -> Bad file descriptor raise
class BasicTest(object): def setup(self): require_user('root') self.ip = IPRoute() self.ifname = uifname() self.ip.link_create(ifname=self.ifname, kind='dummy') self.interface = self.ip.link_lookup(ifname=self.ifname)[0] def teardown(self): self.ip.link('delete', index=self.interface) self.ip.close() def get_qdiscs(self): return [x for x in self.ip.get_qdiscs() if x['index'] == self.interface] def get_qdisc(self): # get qdiscs list and filter out our interface qds = self.get_qdiscs() if qds: return qds[0] else: return None
class TestRule(object): def setup(self): require_user('root') self.ip = IPRoute() self.ifname = uifname() self.ip.link_create(ifname=self.ifname, kind='dummy') self.interface = self.ip.link_lookup(ifname=self.ifname)[0] def teardown(self): self.ip.link('delete', index=self.interface) self.ip.close() def test_basic(self): self.ip.rule('add', 10, 32000) assert len([ x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32000 and x.get_attr('FRA_TABLE') == 10 ]) == 1 self.ip.rule('delete', 10, 32000) assert len([ x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32000 and x.get_attr('FRA_TABLE') == 10 ]) == 0 def test_fwmark(self): self.ip.rule('add', 15, 32006, fwmark=10) assert len([ x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32006 and x.get_attr('FRA_TABLE') == 15 and x.get_attr('FRA_FWMARK') ]) == 1 self.ip.rule('delete', 15, 32006, fwmark=10) assert len([ x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32006 and x.get_attr('FRA_TABLE') == 15 and x.get_attr('FRA_FWMARK') ]) == 0 def test_bad_table(self): try: self.ip.rule('add', -1, 32000) except Exception: pass def test_big_table(self): self.ip.rule('add', 1024, 32000) assert len([ x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32000 and x.get_attr('FRA_TABLE') == 1024 ]) == 1 self.ip.rule('delete', 1024, 32000) assert len([ x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32000 and x.get_attr('FRA_TABLE') == 1024 ]) == 0 def test_src_dst(self): self.ip.rule('add', 17, 32005, src='10.0.0.0', src_len=24, dst='10.1.0.0', dst_len=24) assert len([ x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32005 and x.get_attr( 'FRA_TABLE') == 17 and x.get_attr('FRA_SRC') == '10.0.0.0' and x.get_attr('FRA_DST') == '10.1.0.0' and x['src_len'] == 24 and x['dst_len'] == 24 ]) == 1 self.ip.rule('delete', 17, 32005, src='10.0.0.0', src_len=24, dst='10.1.0.0', dst_len=24) assert len([ x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32005 and x.get_attr( 'FRA_TABLE') == 17 and x.get_attr('FRA_SRC') == '10.0.0.0' and x.get_attr('FRA_DST') == '10.1.0.0' and x['src_len'] == 24 and x['dst_len'] == 24 ]) == 0
def Delete(): ip = IPRoute() idx = ip.link_lookup(ifname=_Device)[0] for i in range(1, int(_NumberOfRuleToBeEnforced) + 1): ip.route('delete', dst=_Prefix + str(i) + '/128', oif=idx) ip.close()
class TestRule(object): def setup(self): require_user('root') self.ip = IPRoute() self.ifname = uifname() self.ip.link('add', ifname=self.ifname, kind='dummy') self.interface = self.ip.link_lookup(ifname=self.ifname)[0] def teardown(self): self.ip.link('delete', index=self.interface) self.ip.close() def test_basic(self): self.ip.rule('add', 10, 32000) assert len([x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32000 and x.get_attr('FRA_TABLE') == 10]) == 1 self.ip.rule('delete', 10, 32000) assert len([x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32000 and x.get_attr('FRA_TABLE') == 10]) == 0 def test_fwmark(self): self.ip.rule('add', 15, 32006, fwmark=10) assert len([x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32006 and x.get_attr('FRA_TABLE') == 15 and x.get_attr('FRA_FWMARK')]) == 1 self.ip.rule('delete', 15, 32006, fwmark=10) assert len([x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32006 and x.get_attr('FRA_TABLE') == 15 and x.get_attr('FRA_FWMARK')]) == 0 def test_fwmark_mask_normalized(self): self.ip.rule('add', 15, 32006, fwmark=10, fwmask=20) assert len([x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32006 and x.get_attr('FRA_TABLE') == 15 and x.get_attr('FRA_FWMARK') and x.get_attr('FRA_FWMASK')]) == 1 self.ip.rule('delete', 15, 32006, fwmark=10, fwmask=20) assert len([x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32006 and x.get_attr('FRA_TABLE') == 15 and x.get_attr('FRA_FWMARK') and x.get_attr('FRA_FWMASK')]) == 0 def test_fwmark_mask_raw(self): self.ip.rule('add', 15, 32006, fwmark=10, FRA_FWMASK=20) assert len([x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32006 and x.get_attr('FRA_TABLE') == 15 and x.get_attr('FRA_FWMARK') and x.get_attr('FRA_FWMASK')]) == 1 self.ip.rule('delete', 15, 32006, fwmark=10, FRA_FWMASK=20) assert len([x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32006 and x.get_attr('FRA_TABLE') == 15 and x.get_attr('FRA_FWMARK') and x.get_attr('FRA_FWMASK')]) == 0 def test_bad_table(self): try: self.ip.rule('add', -1, 32000) except Exception: pass def test_big_table(self): self.ip.rule('add', 1024, 32000) assert len([x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32000 and x.get_attr('FRA_TABLE') == 1024]) == 1 self.ip.rule('delete', 1024, 32000) assert len([x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32000 and x.get_attr('FRA_TABLE') == 1024]) == 0 def test_src_dst(self): self.ip.rule('add', 17, 32005, src='10.0.0.0', src_len=24, dst='10.1.0.0', dst_len=24) assert len([x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32005 and x.get_attr('FRA_TABLE') == 17 and x.get_attr('FRA_SRC') == '10.0.0.0' and x.get_attr('FRA_DST') == '10.1.0.0' and x['src_len'] == 24 and x['dst_len'] == 24]) == 1 self.ip.rule('delete', 17, 32005, src='10.0.0.0', src_len=24, dst='10.1.0.0', dst_len=24) assert len([x for x in self.ip.get_rules() if x.get_attr('FRA_PRIORITY') == 32005 and x.get_attr('FRA_TABLE') == 17 and x.get_attr('FRA_SRC') == '10.0.0.0' and x.get_attr('FRA_DST') == '10.1.0.0' and x['src_len'] == 24 and x['dst_len'] == 24]) == 0
class NetworkConfig(unittest.TestCase): '''Tests the functionality of the network configurer''' def setUp(self): self._iproute = IPRoute() # Unfortunately, when creating dummy interfaces, you'll end up with an # interface named dummyX no matter what you do self._iproute.link('add', name='dummy0', kind='dummy') self._iproute.link('add', name='dummy1', kind='dummy') self._dummy0_idx = self._iproute.link_lookup(ifname='dummy0')[0] self._dummy1_idx = self._iproute.link_lookup(ifname='dummy1')[0] def tearDown(self): # Remove our dummy interfaces self._iproute.link('remove', index=self._dummy0_idx) self._iproute.link('remove', index=self._dummy1_idx) self._iproute.close() def set_mac_addresses(self): # Some tests need the MAC addresses standardized, other's dont self._iproute.link('set', index=self._dummy0_idx, address=LAN_MAC_ADDRESS) self._iproute.link('set', index=self._dummy1_idx, address=MONITOR_MAC_ADDRESS) def configure_interfaces(self, config_file=None): '''Sets up interfaces for most tests''' nc = ndr_netcfg.NetworkConfiguration(config_file) nc.rename_interface("dummy0", "lan127") nc.set_configuration_method( "lan127", ndr_netcfg.InterfaceConfigurationMethods.STATIC) nc.add_static_addr("lan127", "10.1.177.2", 24) nc.rename_interface("dummy1", "monitor234") nc.set_configuration_method( "monitor234", ndr_netcfg.InterfaceConfigurationMethods.STATIC) nc.add_static_addr("monitor234", "10.2.177.2", 24) nc.apply_configuration() return nc def test_renaming_interfaces(self): nc = self.configure_interfaces() # If everything worked as planned, we should successfully be able to get the index numbers # based on the new interface names self.assertEqual(self._dummy0_idx, self._iproute.link_lookup(ifname='lan127')[0]) self.assertEqual(self._dummy1_idx, self._iproute.link_lookup(ifname='monitor234')[0]) def test_serialization_of_data_to_yaml(self): '''Tests that data can be properly serialized to YAML, AND that only managed interfaces end up in the resulting YAML file''' try: fd, scratch_config = tempfile.mkstemp() os.close(fd) # Don't need to write anything to it # First we need to setup the configuration instances self.set_mac_addresses() nc = self.configure_interfaces(scratch_config) # And write it out to the YAML file nc.export_configuration() # Now we need to read it back as YAML and make sure all the stuff we expect is there with open(scratch_config, 'r') as f: yaml_contents = yaml.safe_load(f.read()) # We should only have one element at the moment, interfaces self.assertEqual(len(yaml_contents), 1) interfaces = yaml_contents['interfaces'] # We should have two interfaces self.assertEqual(len(interfaces), 2) matched_lan127 = False matched_monitor234 = False for interface in interfaces: if interface['name'] == 'lan127': # Confirm the lan127 interface is set properly. self.assertEqual(interface['method'], 'static') self.assertEqual(interface['mac_address'], LAN_MAC_ADDRESS) matched_lan127 = True # And again for monitor234 if interface['name'] == 'monitor234': self.assertTrue(interface['method'], 'static') self.assertEqual(interface['mac_address'], MONITOR_MAC_ADDRESS) matched_monitor234 = True # Make sure we got both things self.assertTrue(matched_lan127) self.assertTrue(matched_monitor234) finally: os.remove(scratch_config) def test_import_and_apply_configuration(self): '''Test importing an example configuration and applying it''' self.set_mac_addresses() nc = ndr_netcfg.NetworkConfiguration(IMPORT_CFG_TEST) nc.apply_configuration() # If everything worked as planned, we should successfully be able to get the index numbers # based on the new interface names self.assertEqual(self._dummy0_idx, self._iproute.link_lookup(ifname='lan127')[0]) self.assertEqual(self._dummy1_idx, self._iproute.link_lookup(ifname='monitor234')[0]) def test_v4_netmask_retrivial(self): '''Confirms that we can properly retrieve v4 netmask and broadcast information''' nc = self.configure_interfaces() lan127_interface = nc.get_nic_config_by_name('lan127') self.assertEqual(len(lan127_interface.current_ip_addresses), 1) ip_address_block = lan127_interface.current_ip_addresses[0] self.assertEqual(ip_address_block.ip_addr, ipaddress.ip_address("10.1.177.2")) self.assertEqual(ip_address_block.prefixlen, 24) def test_get_all_managed_interfaces(self): '''Makes sure we only return the managed interfaces''' nc = self.configure_interfaces() self.assertEqual(len(nc.get_all_managed_interfaces()), 2) def test_get_ip_network(self): '''Tests the functionality of getting an IP network from an IPAddressConfig''' ip_address_config = ndr_netcfg.IPAddressConfig("192.168.2.4", 24) ipnet = ipaddress.ip_network("192.168.2.0/24") self.assertEqual(ip_address_config.ip_network(), ipnet) def test_get_all_ip_networks(self): '''Tests that we can find and retrieve all IP networks''' nc = self.configure_interfaces() ip_nets = nc.retrieve_home_ip_networks() ip_net1 = ipaddress.ip_network('10.1.177.0/24') ip_net2 = ipaddress.ip_network('10.1.177.0/24') # We'll see localhost here, as well as any unmanaged interfaces so this is the best that # we can hope to do for testing this function. self.assertIn(ip_net1, ip_nets) self.assertIn(ip_net2, ip_nets)
class AtcdLinuxShaper(AtcdThriftHandlerTask): ID_MANAGER_ID_MIN = HANDLE_MIN ID_MANAGER_ID_MAX = HANDLE_MAX def initTask(self): self.ipr = IPRoute() super(AtcdLinuxShaper, self).initTask() def stop(self): self._release_ipr() def _release_ipr(self): self.ipr.close() def _links_lookup(self): try: self.lan['id'] = self.ipr.link_lookup(ifname=self.lan_name)[0] self.wan['id'] = self.ipr.link_lookup(ifname=self.wan_name)[0] except IndexError: self._release_ipr() msg = 'One of the following interfaces does not exist:' \ ' {0}, {1}'.format(self.lan_name, self.wan_name) self.logger.critical(msg) raise Exception(msg) def initialize_shaping_system(self): """Initialize Iptables and TC subsystems Only call once as this will FLUSH all current shapings... """ self.logger.info("Calling initialize_shaping_system") self._initialize_iptables() self._initialize_tc() def _initialize_iptables(self): """Initialize IPTables by flushing all rules in FORWARD chain from mangle table. """ cmd = "{0} -t mangle -F FORWARD".format(self.iptables) self.run_cmd(cmd) def _initialize_tc_for_interface(self, eth): """Initialize TC on a given interface. If an exception is thrown, it will be forwarded to the main loop unless it can be ignored. Args: eth: the interface to flush TC on. Raises: NetlinkError: An error occured initializing TC subsystem. Exception: Any other exception thrown during initialization. """ idx = 0x10000 eth_name = eth['name'] eth_id = eth['id'] try: self.logger.info("deleting root QDisc on {0}".format(eth_name)) self.ipr.tc(RTM_DELQDISC, None, eth_id, 0, parent=TC_H_ROOT) except Exception as e: # a (2, 'No such file or directory') can be thrown if there is # nothing to delete. Ignore such error, return the error otherwise if isinstance(e, NetlinkError) and e.code == 2: self.logger.warning( "could not delete root QDisc. There might " "have been nothing to delete") else: self.logger.exception( 'Initializing root Qdisc for {0}'.format(eth_name) ) raise try: self.logger.info("setting root qdisc on {0}".format(eth_name)) self.ipr.tc(RTM_NEWQDISC, "htb", eth_id, idx, default=0) except Exception as e: self.logger.exception( 'Setting root Qdisc for {0}'.format(eth_name) ) raise return TrafficControlRc(code=ReturnCode.OK) def _initialize_tc(self): """Initialize TC root qdisc on both LAN and WAN interface. """ for netif in [self.lan, self.wan]: self._initialize_tc_for_interface(netif) def _unset_htb_class(self, mark, eth): """Given a mark and an interface, unset the HTB class. Args: mark: The mark based on which we delete the class. eth: The interface on which to delete that class id. Returns: A TrafficControlRc containing information on success/failure. """ ifid = eth['id'] idx = 0x10000 + mark try: self.logger.info( "deleting class on IFID {0}, classid {1}".format( eth['name'], int_to_classid(idx) ) ) self.ipr.tc(RTM_DELTCLASS, 'htb', ifid, idx) except NetlinkError as e: return TrafficControlRc( code=ReturnCode.NETLINK_HTB_ERROR, message=str(e)) except Exception as e: self.logger.exception('_unset_htb_class') exc_info = sys.exc_info() return TrafficControlRc( code=ReturnCode.UNKNOWN_HTB_ERROR, message=str(exc_info)) return TrafficControlRc(code=ReturnCode.OK) def _set_htb_class(self, mark, eth, shaping): """Given a mark, an interface and shaping settings, set the HTB class. Args: mark: The mark based on which we create the class eth: The interface on which to create that class id. shaping: The shaping settings to set. Returns: A TrafficControlRc containing information on success/failure. """ ifid = eth['id'] idx = 0x10000 + mark parent = 0x10000 self.logger.info( "create new HTB class on IFID {0}, classid {1}," "parent {2}, rate {3}kbits".format( eth['name'], int_to_classid(idx), int_to_classid(parent), shaping.rate or 2**22 - 1) ) try: self.ipr.tc( RTM_NEWTCLASS, 'htb', ifid, idx, parent=parent, rate="{}kbit".format(shaping.rate or (2**22 - 1)), ) except NetlinkError as e: return TrafficControlRc( code=ReturnCode.NETLINK_HTB_ERROR, message=str(e)) except Exception as e: self.logger.exception('_set_htb_class') exc_info = sys.exc_info() return TrafficControlRc( code=ReturnCode.UNKNOWN_HTB_ERROR, message=str(exc_info)) return TrafficControlRc(code=ReturnCode.OK) def _unset_netem_qdisc(self, mark, eth): """This is not needed as deleting the HTB class is sufficient to remove the netem qdisc""" pass def _set_netem_qdisc(self, mark, eth, shaping): """Given a mark, interface and shaping settings, create the NetEm Qdisc. Args: mark: The mark based on which we create the Qdisc. eth: The interface on which we will create the Qdisc. shaping: The shaping settings for that interface. Returns: A TrafficControlRc containing information on success/failure. """ ifid = eth['id'] parent = 0x10000 + mark idx = 0 # automatically assign a handleid self.logger.info( "create new Netem qdisc on IFID {0}, parent {1}," " loss {2}%, delay {3}".format( eth['name'], int_to_classid(parent), shaping.loss.percentage, shaping.delay.delay * 1000) ) try: self.ipr.tc( RTM_NEWQDISC, 'netem', ifid, idx, parent=parent, loss=shaping.loss.percentage, delay=shaping.delay.delay * 1000, jitter=shaping.delay.jitter * 1000, delay_corr=shaping.delay.correlation, loss_corr=shaping.loss.correlation, prob_reorder=shaping.reorder.percentage, corr_reorder=shaping.reorder.correlation, gap=shaping.reorder.gap, prob_corrupt=shaping.corruption.percentage, corr_corrupt=shaping.corruption.correlation, ) except NetlinkError as e: return TrafficControlRc( code=ReturnCode.NETLINK_NETEM_ERROR, message=str(e)) except Exception as e: self.logger.exception('_set_netem_qdisc') exc_info = sys.exc_info() return TrafficControlRc( code=ReturnCode.UNKNOWN_NETEM_ERROR, message=str(exc_info)) return TrafficControlRc(code=ReturnCode.OK) def _unset_filter(self, mark, eth): """Given a mark and an interface, delete the filter. Args: mark: The mark based on which we delete the filter. eth: The interface on which we delete the filter. Returns: A TrafficControlRc containing information on success/failure. """ ifid = eth['id'] parent = 0x10000 self.logger.info( "deleting filter on IFID {0}, handle {1:X}".format( eth['name'], mark ) ) try: self.ipr.tc( RTM_DELTFILTER, 'fw', ifid, mark, parent=parent, protocol=ETH_P_IP, prio=PRIO ) except NetlinkError as e: return TrafficControlRc( code=ReturnCode.NETLINK_FW_ERROR, message=str(e)) except Exception as e: self.logger.exception('_unset_filter') exc_info = sys.exc_info() return TrafficControlRc( code=ReturnCode.UNKNOWN_FW_ERROR, message=str(exc_info)) return TrafficControlRc(code=ReturnCode.OK) def _set_filter(self, mark, eth, shaping): """Given a mark, interface and shaping settings, create a TC filter. Args: mark: The mark based on which we create the filter. eth: The interface on which we create the filter. shaping: The shaping associated to this interface. Returns: A TrafficControlRc containing information on success/failure. """ ifid = eth['id'] idx = 0x10000 + mark parent = 0x10000 self.logger.info( "create new FW filter on IFID {0}, classid {1}," " handle {2:X}, rate: {3}kbits".format( eth['name'], int_to_classid(idx), mark, shaping.rate ) ) try: self.ipr.tc(RTM_NEWTFILTER, 'fw', ifid, mark, parent=parent, protocol=ETH_P_IP, prio=PRIO, classid=idx, rate="{}kbit".format(shaping.rate or 2**22 - 1), burst=self.burst_size, action='drop') except NetlinkError as e: return TrafficControlRc( code=ReturnCode.NETLINK_FW_ERROR, message=str(e)) except Exception as e: self.logger.exception('_set_filter') exc_info = sys.exc_info() return TrafficControlRc( code=ReturnCode.UNKNOWN_FW_ERROR, message=str(exc_info)) return TrafficControlRc(code=ReturnCode.OK) def _unset_iptables(self, mark, eth, ip, options=None): """Given a mark, interface, IP and options, clear iptables rules. Args: mark: The mark to delete. eth: The interface on which to delete the mark. ip: The IP address to shape. options: An array of iptables options for more specific filtering. Returns: A TrafficControlRc containing information on success/failure. """ if options is None or len(options) == 0: options = [''] for opt in options: cmd = "{0} -t mangle -D FORWARD {1} {2} -i {3} {option} " \ "-j MARK --set-mark {4}".format( self.iptables, "-s" if eth['name'] == self.lan['name'] else "-d", ip, eth['name'], mark, option=opt) self.run_cmd(cmd) def _set_iptables(self, mark, eth, ip, options=None): """Given a mark, interface, IP and options, create iptables rules. Those rules will mark packets which will be filtered by TC filter and put in the right shaping bucket. Args: mark: The mark to delete. eth: The interface on which to delete the mark. ip: The IP address to shape. options: An array of iptables options for more specific filtering. Returns: A TrafficControlRc containing information on success/failure. """ if options is None or len(options) == 0: options = [''] for opt in options: cmd = "{0} -t mangle -A FORWARD {1} {2} -i {3} {option} " \ "-j MARK --set-mark {4}".format( self.iptables, "-s" if eth['name'] == self.lan['name'] else "-d", ip, eth['name'], mark, option=opt) self.run_cmd(cmd) def _shape_interface(self, mark, eth, ip, shaping): """Shape the traffic for a given interface. Shape the traffic for a given IP on a given interface, given the mark and the shaping settings. There is a few steps to shape the traffic of an IP: 1. Create an HTB class that limit the throughput. 2. Create a NetEm QDisc that adds corruption, loss, reordering, loss and delay. 3. Create the TC filter that will bucket packets with a given mark in the right HTB class. 4. Set an iptables rule that mark packets going to/coming from IP Args: mark: The mark to set on IP packets. eth: The network interface. ip: The IP to shape traffic for. shaping: The shaping setting to set. Returns: A TrafficControlRc containing information on success/failure. """ self.logger.info( "Shaping ip {0} on interface {1}".format(ip, eth['name'])) # HTB class tcrc = self._set_htb_class(mark, eth, shaping) if tcrc.code != ReturnCode.OK: self.logger.error( "adding HTB class on IFID {0}, mark {1}, err: {2}".format( eth['name'], mark, tcrc.message)) return tcrc # NetemQdisc tcrc = self._set_netem_qdisc(mark, eth, shaping) if tcrc.code != ReturnCode.OK: self.logger.error( "adding NetEm qdisc on IFID {0}, mark {1}, err: {2}".format( eth['name'], mark, tcrc.message)) # delete class self._unset_htb_class(mark, eth) return tcrc # filter tcrc = self._set_filter(mark, eth, shaping) if tcrc.code != ReturnCode.OK: self.logger.error( "adding filter FW on IFID {0}, mark {1}, err: {2}".format( eth['name'], mark, tcrc.message)) # delete class self._unset_htb_class(mark, eth) return tcrc # iptables self._set_iptables(mark, eth, ip, shaping.iptables_options) return TrafficControlRc(code=ReturnCode.OK) def _unshape_interface(self, mark, eth, ip, settings): """Unshape the traffic for a given interface. Unshape the traffic for a given IP on a given interface, given the mark and the shaping settings. There is a few steps to unshape the traffic of an IP: 1. Remove the iptables rule. 2. Remove the TC filter. 3. Remove the HTB class. Args: mark: The mark to set on IP packets. eth: The network interface. ip: The IP to shape traffic for. shaping: The shaping setting to set. Returns: A TrafficControlRc containing information on success/failure. """ self.logger.info( "Unshaping ip {0} on interface {1}".format(ip, eth['name'])) # iptables self._unset_iptables(mark, eth, ip, settings.iptables_options) # filter tcrc = self._unset_filter(mark, eth) if tcrc.code != ReturnCode.OK: self.logger.error( "deleting FW filter on IFID {0}, mark {1}, err: {2}".format( eth['name'], mark, tcrc.message) ) return tcrc # HTB class tcrc = self._unset_htb_class(mark, eth) if tcrc.code != ReturnCode.OK: self.logger.error( "deleting HTB class on IFID {0}, mark {1}, err: {2}".format( eth['name'], mark, tcrc.message) ) return tcrc return TrafficControlRc(code=ReturnCode.OK)
class TestNmapConfigRoot(unittest.TestCase): '''Tests Nmap Configuration by creating a fake interface and confirming scan results''' def setUp(self): self._iproute = IPRoute() self._iproute.link('add', ifname='lan127', kind='dummy') self._iproute.link('add', ifname='monitor234', kind='dummy') self._iproute.link('add', ifname='lan322', kind='dummy') self._dummy0_idx = self._iproute.link_lookup(ifname='lan127')[0] self._dummy1_idx = self._iproute.link_lookup(ifname='monitor234')[0] self._dummy2_idx = self._iproute.link_lookup(ifname='lan322')[0] fd, self._scratch_config = tempfile.mkstemp() os.close(fd) # Don't need to write anything to it # We need to write out a configuration file so NmapConfig can read it netcfg = self.configure_interfaces(self._scratch_config) netcfg.export_configuration() def tearDown(self): # Remove our dummy interfaces self._iproute.link('remove', index=self._dummy0_idx) self._iproute.link('remove', index=self._dummy1_idx) self._iproute.link('remove', index=self._dummy2_idx) self._iproute.close() os.remove(self._scratch_config) # More or less copied and pasted from test_netcfg.py def configure_interfaces(self, config_file=None): '''Sets up interfaces for most tests''' nc = ndr_netcfg.NetworkConfiguration(config_file) nc.set_configuration_method( "lan127", ndr_netcfg.InterfaceConfigurationMethods.STATIC) nc.add_static_addr("lan127", "10.1.177.2", 24) nc.set_configuration_method( "monitor234", ndr_netcfg.InterfaceConfigurationMethods.STATIC) nc.add_static_addr("monitor234", "10.2.177.2", 24) # Create an IPv6 enabled interface nc.set_configuration_method( "lan322", ndr_netcfg.InterfaceConfigurationMethods.STATIC) nc.add_static_addr("lan322", "192.168.17.2", 28) nc.add_static_addr("lan322", "fdd1:2013:2f69:388f::122", 64) nc.apply_configuration() return nc def test_scan_interfaces(self): '''Tests that we can properly only find the LAN interfaces any nothing else''' nmap_cfg = ndr.NmapConfig(self._scratch_config) self.assertIn("lan127", nmap_cfg.scan_interfaces) self.assertIn("lan322", nmap_cfg.scan_interfaces) self.assertNotIn("monitor234", nmap_cfg.scan_interfaces) def test_networks_to_scan(self): '''Tests that we determine the right CIDRs to scan''' nmap_cfg = ndr.NmapConfig(self._scratch_config) self.assertIn(ipaddress.ip_network("10.1.177.0/24"), nmap_cfg.networks_to_scan) self.assertIn(ipaddress.ip_network("192.168.17.0/28"), nmap_cfg.networks_to_scan) self.assertIn(ipaddress.ip_network("fdd1:2013:2f69:388f::/64"), nmap_cfg.networks_to_scan) self.assertNotIn(ipaddress.ip_network("10.2.177.0/24"), nmap_cfg.networks_to_scan)
class TestNSPopen(object): def setup(self): self.ip = IPRoute() self.names = [] def teardown(self): self.ip.close() for ns in self.names: netnsmod.remove(ns) def alloc_nsname(self): nsid = str(uuid4()) self.names.append(nsid) return nsid def test_stdio(self): require_user('root') nsid = self.alloc_nsname() nsp = NSPopen(nsid, ['ip', 'ad'], flags=os.O_CREAT, stdout=subprocess.PIPE) output = nsp.stdout.read() nsp.release() assert output is not None def test_fcntl(self): require_user('root') nsid = self.alloc_nsname() nsp = NSPopen(nsid, ['ip', 'ad'], flags=os.O_CREAT, stdout=subprocess.PIPE) flags = nsp.stdout.fcntl(fcntl.F_GETFL) nsp.release() assert flags == 0 def test_api_class(self): api_nspopen = set(dir(NSPopenDirect)) api_popen = set(dir(subprocess.Popen)) assert api_nspopen & api_popen == api_popen def test_api_object(self): require_user('root') nsid = self.alloc_nsname() nsp = NSPopen(nsid, ['true'], flags=os.O_CREAT, stdout=subprocess.PIPE) smp = subprocess.Popen(['true'], stdout=subprocess.PIPE) nsp.communicate() smp.communicate() api_nspopen = set(dir(nsp)) api_popen = set(dir(smp)) minimal = set(('communicate', 'kill', 'wait')) assert minimal & (api_nspopen & api_popen) == minimal smp.wait() nsp.wait() assert nsp.returncode == smp.returncode == 0 nsp.release() def test_release(self): require_user('root') nsid = self.alloc_nsname() nsp = NSPopen(nsid, ['true'], flags=os.O_CREAT, stdout=subprocess.PIPE) nsp.communicate() nsp.wait() nsp.release() try: print(nsp.returncode) except RuntimeError: pass def test_basic(self): require_user('root') nsid = self.alloc_nsname() # create NS and run a child nsp = NSPopen(nsid, ['ip', '-o', 'link'], stdout=subprocess.PIPE, flags=os.O_CREAT) ret = nsp.communicate()[0].decode('utf-8') host_links = [x.get_attr('IFLA_IFNAME') for x in self.ip.get_links()] netns_links = [ x.split(':')[1].split('@')[0].strip() for x in ret.split('\n') if len(x) ] assert nsp.wait() == nsp.returncode == 0 assert set(host_links) & set(netns_links) == set(netns_links) assert set(netns_links) < set(host_links) assert not set(netns_links) > set(host_links) nsp.release()
def test_multiple_instances(self): ip1 = IPRoute() ip2 = IPRoute() ip1.close() ip2.close()
class NetworkBase(Base): def __init__(self): Base.__init__(self) self.ipr = IPRoute() self.nodes = [] def add_node(self, node): assert isinstance(node, Node) self.nodes.append(node) def get_interface_name(self, source, dest): assert isinstance(source, Node) assert isinstance(dest, Node) interface_name = "veth-" + source.name + "-" + dest.name return interface_name def get_interface(self, ifname): interfaces = self.ipr.link_lookup(ifname=ifname) if len(interfaces) != 1: raise Exception("Could not identify interface " + ifname) ix = interfaces[0] assert isinstance(ix, int) return ix def set_interface_ipaddress(self, node, ifname, address, mask): # Ask a node to set the specified interface address if address is None: return assert isinstance(node, Node) command = ["ip", "addr", "add", str(address) + "/" + str(mask), "dev", str(ifname)] result = node.execute(command) assert(result == 0) def create_link(self, src, dest): assert isinstance(src, Endpoint) assert isinstance(dest, Endpoint) ifname = self.get_interface_name(src.parent, dest.parent) destname = self.get_interface_name(dest.parent, src.parent) self.ipr.link_create(ifname=ifname, kind="veth", peer=destname) self.message("Create", ifname, "link") # Set source endpoint information ix = self.get_interface(ifname) self.ipr.link("set", index=ix, address=src.mac_addr) # push source endpoint into source namespace self.ipr.link("set", index=ix, net_ns_fd=src.parent.get_ns_name(), state="up") # Set interface ip address; seems to be # lost of set prior to moving to namespace self.set_interface_ipaddress( src.parent, ifname, src.ipaddress , src.prefixlen) # Sef destination endpoint information ix = self.get_interface(destname) self.ipr.link("set", index=ix, address=dest.mac_addr) # push destination endpoint into the destination namespace self.ipr.link("set", index=ix, net_ns_fd=dest.parent.get_ns_name(), state="up") # Set interface ip address self.set_interface_ipaddress(dest.parent, destname, dest.ipaddress, dest.prefixlen) def show_interfaces(self, node): cmd = ["ip", "addr"] if node is None: # Run with no namespace subprocess.call(cmd) else: # Run in node's namespace assert isinstance(node, Node) self.message("Enumerating all interfaces in ", node.name) node.execute(cmd) def delete(self): self.message("Deleting virtual network") for n in self.nodes: n.remove() self.ipr.close()
class TestIPRoute(object): def setup(self): self.ip = IPRoute() try: self.ifaces = [] self.dev, idx = self.create() except IndexError: pass def create(self, kind='dummy'): name = uifname() create_link(name, kind=kind) idx = self.ip.link_lookup(ifname=name)[0] self.ifaces.append(idx) return (name, idx) def teardown(self): if hasattr(self, 'ifaces'): for dev in self.ifaces: try: self.ip.link('delete', index=dev) except: pass self.ip.close() def _test_nla_operators(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) r = [x for x in self.ip.get_addr() if x['index'] == self.ifaces[0]] complement = r[0] - r[1] intersection = r[0] & r[1] assert complement.get_attr('IFA_ADDRESS') == '172.16.0.1' assert complement.get_attr('IFA_LABEL') is None assert complement['prefixlen'] == 0 assert complement['index'] == 0 assert intersection.get_attr('IFA_ADDRESS') is None assert intersection.get_attr('IFA_LABEL') == self.dev assert intersection['prefixlen'] == 24 assert intersection['index'] == self.ifaces[0] def test_addr_add(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) assert '172.16.0.1/24' in get_ip_addr() def test_vlan_filter_dump(self): require_user('root') (an, ax) = self.create('bridge') (bn, bx) = self.create('bridge') self.ip.link('set', index=ax, state='up') self.ip.link('set', index=bx, state='up') assert len(self.ip.get_vlans()) >= 2 for name in (an, bn): assert len(self.ip.get_vlans(ifname=name)) == 1 assert (self .ip .get_vlans(ifname=name)[0] .get_attr('IFLA_IFNAME')) == name assert (self .ip .get_vlans(ifname=name)[0] .get_nested('IFLA_AF_SPEC', 'IFLA_BRIDGE_VLAN_INFO'))['vid'] == 1 def test_vlan_filter_add(self): require_user('root') (bn, bx) = self.create('bridge') (sn, sx) = self.create('dummy') self.ip.link('set', index=sx, master=bx) assert not grep('bridge vlan show', pattern='567') self.ip.vlan_filter('add', index=sx, vlan_info={'vid': 567}) assert grep('bridge vlan show', pattern='567') self.ip.vlan_filter('del', index=sx, vlan_info={'vid': 567}) assert not grep('bridge vlan show', pattern='567') def test_vlan_filter_add_raw(self): require_user('root') (bn, bx) = self.create('bridge') (sn, sx) = self.create('dummy') self.ip.link('set', index=sx, master=bx) assert not grep('bridge vlan show', pattern='567') self.ip.vlan_filter('add', index=sx, af_spec={'attrs': [['IFLA_BRIDGE_VLAN_INFO', {'vid': 567}]]}) assert grep('bridge vlan show', pattern='567') self.ip.vlan_filter('del', index=sx, af_spec={'attrs': [['IFLA_BRIDGE_VLAN_INFO', {'vid': 567}]]}) assert not grep('bridge vlan show', pattern='567') def test_local_add(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', local='172.16.0.1', mask=24) link = self.ip.get_addr(index=self.ifaces[0])[0] address = link.get_attr('IFA_ADDRESS') local = link.get_attr('IFA_LOCAL') assert address == '172.16.0.2' assert local == '172.16.0.1' def test_addr_broadcast(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24, broadcast='172.16.0.250') assert '172.16.0.250' in get_ip_brd() def test_addr_broadcast_default(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24, broadcast=True) assert '172.16.0.255' in get_ip_brd() def test_flush_addr(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.1.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.1.2', mask=24) assert len(self.ip.get_addr(index=self.ifaces[0], family=socket.AF_INET)) == 4 self.ip.flush_addr(index=self.ifaces[0]) assert len(self.ip.get_addr(index=self.ifaces[0], family=socket.AF_INET)) == 0 def test_flush_rules(self): require_user('root') init = len(self.ip.get_rules(family=socket.AF_INET)) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 self.ip.rule('add', table=10, priority=110) self.ip.rule('add', table=15, priority=150, action='FR_ACT_PROHIBIT') self.ip.rule('add', table=20, priority=200, src='172.16.200.1') self.ip.rule('add', table=25, priority=250, dst='172.16.250.1') assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 4 assert len(self.ip.get_rules(src='172.16.200.1')) == 1 assert len(self.ip.get_rules(dst='172.16.250.1')) == 1 self.ip.flush_rules(family=socket.AF_INET, priority=lambda x: 100 < x < 500) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 assert len(self.ip.get_rules(src='172.16.200.1')) == 0 assert len(self.ip.get_rules(dst='172.16.250.1')) == 0 assert len(self.ip.get_rules(family=socket.AF_INET)) == init def test_rules_deprecated(self): require_user('root') init = len(self.ip.get_rules(family=socket.AF_INET)) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 self.ip.rule('add', 10, 110) self.ip.rule('add', 15, 150, 'FR_ACT_PROHIBIT') assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 2 self.ip.flush_rules(family=socket.AF_INET, priority=lambda x: 100 < x < 500) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 assert len(self.ip.get_rules(family=socket.AF_INET)) == init def test_addr_filter(self): require_user('root') self.ip.addr('add', index=self.ifaces[0], address='172.16.0.1', prefixlen=24, broadcast='172.16.0.255') self.ip.addr('add', index=self.ifaces[0], address='172.16.0.2', prefixlen=24, broadcast='172.16.0.255') assert len(self.ip.get_addr(index=self.ifaces[0])) == 2 assert len(self.ip.get_addr(address='172.16.0.1')) == 1 assert len(self.ip.get_addr(broadcast='172.16.0.255')) == 2 assert len(self.ip.get_addr(match=lambda x: x['index'] == self.ifaces[0])) == 2 @skip_if_not_supported def _create_ipvlan(self, smode): master = uifname() ipvlan = uifname() # create the master link self.ip.link('add', ifname=master, kind='dummy') midx = self.ip.link_lookup(ifname=master)[0] # check modes # maybe move modes dict somewhere else? cmode = ifinfmsg.ifinfo.ipvlan_data.modes[smode] assert ifinfmsg.ifinfo.ipvlan_data.modes[cmode] == smode # create ipvlan self.ip.link('add', ifname=ipvlan, kind='ipvlan', link=midx, mode=cmode) devs = self.ip.link_lookup(ifname=ipvlan) assert devs self.ifaces.extend(devs) def test_create_ipvlan_l2(self): return self._create_ipvlan('IPVLAN_MODE_L2') def test_create_ipvlan_l3(self): return self._create_ipvlan('IPVLAN_MODE_L3') @skip_if_not_supported def _create(self, kind, **kwarg): name = uifname() self.ip.link('add', ifname=name, kind=kind, **kwarg) devs = self.ip.link_lookup(ifname=name) assert devs self.ifaces.extend(devs) return (name, devs[0]) def test_create_dummy(self): require_user('root') self._create('dummy') def test_create_bond(self): require_user('root') self._create('bond') def test_create_bridge(self): require_user('root') self._create('bridge') def test_create_team(self): require_user('root') self._create('team') def test_ntables(self): setA = set(filter(lambda x: x is not None, [x.get_attr('NDTA_PARMS').get_attr('NDTPA_IFINDEX') for x in self.ip.get_ntables()])) setB = set([x['index'] for x in self.ip.get_links()]) assert setA == setB def test_fdb_vxlan(self): require_kernel(4, 4) require_user('root') # create dummy (dn, dx) = self._create('dummy') # create vxlan on it (vn, vx) = self._create('vxlan', vxlan_link=dx, vxlan_id=500) # create FDB record l2 = '00:11:22:33:44:55' self.ip.fdb('add', lladdr=l2, ifindex=vx, vni=600, port=5678, dst='172.16.40.40') # dump r = self.ip.fdb('dump', ifindex=vx, lladdr=l2) assert len(r) == 1 assert r[0]['ifindex'] == vx assert r[0].get_attr('NDA_LLADDR') == l2 assert r[0].get_attr('NDA_DST') == '172.16.40.40' assert r[0].get_attr('NDA_PORT') == 5678 assert r[0].get_attr('NDA_VNI') == 600 def test_fdb_bridge_simple(self): require_kernel(4, 4) require_user('root') # create bridge (bn, bx) = self._create('bridge') # create FDB record l2 = '00:11:22:33:44:55' self.ip.fdb('add', lladdr=l2, ifindex=bx) # dump FDB r = self.ip.fdb('dump', ifindex=bx, lladdr=l2) # one vlan == 1, one w/o vlan assert len(r) == 2 assert len(list(filter(lambda x: x['ifindex'] == bx, r))) == 2 assert len(list(filter(lambda x: x.get_attr('NDA_VLAN'), r))) == 1 assert len(list(filter(lambda x: x.get_attr('NDA_MASTER') == bx, r))) == 2 assert len(list(filter(lambda x: x.get_attr('NDA_LLADDR') == l2, r))) == 2 r = self.ip.fdb('dump', ifindex=bx, lladdr=l2, vlan=1) assert len(r) == 1 assert r[0].get_attr('NDA_VLAN') == 1 assert r[0].get_attr('NDA_MASTER') == bx assert r[0].get_attr('NDA_LLADDR') == l2 def test_neigh_real_links(self): links = set([x['index'] for x in self.ip.get_links()]) neigh = set([x['ifindex'] for x in self.ip.get_neighbours()]) assert neigh < links def test_neigh_filter(self): require_user('root') # inject arp records self.ip.neigh('add', dst='172.16.45.1', lladdr='00:11:22:33:44:55', ifindex=self.ifaces[0]) self.ip.neigh('add', dst='172.16.45.2', lladdr='00:11:22:33:44:55', ifindex=self.ifaces[0]) # assert two arp records on the interface assert len(self.ip.get_neighbours(ifindex=self.ifaces[0])) == 2 # filter by dst assert len(self.ip.get_neighbours(dst='172.16.45.1')) == 1 # filter with lambda assert len(self.ip.get_neighbours(match=lambda x: x['ifindex'] == self.ifaces[0])) == 2 def test_mass_ipv6(self): # # Achtung! This test is time consuming. # It is really time consuming, I'm not not # kidding you. Beware. # require_user('root') base = 'fdb3:84e5:4ff4:55e4::{0}' limit = int(os.environ.get('PYROUTE2_SLIMIT', '0x800'), 16) # add addresses for idx in range(limit): self.ip.addr('add', self.ifaces[0], base.format(hex(idx)[2:]), 48) # assert addresses in two steps, to ease debug addrs = self.ip.get_addr(10) assert len(addrs) >= limit # clean up addresses # # it is not required, but if you don't do that, # you'll get this on the interface removal: # # >> kernel:BUG: soft lockup - CPU#0 stuck for ... # # so, not to scare people, remove addresses gracefully # one by one # # it also verifies all the addresses are in place for idx in reversed(range(limit)): self.ip.addr('delete', self.ifaces[0], base.format(hex(idx)[2:]), 48) def test_fail_not_permitted(self): try: self.ip.addr('add', 1, address='172.16.0.1', mask=24) except NetlinkError as e: if e.code != errno.EPERM: # Operation not permitted raise finally: try: self.ip.addr('delete', 1, address='172.16.0.1', mask=24) except: pass def test_fail_no_such_device(self): require_user('root') dev = sorted([i['index'] for i in self.ip.get_links()])[-1] + 10 try: self.ip.addr('add', dev, address='172.16.0.1', mask=24) except NetlinkError as e: if e.code != errno.ENODEV: # No such device raise def test_remove_link(self): require_user('root') try: self.ip.link('del', index=self.ifaces[0]) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 0 def _test_route_proto(self, proto, fake, spec=''): require_user('root') os.system('ip route add 172.16.3.0/24 via 127.0.0.1 %s' % spec) time.sleep(1) assert grep('ip ro', pattern='172.16.3.0/24.*127.0.0.1') try: self.ip.route('del', dst='172.16.3.0/24', gateway='127.0.0.1', proto=fake) except NetlinkError: pass self.ip.route('del', dst='172.16.3.0/24', gateway='127.0.0.1', proto=proto) assert not grep('ip ro', pattern='172.16.3.0/24.*127.0.0.1') def test_route_proto_static(self): return self._test_route_proto('static', 'boot', 'proto static') def test_route_proto_static_num(self): return self._test_route_proto(4, 3, 'proto static') def test_route_proto_boot(self): return self._test_route_proto('boot', 4) def test_route_proto_boot_num(self): return self._test_route_proto(3, 'static') def test_route_oif_as_iterable(self): require_user('root') spec = {'dst': '172.16.0.0', 'dst_len': 24, 'oif': (1, )} self.ip.route('add', **spec) rts = self.ip.get_routes(family=socket.AF_INET, dst='172.16.0.0') self.ip.route('del', **spec) assert len(rts) == 1 assert rts[0].get_attr('RTA_OIF') == 1 def test_route_get_target(self): if not self.ip.get_default_routes(table=254): raise SkipTest('no default IPv4 routes') rts = self.ip.get_routes(family=socket.AF_INET, dst='8.8.8.8', table=254) assert len(rts) > 0 def test_route_get_target_default_ipv4(self): rts = self.ip.get_routes(dst='127.0.0.1') assert len(rts) > 0 def test_route_get_target_default_ipv6(self): rts = self.ip.get_routes(dst='::1') assert len(rts) > 0 def test_route_get_by_spec(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', index=self.ifaces[0], address='172.16.60.1', mask=24) self.ip.addr('add', index=self.ifaces[0], address='172.16.61.1', mask=24) rts = self.ip.get_routes(family=socket.AF_INET, dst=lambda x: x in ('172.16.60.0', '172.16.61.0')) assert len(rts) == 4 @skip_if_not_supported def _test_route_mpls_via_ipv(self, family, address, label): require_kernel(4, 4) require_user('root') self.ip.route('add', **{'family': AF_MPLS, 'oif': self.ifaces[0], 'via': {'family': family, 'addr': address}, 'newdst': {'label': label, 'bos': 1}}) rt = self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)[0] assert rt.get_attr('RTA_VIA')['addr'] == address assert rt.get_attr('RTA_VIA')['family'] == family assert rt.get_attr('RTA_NEWDST')[0]['label'] == label assert len(rt.get_attr('RTA_NEWDST')) == 1 self.ip.route('del', **{'family': AF_MPLS, 'oif': self.ifaces[0], 'dst': {'label': 0x10, 'bos': 1}, 'via': {'family': family, 'addr': address}, 'newdst': {'label': label, 'bos': 1}}) assert len(self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)) == 0 def test_route_mpls_via_ipv4(self): self._test_route_mpls_via_ipv(socket.AF_INET, '172.16.0.1', 0x20) def test_route_mpls_via_ipv6(self): self._test_route_mpls_via_ipv(socket.AF_INET6, 'fe80::5054:ff:fe4b:7c32', 0x20) @skip_if_not_supported def test_route_mpls_swap_newdst_simple(self): require_kernel(4, 4) require_user('root') req = {'family': AF_MPLS, 'oif': self.ifaces[0], 'dst': {'label': 0x20, 'bos': 1}, 'newdst': {'label': 0x21, 'bos': 1}} self.ip.route('add', **req) rt = self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)[0] assert rt.get_attr('RTA_DST')[0]['label'] == 0x20 assert len(rt.get_attr('RTA_DST')) == 1 assert rt.get_attr('RTA_NEWDST')[0]['label'] == 0x21 assert len(rt.get_attr('RTA_NEWDST')) == 1 self.ip.route('del', **req) assert len(self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)) == 0 @skip_if_not_supported def test_route_mpls_swap_newdst_list(self): require_kernel(4, 4) require_user('root') req = {'family': AF_MPLS, 'oif': self.ifaces[0], 'dst': {'label': 0x20, 'bos': 1}, 'newdst': [{'label': 0x21, 'bos': 1}]} self.ip.route('add', **req) rt = self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)[0] assert rt.get_attr('RTA_DST')[0]['label'] == 0x20 assert len(rt.get_attr('RTA_DST')) == 1 assert rt.get_attr('RTA_NEWDST')[0]['label'] == 0x21 assert len(rt.get_attr('RTA_NEWDST')) == 1 self.ip.route('del', **req) assert len(self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)) == 0 def test_route_multipath_raw(self): require_user('root') self.ip.route('add', dst='172.16.241.0', mask=24, multipath=[{'hops': 20, 'oif': 1, 'attrs': [['RTA_GATEWAY', '127.0.0.2']]}, {'hops': 30, 'oif': 1, 'attrs': [['RTA_GATEWAY', '127.0.0.3']]}]) assert grep('ip route show', pattern='172.16.241.0/24') assert grep('ip route show', pattern='nexthop.*127.0.0.2.*weight 21') assert grep('ip route show', pattern='nexthop.*127.0.0.3.*weight 31') self.ip.route('del', dst='172.16.241.0', mask=24) def test_route_multipath_helper(self): require_user('root') req = IPRouteRequest({'dst': '172.16.242.0/24', 'multipath': [{'hops': 20, 'oif': 1, 'gateway': '127.0.0.2'}, {'hops': 30, 'oif': 1, 'gateway': '127.0.0.3'}]}) self.ip.route('add', **req) assert grep('ip route show', pattern='172.16.242.0/24') assert grep('ip route show', pattern='nexthop.*127.0.0.2.*weight 21') assert grep('ip route show', pattern='nexthop.*127.0.0.3.*weight 31') self.ip.route('del', dst='172.16.242.0', mask=24) def test_route_multipath(self): require_user('root') self.ip.route('add', dst='172.16.243.0/24', multipath=[{'gateway': '127.0.0.2'}, {'gateway': '127.0.0.3'}]) assert grep('ip route show', pattern='172.16.243.0/24') assert grep('ip route show', pattern='nexthop.*127.0.0.2') assert grep('ip route show', pattern='nexthop.*127.0.0.3') self.ip.route('del', dst='172.16.243.0', mask=24) @skip_if_not_supported def test_lwtunnel_multipath_mpls(self): require_kernel(4, 4) require_user('root') self.ip.route('add', dst='172.16.216.0/24', multipath=[{'encap': {'type': 'mpls', 'labels': 500}, 'oif': 1}, {'encap': {'type': 'mpls', 'labels': '600/700'}, 'gateway': '127.0.0.4'}]) routes = self.ip.route('dump', dst='172.16.216.0/24') assert len(routes) == 1 mp = routes[0].get_attr('RTA_MULTIPATH') assert len(mp) == 2 assert mp[0]['oif'] == 1 assert mp[0].get_attr('RTA_ENCAP_TYPE') == 1 labels = mp[0].get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 1 assert labels[0]['bos'] == 1 assert labels[0]['label'] == 500 assert mp[1].get_attr('RTA_ENCAP_TYPE') == 1 labels = mp[1].get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 2 assert labels[0]['bos'] == 0 assert labels[0]['label'] == 600 assert labels[1]['bos'] == 1 assert labels[1]['label'] == 700 self.ip.route('del', dst='172.16.216.0/24') @skip_if_not_supported def test_lwtunnel_mpls_dict_label(self): require_kernel(4, 4) require_user('root') self.ip.route('add', dst='172.16.226.0/24', encap={'type': 'mpls', 'labels': [{'bos': 0, 'label': 226}, {'bos': 1, 'label': 227}]}, gateway='127.0.0.2') routes = self.ip.route('dump', dst='172.16.226.0/24') assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_GATEWAY') == '127.0.0.2' labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 2 assert labels[0]['bos'] == 0 assert labels[0]['label'] == 226 assert labels[1]['bos'] == 1 assert labels[1]['label'] == 227 self.ip.route('del', dst='172.16.226.0/24') @skip_if_not_supported def test_lwtunnel_mpls_2_int_label(self): require_kernel(4, 4) require_user('root') self.ip.route('add', dst='172.16.206.0/24', encap={'type': 'mpls', 'labels': [206, 207]}, oif=1) routes = self.ip.route('dump', dst='172.16.206.0/24') assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_OIF') == 1 labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 2 assert labels[0]['bos'] == 0 assert labels[0]['label'] == 206 assert labels[1]['bos'] == 1 assert labels[1]['label'] == 207 self.ip.route('del', dst='172.16.206.0/24') @skip_if_not_supported def test_lwtunnel_mpls_2_str_label(self): require_kernel(4, 4) require_user('root') self.ip.route('add', dst='172.16.246.0/24', encap={'type': 'mpls', 'labels': "246/247"}, oif=1) routes = self.ip.route('dump', dst='172.16.246.0/24') assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_OIF') == 1 labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 2 assert labels[0]['bos'] == 0 assert labels[0]['label'] == 246 assert labels[1]['bos'] == 1 assert labels[1]['label'] == 247 self.ip.route('del', dst='172.16.246.0/24') @skip_if_not_supported def test_lwtunnel_mpls_1_str_label(self): require_kernel(4, 4) require_user('root') self.ip.route('add', dst='172.16.244.0/24', encap={'type': 'mpls', 'labels': "244"}, oif=1) routes = self.ip.route('dump', dst='172.16.244.0/24') assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_OIF') == 1 labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 1 assert labels[0]['bos'] == 1 assert labels[0]['label'] == 244 self.ip.route('del', dst='172.16.244.0/24') @skip_if_not_supported def test_lwtunnel_mpls_1_int_label(self): require_kernel(4, 4) require_user('root') self.ip.route('add', dst='172.16.245.0/24', encap={'type': 'mpls', 'labels': 245}, oif=1) routes = self.ip.route('dump', dst='172.16.245.0/24') assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_OIF') == 1 labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 1 assert labels[0]['bos'] == 1 assert labels[0]['label'] == 245 self.ip.route('del', dst='172.16.245.0/24') def test_route_change_existing(self): # route('replace', ...) should succeed, if route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) self.ip.route('add', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.route('change', dst='172.16.1.0', mask=24, gateway='172.16.0.2', table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') def test_route_change_not_existing_fail(self): # route('change', ...) should fail, if no route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') try: self.ip.route('change', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) except NetlinkError as e: if e.code != errno.ENOENT: raise def test_route_replace_existing(self): # route('replace', ...) should succeed, if route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) self.ip.route('replace', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.route('replace', dst='172.16.1.0', mask=24, gateway='172.16.0.2', table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') def test_route_replace_not_existing(self): # route('replace', ...) should succeed, if route doesn't exist require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('replace', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') def test_flush_routes(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('add', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) self.ip.route('add', dst='172.16.2.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.2.0/24.*172.16.0.1') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert not grep('ip route show table 100', pattern='172.16.2.0/24.*172.16.0.1') def test_route_table_2048(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('add', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=2048) assert grep('ip route show table 2048', pattern='172.16.1.0/24.*172.16.0.1') remove_link('bala') def test_symbolic_flags_ifaddrmsg(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], '172.16.1.1', 24) addr = [x for x in self.ip.get_addr() if x.get_attr('IFA_LOCAL') == '172.16.1.1'][0] assert 'IFA_F_PERMANENT' in addr.flags2names(addr['flags']) def test_symbolic_flags_ifinfmsg(self): require_user('root') self.ip.link('set', index=self.ifaces[0], flags=['IFF_UP']) iface = self.ip.get_links(self.ifaces[0])[0] assert iface['flags'] & 1 assert 'IFF_UP' in iface.flags2names(iface['flags']) self.ip.link('set', index=self.ifaces[0], flags=['!IFF_UP']) assert not (self.ip.get_links(self.ifaces[0])[0]['flags'] & 1) def test_updown_link(self): require_user('root') try: self.ip.link_up(*self.ifaces) except NetlinkError: pass assert self.ip.get_links(*self.ifaces)[0]['flags'] & 1 try: self.ip.link_down(*self.ifaces) except NetlinkError: pass assert not (self.ip.get_links(*self.ifaces)[0]['flags'] & 1) def test_callbacks_positive(self): require_user('root') dev = self.ifaces[0] self.cb_counter = 0 self.ip.register_callback(_callback, lambda x: x.get('index', None) == dev, (self, )) self.test_updown_link() assert self.cb_counter > 0 self.ip.unregister_callback(_callback) def test_callbacks_negative(self): require_user('root') self.cb_counter = 0 self.ip.register_callback(_callback, lambda x: x.get('index', None) == -1, (self, )) self.test_updown_link() assert self.cb_counter == 0 self.ip.unregister_callback(_callback) def test_link_filter(self): l = self.ip.link('dump', ifname='lo') assert len(l) == 1 assert l[0].get_attr('IFLA_IFNAME') == 'lo' def test_link_rename(self): require_user('root') dev = self.ifaces[0] try: self.ip.link_rename(dev, 'bala') except NetlinkError: pass assert len(self.ip.link_lookup(ifname='bala')) == 1 try: self.ip.link_rename(dev, self.dev) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 1 def test_rules(self): assert len(get_ip_rules('-4')) == \ len(self.ip.get_rules(socket.AF_INET)) assert len(get_ip_rules('-6')) == \ len(self.ip.get_rules(socket.AF_INET6)) def test_addr(self): assert len(get_ip_addr()) == len(self.ip.get_addr()) def test_links(self): assert len(get_ip_link()) == len(self.ip.get_links()) def test_one_link(self): lo = self.ip.get_links(1)[0] assert lo.get_attr('IFLA_IFNAME') == 'lo' def test_default_routes(self): assert len(get_ip_default_routes()) == \ len(self.ip.get_default_routes(family=socket.AF_INET, table=254)) def test_routes(self): assert len(get_ip_route()) == \ len(self.ip.get_routes(family=socket.AF_INET, table=255))
class Blinker(Thread): def __init__(self): Thread.__init__(self) self.ch = { 'A': '._', 'B': '_...', 'C': '_._.', 'D': '_..', 'E': '.', 'F': '.._.', 'G': '__.', 'H': '....', 'I': '..', 'J': '.___', 'K': '_._', 'L': '._..', 'M': '__', 'N': '_.', 'O': '___', 'P': '.__.', 'Q': '__._', 'R': '._.', 'S': '...', 'T': '_', 'U': '.._', 'V': '..._', 'W': '.__', 'X': '_.._', 'Y': '_.__', 'Z': '__..', '0': '_____', '1': '.____', '2': '..___', '3': '...__', '4': '...._', '5': '.....', '6': '_....', '7': '__...', '8': '___..', '9': '____.', } with open(LEDDEV, "w") as f: f.write("none") with warnings.catch_warnings(): warnings.simplefilter("ignore") self.activity = LED(47, active_high=False) self.activity.off() self.currentStatus = None self.lanConfig = {} self.ipr = IPRoute() self.ipAddr = None self.eth = None self.wlan = None self.curDev = None #parse ip address from dhcpcd.conf #these are expected static ip addresses #self.dev = 'wlan0' #self.devl = 'eth0' with open("/etc/wpa_supplicant/wpa_supplicant.conf", "r") as f: re1 = re.compile('ssid\s*=\s*"?(\w*?)"?$') re2 = re.compile('key_mgmt\s*=\s*"?([\w_\-]*?)"?$') txt = f.read() for line in txt.split('\n'): line = line.strip() if len(line) > 0 and line[0] != '#': rr1 = re1.search(line) rr2 = re2.search(line) if rr1: self.expectedWifi = rr1.group(1) print "WIFI config name: %s" % self.expectedWifi elif rr2: self.expectedWifiType = rr2.group(1) print "WIFI config type: %s" % self.expectedWifiType else: #print line pass with open("/etc/dhcpcd.conf", "r") as f: txt = f.read() re1 = re.compile('^interface\s+(.+)$') re2 = re.compile('^static\s+(\w+)\s*=\s*(.+)$') intf = '' for line in txt.split('\n'): if intf: #other interface mm1 = re1.search(line) if mm1: intf = mm1.group(1) self.lanConfig[intf] = {} if intf.startswith("wlan"): self.wlan = intf elif intf.startswith("eth"): self.eth = intf if len(line) > 7 and line[0] != '#': zz = re2.search(line) if zz: self.lanConfig[intf][zz.group(1)] = zz.group( 2).split('/') else: mm1 = re1.search(line) if mm1: intf = mm1.group(1) if intf.startswith("wlan"): self.wlan = intf elif intf.startswith("eth"): self.eth = intf self.lanConfig[intf] = {} #actual name server set to resolv.conf self.actualNameServer = None with open("/etc/resolv.conf", "r") as f: txt = f.read() re1 = re.compile('^nameserver\s+([\.\d]+)$') intf = '' for line in txt.split('\n'): r = re1.search(line) if r: self.actualNameserver = r.group(1) self.curDev = None self.terminate = Event() self.setCurrentStatus('OK') def getCurrentStatusOK(self): return self.currentStatus == 'OK' def setCurrentStatus(self, st): if self.currentStatus != st: if self.currentStatus != None: print "%s - status %s set" % (asctime(), st) self.currentStatus = st return (st == 'OK') def end(self): if self.terminate.wait(0): return print "Ctrl-C pressed - will exit in few seconds" self.activity.off() with open(LEDDEV, "w") as f: f.write("mmc0") self.terminate.set() self.ipr.close() self.ipr = None def checkStatus(self): #if eth is up, then use treat it as main i/f self.curDev = None self.ipAddr = None self.setCurrentStatus('OK') if self.eth and self.lanConfig[self.eth]: ret = self.ifconfig(self.eth) #see check eth i/f is up if ret: if self.lanConfig[self.eth]['ip_address'][0] == ret[0]: #got right ip address print "Got LAN ip address: %s" % ret[0] self.ipAddr = ret[0] self.curDev = self.eth self.setCurrentStatus('OK') else: print "wrong ip address for dev %s: %s" % (self.eth, ret[0]) self.setCurrentStatus('X') #if eth is not up and wlan is configured if self.wlan and self.lanConfig[self.wlan] and not self.ipAddr: #if LAN is not up, check wifi device status ret = self.iwconfig(self.wlan) #see if wifi is connected if not ret: return self.curDev = self.wlan #print "WIFI = OK (%s:%s)" % (self.wlan,self.currentWifi) ret = self.ifconfig(self.wlan) #see check wlan i/f is up if ret: self.curDev = self.wlan if self.lanConfig[self.wlan]['ip_address'][0] == ret[0]: print "Got WIFI ip address: %s" % ret[0] self.ipAddr = ret[0] self.setCurrentStatus('OK') else: print "wrong ip address for dev %s: %s" % (self.wlan, ret[0]) self.setCurrentStatus('X') if not self.ipAddr: print "both eth and wlan are down, status=%s" % self.currentStatus return #check IP conflict if not self.arp(): return if self.terminate.wait(0): return self.gw = self.lanConfig[self.curDev]['routers'][0] self.dns = self.lanConfig[self.curDev]['domain_name_servers'][0] self.setCurrentStatus('OK') ret = self.route(self.curDev) if not ret: return print "Got default route, %s" % self.gw if self.terminate.wait(0): return #print self.lanConfig[self.curDev] #ret = self.ping(self.actualNameServer) #ping dns #print ret ret = self.ping(self.gw) #ping g/w if not ret: print "G/W ping failed" self.setCurrentStatus('P') return self.setCurrentStatus('OK') #print "All OK" def runCmd(self, cmd, re1=None): p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True) ok = False for i in range(0, 5): if p.poll() is not None: ok = True out, err = p.communicate() break sleep(1) if not ok: p.kill() print "failed 1 %s" % cmd return None if not re1: return out if type(re1).__name__ == 'SRE_Pattern': re1 = [re1] for rrr in re1: for line in out.split("\n"): rr1 = rrr.search(line.strip()) #print line if rr1: return rr1 #print ">> " + line #print re1.pattern #print "failed 2 " + cmd return None def iwconfig(self, dev): cmd = "/sbin/iwconfig %s | grep SSID" % dev r = re.compile('SSID:"(.+?)"') ret = self.runCmd(cmd, r) self.currentWifi = ret.group(1) if ret else None print "WIFI connected: %s" % self.currentWifi if self.currentWifi is not None: return True return self.setCurrentStatus( 'W') # wifi is configured but not connected def arp(self): #check IP conflict for n in self.ipr.get_neighbours(): attrs = Attrs(n['attrs'] if 'attrs' in n else None) if attrs and attrs.NDA_LLADDR == '00:00:00:00:00:00': continue if attrs.NDA_DST == self.ipAddr: print "detect conflict with with mac addr %s" % (NDA_LLADDR) return self.setCurrentStatus('D') print "ip address %s has no conflict" % self.ipAddr return True def route(self, dev): gwmatch = False for n in self.ipr.get_routes(): if 'family' in n and n['family'] != 2: #ipv4 only continue attrs = Attrs(n['attrs'] if 'attrs' in n else None) #print attrs if attrs and attrs.RTA_GATEWAY == self.gw: gwmatch = True if not gwmatch: print "default g/w doesn't match with config: %s" % attrs.RTA_GATEWAY self.setCurrentStatus('G') return self.getCurrentStatusOK() def ifconfig(self, dev): for dd in self.ipr.get_links(): #filter may not work always attrs = Attrs(dd['attrs'] if 'attrs' in dd else None) if attrs and attrs.IFLA_IFNAME == dev: #print "dev:%s,index: %s" % (dev,dd['index']) aa = self.ipr.get_addr(index=dd['index']) if aa: attrs = Attrs(aa[0]['attrs'] if 'attrs' in aa[0] else None) return (attrs.IFA_ADDRESS, attrs.IFA_BROADCAST) if attrs else None return None def ping(self, ipaddr=None): cmd = "/bin/ping -t1 -c3 %s" % ipaddr r = re.compile( '^rtt min/avg/max/mdev = ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms$' ) ret = self.runCmd(cmd, r) return ret.groups() if ret else None def run(self): self.blink() #print "blinker thread ended" def changeDetector(self): if not self.ipr: return try: self.ipr.bind() except: print "Exception: %s" % traceback.format_exc() return self.checkerTimer = None def checker(): print "%s - checker invoked" % asctime() self.checkerTimer = None self.checkStatus() def checkerInvoker(): if not self.checkerTimer: print "%s - network state changed" % asctime() self.checkerTimer = Timer(8, checker) self.checkerTimer.start() while not self.terminate.wait(.1): if not self.ipr: return try: ret = select([self.ipr], [], [], 5) if len(ret[0]) == 0: continue for msg in self.ipr.get(): if 'family' in msg and msg['family'] != 2: #ipv4 only continue attrs = Attrs(msg['attrs'] if 'attrs' in msg else None) event = msg['event'] #print 'change detected: %s' % event msg.pop('event', None) msg.pop('attrs', None) msg.pop('header', None) if event in ("RTM_NEWADDR", "RTM_DELADDR", "RTM_GETADDR"): #print "%s - %s" % (asctime(),event) checkerInvoker() elif event in ("RTM_NEWROUTE", "RTM_DELROUTE", "RTM_GETROUTE"): #print "%s - %s" % (asctime(),event) checkerInvoker() elif event in ("RTM_NEWNEIGH"): if attrs.NDA_LLADDR == '00:00:00:00:00:00': continue if attrs.NDA_DST == self.ipAddr: print "detect conflict with with mac addr %s" % ( NDA_LLADDR) self.setCurrentStatus('D') except selerr, e: if e[0] != 4: print "Exception: %s" % e break except:
def _ns_add_ifc(self, name, ns_ifc, ifc_base_name=None, in_ifc=None, out_ifc=None, ipaddr=None, macaddr=None, fn=None, cmd=None, action="ok", disable_ipv6=False): if name in self.ipdbs: ns_ipdb = self.ipdbs[name] else: try: nl = NetNS(name) self.namespaces.append(nl) except KeyboardInterrupt: # remove the namespace if it has been created pyroute2.netns.remove(name) raise ns_ipdb = IPDB(nl) self.ipdbs[nl.netns] = ns_ipdb if disable_ipv6: cmd1 = [ "sysctl", "-q", "-w", "net.ipv6.conf.default.disable_ipv6=1" ] nsp = NSPopen(ns_ipdb.nl.netns, cmd1) nsp.wait() nsp.release() try: ns_ipdb.interfaces.lo.up().commit() except pyroute2.ipdb.exceptions.CommitException: print( "Warning, commit for lo failed, operstate may be unknown") if in_ifc: in_ifname = in_ifc.ifname with in_ifc as v: # move half of veth into namespace v.net_ns_fd = ns_ipdb.nl.netns else: # delete the potentially leaf-over veth interfaces ipr = IPRoute() for i in ipr.link_lookup(ifname='%sa' % ifc_base_name): ipr.link("del", index=i) ipr.close() try: out_ifc = self.ipdb.create(ifname="%sa" % ifc_base_name, kind="veth", peer="%sb" % ifc_base_name).commit() in_ifc = self.ipdb.interfaces[out_ifc.peer] in_ifname = in_ifc.ifname with in_ifc as v: v.net_ns_fd = ns_ipdb.nl.netns except KeyboardInterrupt: # explicitly remove the interface out_ifname = "%sa" % ifc_base_name if out_ifname in self.ipdb.interfaces: self.ipdb.interfaces[out_ifname].remove().commit() raise if out_ifc: out_ifc.up().commit() try: # this is a workaround for fc31 and possible other disto's. # when interface 'lo' is already up, do another 'up().commit()' # has issues in fc31. # the workaround may become permanent if we upgrade pyroute2 # in all machines. if 'state' in ns_ipdb.interfaces.lo.keys(): if ns_ipdb.interfaces.lo['state'] != 'up': ns_ipdb.interfaces.lo.up().commit() else: ns_ipdb.interfaces.lo.up().commit() except pyroute2.ipdb.exceptions.CommitException: print("Warning, commit for lo failed, operstate may be unknown") ns_ipdb.initdb() in_ifc = ns_ipdb.interfaces[in_ifname] with in_ifc as v: v.ifname = ns_ifc if ipaddr: v.add_ip("%s" % ipaddr) if macaddr: v.address = macaddr v.up() if disable_ipv6: cmd1 = [ "sysctl", "-q", "-w", "net.ipv6.conf.%s.disable_ipv6=1" % out_ifc.ifname ] subprocess.call(cmd1) if fn and out_ifc: self.ipdb.nl.tc("add", "ingress", out_ifc["index"], "ffff:") self.ipdb.nl.tc("add-filter", "bpf", out_ifc["index"], ":1", fd=fn.fd, name=fn.name, parent="ffff:", action=action, classid=1) if cmd: self.processes.append(NSPopen(ns_ipdb.nl.netns, cmd)) return (ns_ipdb, out_ifc, in_ifc)
class InterfaceServer(InterfaceServiceServicer): def __init__(self): self.interfaces_q = queue.Queue() # Used for Produce/Consume sync self.iproute = IPRoute() self.interfaces = {} # In-memory tracking for Pod interfaces self.queued_pods = set( ) # A set of pods, with queued interfaces (to be consumed) self.interfaces_lock = threading.Lock() cmd = 'ip addr show eth0 | grep "inet\\b" | awk \'{print $2}\' | cut -d/ -f1' r = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) self.droplet_ip = r.stdout.read().decode().strip() cmd = 'ip addr show eth0 | grep "link/ether\\b" | awk \'{print $2}\' | cut -d/ -f1' r = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) self.droplet_mac = r.stdout.read().decode().strip() cmd = 'hostname' r = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) self.droplet_name = r.stdout.read().decode().strip() self.itf = 'eth0' self.rpc = LocalTransitRpc('127.0.0.1', self.droplet_mac) def __del__(self): self.iproute.close() def InitializeInterfaces(self, request, context): """ Called by the endpoints operator to initilaize the interface and allocate its mac address """ interfaces = request for interface in interfaces.interfaces: self._CreateInterface(interface) logger.debug("Allocated mac {}".format(interface.address.mac)) return interfaces def _CreateInterface(self, interface): """ Type specific interface creation """ if interface.interface_type == InterfaceType.veth: return self._CreateVethInterface(interface) def _CreateVethInterface(self, interface): """ Creates a veth interface """ veth_name = interface.veth.name veth_peer = interface.veth.peer veth_index = get_iface_index(veth_name, self.iproute) if veth_index != -1: self.iproute.link('delete', index=veth_index) veth_index = -1 if veth_index == -1: self.iproute.link('add', ifname=veth_name, peer=veth_peer, kind='veth') veth_index = get_iface_index(veth_name, self.iproute) # Update the mac address with the interface address address = InterfaceAddress(version=interface.address.version, ip_address=interface.address.ip_address, ip_prefix=interface.address.ip_prefix, gateway_ip=interface.address.gateway_ip, mac=get_iface_mac(veth_index, self.iproute), tunnel_id=interface.address.tunnel_id) interface.address.CopyFrom(address) def ProduceInterfaces(self, request, context): """ Called by the endpoints operator to program the transit Agent and prepare the interface for consumption. """ interfaces = request for interface in interfaces.interfaces: self._QueueInterface(interface) return interfaces def _QueueInterface(self, interface): pod_name = get_pod_name(interface.interface_id.pod_id) logger.info("Producing interface {}".format(interface)) logger.info("Current queued interfaces {}".format(self.interfaces)) with self.interfaces_lock: # Append the interface to the pod's interfaces (important in # multi-interfaces case) if pod_name not in self.interfaces: self.interfaces[pod_name] = [] self.interfaces[pod_name].append(interface) # Move the pod_name into interfaces_q. This tells the consume # function that the pod has queued interfaces that can be consumed if pod_name not in self.queued_pods: self.interfaces_q.put(pod_name) self.queued_pods.add(pod_name) # Change interface status from init to queued. interface.status = InterfaceStatus.queued def _ConsumeInterfaces(self, pod_name, cni_params): """ Remove the pod from the interfaces_q and provision the interface (program the transit agent) """ with self.interfaces_lock: if pod_name in self.queued_pods: self.queued_pods.remove(pod_name) interfaces = self.interfaces.get(pod_name, []) for interface in interfaces: if interface.status == InterfaceStatus.queued: self._ProvisionInterface(interface, cni_params) interface.status = InterfaceStatus.consumed interfaces = InterfacesList(interfaces=interfaces) logger.info("Consumed {}".format(interfaces)) return interfaces def _ProvisionInterface(self, interface, cni_params): """ Provision the interface according to its type """ if interface.interface_type == InterfaceType.veth: self._ProvisionVethInterface(interface, cni_params) def _ProvisionVethInterface(self, interface, cni_params): """ Provision a veth interface. """ # Finalize all interface configuration. All main interface configuration # will be in the CNI. All the peer interface configuration, will be here # Network namespace operations (Move these to the CNI) veth_peer_index = get_iface_index(interface.veth.peer, self.iproute) if os.getenv('FEATUREGATE_BWQOS', 'false').lower() in ('false', '0'): self.iproute.link('set', index=veth_peer_index, state='up', mtu=9000) else: mzbr_index = get_iface_index(CONSTANTS.MIZAR_BRIDGE, self.iproute) self.iproute.link('set', index=veth_peer_index, master=mzbr_index, state='up', mtu=9000) # Configure the Transit Agent self._ConfigureTransitAgent(interface) def _ConfigureTransitAgent(self, interface): """ Load the Transit Agent XDP program, program all the bouncer substrate, update the agent metadata and endpoint. """ self.rpc.load_transit_agent_xdp(interface) for bouncer in interface.bouncers: self.rpc.update_agent_substrate_ep(interface.veth.peer, bouncer.ip_address, bouncer.mac) self.rpc.update_agent_metadata(interface) self.rpc.update_ep(interface) self.rpc.update_packet_metadata(interface.veth.peer, interface) def ConsumeInterfaces(self, request, context): """ Called by the CNI to consume queued interfaces for a specific POD """ cni_params = request requested_pod_id = cni_params.pod_id requested_pod_name = get_pod_name(requested_pod_id) logger.info( "Call from CNI Consume: cni_params/request: {}, cni_params.pod_id {}, pod_name {}" .format(request, request.pod_id, requested_pod_name)) logger.info("Consume Interfaces {}".format(request)) logger.info( "Consuming interfaces for pod: {} Current Queue: {}".format( requested_pod_name, list(self.interfaces_q.queue))) start = time.time() # The following is a synchronization mechanism to make sure the # CNI calls _ConsumeInterfaces after the interfaces got produced. # In Arktos, this may be skipped because the Kubelet will only # invoke CNI after the Pod operator marks the pod's network ready while True: try: queued_pod_name = self.interfaces_q.get( timeout=CONSUME_INTERFACE_TIMEOUT) except: break if queued_pod_name == requested_pod_name: # Interfaces for the Pod has been produced return self._ConsumeInterfaces(queued_pod_name, request) # Update the wait time and break the wait if necessary self.interfaces_q.put(queued_pod_name) now = time.time() if now - start >= CONSUME_INTERFACE_TIMEOUT: break # If we are here, the endpoint operator has not produced any interfaces # for the Pod. Typically the CNI will retry to consume the interface. logger.error("Timeout, no new interface to consume! {} {}".format( requested_pod_name, list(self.interfaces_q.queue))) return self._ConsumeInterfaces(requested_pod_name, request) def _DeleteVethInterface(self, interface): """ Delete a veth interface """ veth_peer_index = get_iface_index(interface.veth.peer, self.iproute) self.rpc.unload_transit_agent_xdp(interface) self.iproute.link('del', index=veth_peer_index) def DeleteInterface(self, request, context): """ Delete network interfaces for a pod """ cni_params = request pod_name = get_pod_name(cni_params.pod_id) pod_interfaces = self.interfaces.get(pod_name, []) iface = cni_params.interface logger.info("Deleting interfaces for pod {} with interfaces {}".format( pod_name, pod_interfaces)) for interface in pod_interfaces: self.interfaces[pod_name].remove(interface) if iface == interface and interface.interface_type == InterfaceType.veth: logger.info("Deleting interface: {}".format(iface)) self._DeleteVethInterface(interface) logger.info("Removed {}".format(interface)) return empty_pb2.Empty() def ActivateHostInterface(self, request, context): interface = request # Provision host veth interface and load transit xdp agent. veth_peer_index = get_iface_index(interface.veth.peer, self.iproute) self.iproute.link('set', index=veth_peer_index, state='up', mtu=9000) self.rpc.load_transit_agent_xdp(interface) veth_index = get_iface_index(interface.veth.name, self.iproute) # configure and activate interfaces self.iproute.link('set', index=veth_index, ifname=interface.veth.name) self.iproute.link('set', index=veth_index, state='up') # Setting the prefix length of the veth device to a large range adds a route to # the host. Since this device has the same IP address as the host's main # interface, this will affect the host network causing host traffic to be routed # to the wrong interface. Here we set the prefix length to 32. self.iproute.addr('add', index=veth_index, address=interface.address.ip_address, prefixlen=int(interface.address.ip_prefix)) self.iproute.route('add', dst=OBJ_DEFAULTS.default_net_ip, mask=int(OBJ_DEFAULTS.default_net_prefix), oif=veth_index) # Disable TSO and checksum offload as xdp currently does not support logger.info("Disable tso for host ep") cmd = "nsenter -t 1 -m -u -n -i ethtool -K {} tso off gso off ufo off".format( interface.veth.name) rc, text = run_cmd(cmd) logger.info("Disabled tso rc:{} text{}".format(rc, text)) logger.info("Disable rx tx offload for host ep") cmd = "nsenter -t 1 -m -u -n -i ethtool --offload {} rx off tx off".format( interface.veth.name) rc, text = run_cmd(cmd) logger.info( "Disabled rx tx offload for host ep rc: {} text: {}".format( rc, text)) return interface
def test_freeze(self): require_user('root') interface = self.ip.interfaces[self.ifd] # set up the interface with interface as i: i.add_ip('172.16.0.1/24') i.add_ip('172.16.1.1/24') i.up() # check assert ('172.16.0.1', 24) in interface.ipaddr assert ('172.16.1.1', 24) in interface.ipaddr assert interface.flags & 1 # assert routine def probe(): # The freeze results are dynamic: it is not a real freeze, # it is a restore routine. So it takes time for results # to stabilize err = None for _ in range(3): err = None interface.ipaddr.set_target((('172.16.0.1', 24), ('172.16.1.1', 24))) interface.ipaddr.target.wait() try: assert ('172.16.0.1', 24) in interface.ipaddr assert ('172.16.1.1', 24) in interface.ipaddr assert interface.flags & 1 break except AssertionError as e: err = e continue except Exception as e: err = e break if err is not None: interface.unfreeze() i2.close() raise err # freeze interface.freeze() # change the interface somehow i2 = IPRoute() i2.addr('delete', interface.index, '172.16.0.1', 24) i2.addr('delete', interface.index, '172.16.1.1', 24) probe() # unfreeze self.ip.interfaces[self.ifd].unfreeze() try: i2.addr('delete', interface.index, '172.16.0.1', 24) i2.addr('delete', interface.index, '172.16.1.1', 24) except: pass finally: i2.close() # should be up, but w/o addresses interface.ipaddr.set_target(set()) interface.ipaddr.target.wait(3) assert ('172.16.0.1', 24) not in self.ip.interfaces[self.ifd].ipaddr assert ('172.16.1.1', 24) not in self.ip.interfaces[self.ifd].ipaddr assert self.ip.interfaces[self.ifd].flags & 1
class NetNSManager(Inotify): def __init__(self, libc=None, path=None, target='netns_manager'): path = set(path or []) super(NetNSManager, self).__init__(libc, path) if not self.path: for d in ['/var/run/netns', '/var/run/docker/netns']: try: self.register_path(d) except OSError: pass self.ipr = IPRoute(target=target) self.registry = {} self.update() self.target = target def update(self): self.ipr.netns_path = self.path for info in self.ipr.get_netns_info(): self.registry[info.get_attr('NSINFO_PATH')] = info def get(self): for msg in super(NetNSManager, self).get(): info = nsinfmsg() if msg is None: info['header']['error'] = NetlinkError(errno.ECONNRESET) info['header']['type'] = RTM_DELNETNS info['header']['target'] = self.target info['event'] = 'RTM_DELNETNS' yield info return path = '{path}/{name}'.format(**msg) info['header']['error'] = None info['header']['target'] = self.target if path not in self.registry: self.update() if path in self.registry: info.load(self.registry[path]) else: info['attrs'] = [('NSINFO_PATH', path)] del info['value'] if msg['mask'] & 0x200: info['header']['type'] = RTM_DELNETNS info['event'] = 'RTM_DELNETNS' elif not msg['mask'] & 0x100: continue yield info def close(self, code=None): self.ipr.close() super(NetNSManager, self).close() def create(self, path): netnspath = netns._get_netnspath(path) try: netns.create(netnspath, self.libc) except OSError as e: raise NetlinkError(e.errno) info = self.ipr._dump_one_ns(netnspath, set()) info['header']['type'] = RTM_NEWNETNS info['header']['target'] = self.target info['event'] = 'RTM_NEWNETNS' del info['value'] return info, def remove(self, path): netnspath = netns._get_netnspath(path) info = None try: info = self.ipr._dump_one_ns(netnspath, set()) except SkipInode: raise NetlinkError(errno.EEXIST) info['header']['type'] = RTM_DELNETNS info['header']['target'] = self.target info['event'] = 'RTM_DELNETNS' del info['value'] try: netns.remove(netnspath, self.libc) except OSError as e: raise NetlinkError(e.errno) return info, def netns(self, cmd, *argv, **kwarg): path = kwarg.get('path', kwarg.get('NSINFO_PATH')) if path is None: raise ValueError('netns spec is required') netnspath = netns._get_netnspath(path) if cmd == 'add': return self.create(netnspath) elif cmd == 'del': return self.remove(netnspath) elif cmd not in ('get', 'set'): raise ValueError('method not supported') for item in self.dump(): if item.get_attr('NSINFO_PATH') == netnspath: return (item, ) return tuple() def dump(self): return self.ipr.get_netns_info()
def Server(cmdch, brdch): ''' A server routine to run an IPRoute object and expose it via custom RPC. many TODOs: * document the protocol * provide not only IPRoute RPC Messages sent via channels are dictionaries with predefined structure. There are 4 s.c. stages:: init (server <-----> client) command (server <-----> client) broadcast (server ------> client) shutdown (server <------ client) Stage 'init' is used during initialization. The client establishes connections to the server and announces them by sending a single message via each channel:: {'stage': 'init', 'domain': ch_domain, 'client': client.uuid} Here, the client uuid is used to group all the connections of the same client and `ch_domain` is either 'command', or 'broadcast'. The latter will become a unidirectional channel from the server to the client, all data that arrives on the server side via netlink socket will be forwarded to the broadcast channel. The command channel will be used to make RPC calls and to shut the worker thread down before the client disconnects from the server. When all the registration is done, the server sends a single message via the command channel:: {'stage': 'init', 'error': exception or None} If the `error` field is None, everything is ok. If it is an exception, the init is failed and the exception should be thrown on the client side. In the runtime, all the data that arrives on the netlink socket fd, is to be forwarded directly via the broadcast channel. Commands are handled with the `command` stage:: # request {'stage': 'command', 'name': str, 'cookie': cookie, 'argv': [...], 'kwarg': {...}} # response {'stage': 'command', 'error': exception or None, 'return': retval, 'cookie': cookie} Right now the protocol is synchronous, so there is not need in cookie yet. But in some future it can turn into async, and then cookies will be used to match messages. The final stage is 'shutdown'. It terminates the worker thread, has no response and no messages can passed after. ''' def close(s, frame): # just leave everything else as is brdch.send({'stage': 'signal', 'data': s}) try: ipr = IPRoute() lock = ipr._sproxy.lock ipr._s_channel = ProxyChannel(brdch, 'broadcast') poll = select.poll() poll.register(ipr, select.POLLIN | select.POLLPRI) poll.register(cmdch, select.POLLIN | select.POLLPRI) except Exception as e: cmdch.send({'stage': 'init', 'error': e}) return 255 # all is OK so far cmdch.send({'stage': 'init', 'error': None}) signal.signal(signal.SIGHUP, close) signal.signal(signal.SIGINT, close) signal.signal(signal.SIGTERM, close) # 8<------------------------------------------------------------- while True: try: events = poll.poll() except: continue for (fd, event) in events: if fd == ipr.fileno(): bufsize = ipr.getsockopt(SOL_SOCKET, SO_RCVBUF) // 2 with lock: error = None data = None try: data = ipr.recv(bufsize) except Exception as e: error = e error.tb = traceback.format_exc() brdch.send({'stage': 'broadcast', 'data': data, 'error': error}) elif fd == cmdch.fileno(): cmd = cmdch.recv() if cmd['stage'] == 'shutdown': poll.unregister(ipr) poll.unregister(cmdch) ipr.close() return elif cmd['stage'] == 'reconstruct': error = None try: msg = cmd['argv'][0]() msg.load(pickle.loads(cmd['argv'][1])) msg.encode() ipr.sendto_gate(msg, cmd['argv'][2]) except Exception as e: error = e error.tb = traceback.format_exc() cmdch.send({'stage': 'reconstruct', 'error': error, 'return': None, 'cookie': cmd['cookie']}) elif cmd['stage'] == 'command': error = None try: ret = getattr(ipr, cmd['name'])(*cmd['argv'], **cmd['kwarg']) except Exception as e: error = e error.tb = traceback.format_exc() cmdch.send({'stage': 'command', 'error': error, 'return': ret, 'cookie': cmd['cookie']})
class Cni: def __init__(self): stdin = ''.join(sys.stdin.readlines()) self.command = os.environ.get("CNI_COMMAND") # ADD | DEL | VERSION self.container_id = os.environ.get("CNI_CONTAINERID") self.netns = os.environ.get("CNI_NETNS") self.interface = os.environ.get("CNI_IFNAME") self.cni_path = os.environ.get("CNI_PATH") self.cni_args = os.environ.get("CNI_ARGS") self.cni_args_dict = {} netns_folder = "/var/run/netns/" if not self.netns.startswith(netns_folder): dst_netns = self.netns.replace('/', '_') dst_netns_path = os.path.join(netns_folder, dst_netns) if self.command == "ADD": errorcode = bindmount_netns(self.netns, dst_netns_path) if errorcode != 0: logger.info( "failed to bind mount {} to {}: error code {}".format( self.netns, dst_netns_path, errorcode)) raise OSError( "failed to bind mount netns {} to {}, error code: {}". format(self.netns, dst_netns_path, errorcode)) self.netns = dst_netns_path config_json = json.loads(stdin) # expected parameters in the CNI specification: self.cni_version = config_json.get("cniVersion") self.network_name = config_json.get("name") self.plugin = config_json.get("type") if len(self.cni_args) > 1: self.cni_args_dict = dict( i.split("=") for i in self.cni_args.split(";")) self.k8s_namespace = self.cni_args_dict.get( 'K8S_POD_NAMESPACE', '') self.k8s_pod_name = self.cni_args_dict.get('K8S_POD_NAME', '') # TODO: parse 'Arktos specific' CNI_ARGS self.k8s_pod_tenant = self.cni_args_dict.get('K8S_POD_TENANT', '') self.pod_id = PodId(k8s_pod_name=self.k8s_pod_name, k8s_namespace=self.k8s_namespace, k8s_pod_tenant=self.k8s_pod_tenant) self.interface_id = InterfaceId(pod_id=self.pod_id, interface=self.interface) self.iproute = IPRoute() def __del__(self): if self.command != "VERSION": logger.info("Closing IPRoute") self.iproute.close() def run(self): if len(self.cni_args_dict) != 0: logging.info("CNI ARGS {}".format(self.cni_args_dict)) val = "Unsuported cni command!" switcher = { 'ADD': self.do_add, 'DEL': self.do_delete, 'GET': self.do_get, 'VERSION': self.do_version } func = switcher.get(self.command, lambda: "Unsupported cni command") if func: func() print(val) exit(1) def do_add(self): # Construct a CniParameters grpc message param = CniParameters(pod_id=self.pod_id, netns=self.netns, interface=self.interface) logger.info("Doing CNI add for {}".format(self.pod_id)) # Consume new (and existing) interfaces for this Pod interfaces = InterfaceServiceClient("localhost").ConsumeInterfaces( param).interfaces if len(interfaces) == 0: logger.error("No interfaces found for {}".format(self.pod_id)) exit(1) result = {"cniVersion": self.cni_version, "interfaces": [], "ips": []} # Construct the result string idx = 0 for interface in interfaces: self.activate_interface(interface) itf = { "name": interface.interface_id.interface, "mac": interface.address.mac, "sandbox": self.netns } ip = { "version": interface.address.version, "address": "{}/{}".format(interface.address.ip_address, interface.address.ip_prefix), "gateway": interface.address.gateway_ip, "interface": idx } result['interfaces'].append(itf) result['ips'].append(ip) idx += 1 print(json.dumps(result)) exit(0) def activate_interface(self, interface): """ moves the interface to the CNI netnt, rename it, set the IP address, and the GW. """ _, netns = os.path.split(self.netns) iproute_ns = NetNS(netns) veth_index = get_iface_index(interface.veth.name, self.iproute) actived_idxs = iproute_ns.link_lookup(operstate="UP") if (veth_index in actived_idxs): return logger.info("Move interface {}/{} to netns {}".format( interface.veth.name, veth_index, netns)) self.iproute.link('set', index=veth_index, net_ns_fd=netns) # configure and activate interfaces inside the netns iproute_ns.link('set', index=veth_index, ifname=self.interface) lo_idx = iproute_ns.link_lookup(ifname="lo")[0] iproute_ns.link('set', index=lo_idx, state='up') iproute_ns.link('set', index=veth_index, state='up') iproute_ns.addr('add', index=veth_index, address=interface.address.ip_address, prefixlen=int(interface.address.ip_prefix)) iproute_ns.route('add', gateway=interface.address.gateway_ip) # Disable TSO and checksum offload as xdp currently does not support logger.info("Disable tso for pod") cmd = "ip netns exec {} ethtool -K {} tso off gso off ufo off".format( netns, "eth0") rc, text = run_cmd(cmd) logger.info("Executed cmd {} tso rc: {} text {}".format(cmd, rc, text)) logger.info("Disable rx tx offload for pod") cmd = "ip netns exec {} ethtool --offload {} rx off tx off".format( netns, "eth0") rc, text = run_cmd(cmd) logger.info("Executed cmd {} rc: {} text {}".format(cmd, rc, text)) def do_delete(self): param = CniParameters(pod_id=self.pod_id, netns=self.netns, interface=self.interface) InterfaceServiceClient("localhost").DeleteInterface(param) _, netns = os.path.split(self.netns) if len(netns) > 0: subprocess.run(["ip", "netns", "delete", netns]) exit(0) def do_get(self): logger.info("CNI get is not implemented") print("") exit(0) def do_version(self): val, status = json.dumps({ 'cniVersion': '0.3.1', "supportedVersions": ["0.2.0", "0.3.0", "0.3.1"] }), 0 logger.info("server's version is {}".format(val)) print(val) exit(status)
class TestIPRoute(object): def setup(self): self.ip = IPRoute() self.ap = AddrPool() self.iftmp = 'pr2x{0}' try: self.dev, idx = self.create() self.ifaces = [idx] except IndexError: pass def get_ifname(self): return self.iftmp.format(self.ap.alloc()) def create(self, kind='dummy'): name = self.get_ifname() create_link(name, kind=kind) idx = self.ip.link_lookup(ifname=name)[0] return (name, idx) def teardown(self): if hasattr(self, 'ifaces'): for dev in self.ifaces: try: self.ip.link('delete', index=dev) except: pass self.ip.close() def _test_nla_operators(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) r = [x for x in self.ip.get_addr() if x['index'] == self.ifaces[0]] complement = r[0] - r[1] intersection = r[0] & r[1] assert complement.get_attr('IFA_ADDRESS') == '172.16.0.1' assert complement.get_attr('IFA_LABEL') is None assert complement['prefixlen'] == 0 assert complement['index'] == 0 assert intersection.get_attr('IFA_ADDRESS') is None assert intersection.get_attr('IFA_LABEL') == self.dev assert intersection['prefixlen'] == 24 assert intersection['index'] == self.ifaces[0] def test_add_addr(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) assert '172.16.0.1/24' in get_ip_addr() def _create(self, kind): name = self.get_ifname() self.ip.link_create(ifname=name, kind=kind) devs = self.ip.link_lookup(ifname=name) assert devs self.ifaces.extend(devs) def test_create_dummy(self): require_user('root') self._create('dummy') def test_create_bond(self): require_user('root') self._create('bond') def test_create_bridge(self): require_user('root') self._create('bridge') def test_create_ovs_bridge(self): require_user('root') self._create('ovs-bridge') def test_create_team(self): require_user('root') self._create('team') def test_neigh_real_links(self): links = set([x['index'] for x in self.ip.get_links()]) neigh = set([x['ifindex'] for x in self.ip.get_neighbors()]) assert neigh < links def test_mass_ipv6(self): # # Achtung! This test is time consuming. # It is really time consuming, I'm not not # kidding you. Beware. # require_user('root') base = 'fdb3:84e5:4ff4:55e4::{0}' limit = int(os.environ.get('PYROUTE2_SLIMIT', '0x800'), 16) # add addresses for idx in range(limit): self.ip.addr('add', self.ifaces[0], base.format(hex(idx)[2:]), 48) # assert addresses in two steps, to ease debug addrs = self.ip.get_addr(10) assert len(addrs) >= limit # clean up addresses # # it is not required, but if you don't do that, # you'll get this on the interface removal: # # >> kernel:BUG: soft lockup - CPU#0 stuck for ... # # so, not to scare people, remove addresses gracefully # one by one # # it also verifies all the addresses are in place for idx in reversed(range(limit)): self.ip.addr('delete', self.ifaces[0], base.format(hex(idx)[2:]), 48) def test_fail_not_permitted(self): try: self.ip.addr('add', 1, address='172.16.0.1', mask=24) except NetlinkError as e: if e.code != 1: # Operation not permitted raise finally: try: self.ip.addr('delete', 1, address='172.16.0.1', mask=24) except: pass def test_fail_no_such_device(self): require_user('root') dev = sorted([i['index'] for i in self.ip.get_links()])[-1] + 10 try: self.ip.addr('add', dev, address='172.16.0.1', mask=24) except NetlinkError as e: if e.code != 19: # No such device raise def test_remove_link(self): require_user('root') try: self.ip.link_remove(self.ifaces[0]) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 0 def test_get_route(self): if not self.ip.get_default_routes(table=254): return rts = self.ip.get_routes(family=socket.AF_INET, dst='8.8.8.8', table=254) assert len(rts) > 0 def test_route_change_existing(self): # route('replace', ...) should succeed, if route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) self.ip.route('add', prefix='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.route('change', prefix='172.16.1.0', mask=24, gateway='172.16.0.2', table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') def test_route_change_not_existing_fail(self): # route('change', ...) should fail, if no route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') try: self.ip.route('change', prefix='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) except NetlinkError as e: if e.code != 2: raise def test_route_replace_existing(self): # route('replace', ...) should succeed, if route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) self.ip.route('replace', prefix='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.route('replace', prefix='172.16.1.0', mask=24, gateway='172.16.0.2', table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') def test_route_replace_not_existing(self): # route('replace', ...) should succeed, if route doesn't exist require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('replace', prefix='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') def test_flush_routes(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('add', prefix='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) self.ip.route('add', prefix='172.16.2.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.2.0/24.*172.16.0.1') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert not grep('ip route show table 100', pattern='172.16.2.0/24.*172.16.0.1') def test_route_table_2048(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('add', prefix='172.16.1.0', mask=24, gateway='172.16.0.1', table=2048) assert grep('ip route show table 2048', pattern='172.16.1.0/24.*172.16.0.1') remove_link('bala') def test_symbolic_flags_ifaddrmsg(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], '172.16.1.1', 24) addr = [ x for x in self.ip.get_addr() if x.get_attr('IFA_LOCAL') == '172.16.1.1' ][0] assert 'IFA_F_PERMANENT' in addr.flags2names(addr['flags']) def test_symbolic_flags_ifinfmsg(self): require_user('root') self.ip.link('set', index=self.ifaces[0], flags=['IFF_UP']) iface = self.ip.get_links(self.ifaces[0])[0] assert iface['flags'] & 1 assert 'IFF_UP' in iface.flags2names(iface['flags']) self.ip.link('set', index=self.ifaces[0], flags=['!IFF_UP']) assert not (self.ip.get_links(self.ifaces[0])[0]['flags'] & 1) def test_updown_link(self): require_user('root') try: self.ip.link_up(*self.ifaces) except NetlinkError: pass assert self.ip.get_links(*self.ifaces)[0]['flags'] & 1 try: self.ip.link_down(*self.ifaces) except NetlinkError: pass assert not (self.ip.get_links(*self.ifaces)[0]['flags'] & 1) def test_callbacks_positive(self): require_user('root') dev = self.ifaces[0] self.cb_counter = 0 self.ip.register_callback(_callback, lambda x: x.get('index', None) == dev, (self, )) self.test_updown_link() assert self.cb_counter > 0 self.ip.unregister_callback(_callback) def test_callbacks_negative(self): require_user('root') self.cb_counter = 0 self.ip.register_callback(_callback, lambda x: x.get('index', None) == -1, (self, )) self.test_updown_link() assert self.cb_counter == 0 self.ip.unregister_callback(_callback) def test_rename_link(self): require_user('root') dev = self.ifaces[0] try: self.ip.link_rename(dev, 'bala') except NetlinkError: pass assert len(self.ip.link_lookup(ifname='bala')) == 1 try: self.ip.link_rename(dev, self.dev) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 1 def test_rules(self): assert len(get_ip_rules('-4')) == \ len(self.ip.get_rules(socket.AF_INET)) assert len(get_ip_rules('-6')) == \ len(self.ip.get_rules(socket.AF_INET6)) def test_addr(self): assert len(get_ip_addr()) == len(self.ip.get_addr()) def test_links(self): assert len(get_ip_link()) == len(self.ip.get_links()) def test_one_link(self): lo = self.ip.get_links(1)[0] assert lo.get_attr('IFLA_IFNAME') == 'lo' def test_default_routes(self): assert len(get_ip_default_routes()) == \ len(self.ip.get_default_routes(family=socket.AF_INET, table=254)) def test_routes(self): assert len(get_ip_route()) == \ len(self.ip.get_routes(family=socket.AF_INET, table=255))
def Server(cmdch, brdch): ''' A server routine to run an IPRoute object and expose it via custom RPC. many TODOs: * document the protocol * provide not only IPRoute RPC Messages sent via channels are dictionaries with predefined structure. There are 4 s.c. stages:: init (server <-----> client) command (server <-----> client) broadcast (server ------> client) shutdown (server <------ client) Stage 'init' is used during initialization. The client establishes connections to the server and announces them by sending a single message via each channel:: {'stage': 'init', 'domain': ch_domain, 'client': client.uuid} Here, the client uuid is used to group all the connections of the same client and `ch_domain` is either 'command', or 'broadcast'. The latter will become a unidirectional channel from the server to the client, all data that arrives on the server side via netlink socket will be forwarded to the broadcast channel. The command channel will be used to make RPC calls and to shut the worker thread down before the client disconnects from the server. When all the registration is done, the server sends a single message via the command channel:: {'stage': 'init', 'error': exception or None} If the `error` field is None, everything is ok. If it is an exception, the init is failed and the exception should be thrown on the client side. In the runtime, all the data that arrives on the netlink socket fd, is to be forwarded directly via the broadcast channel. Commands are handled with the `command` stage:: # request {'stage': 'command', 'name': str, 'cookie': cookie, 'argv': [...], 'kwarg': {...}} # response {'stage': 'command', 'error': exception or None, 'return': retval, 'cookie': cookie} Right now the protocol is synchronous, so there is not need in cookie yet. But in some future it can turn into async, and then cookies will be used to match messages. The final stage is 'shutdown'. It terminates the worker thread, has no response and no messages can passed after. ''' def close(s, frame): # just leave everything else as is brdch.send({'stage': 'signal', 'data': s}) try: ipr = IPRoute() lock = ipr._sproxy.lock ipr._s_channel = brdch poll = select.poll() poll.register(ipr, select.POLLIN | select.POLLPRI) poll.register(cmdch, select.POLLIN | select.POLLPRI) except Exception as e: cmdch.send({'stage': 'init', 'error': e}) return 255 # all is OK so far cmdch.send({'stage': 'init', 'error': None}) signal.signal(signal.SIGTERM, close) # 8<------------------------------------------------------------- while True: try: events = poll.poll() except: continue for (fd, event) in events: if fd == ipr.fileno(): bufsize = ipr.getsockopt(SOL_SOCKET, SO_RCVBUF) // 2 with lock: error = None data = None try: data = ipr.recv(bufsize) except Exception as e: error = e error.tb = traceback.format_exc() brdch.send({ 'stage': 'broadcast', 'data': data, 'error': error }) elif fd == cmdch.fileno(): cmd = cmdch.recv() if cmd['stage'] == 'shutdown': poll.unregister(ipr) poll.unregister(cmdch) ipr.close() return elif cmd['stage'] == 'command': error = None try: ret = getattr(ipr, cmd['name'])(*cmd['argv'], **cmd['kwarg']) except Exception as e: error = e error.tb = traceback.format_exc() cmdch.send({ 'stage': 'command', 'error': error, 'return': ret, 'cookie': cmd['cookie'] })
def _ns_add_ifc(self, name, ns_ifc, ifc_base_name=None, in_ifc=None, out_ifc=None, ipaddr=None, macaddr=None, fn=None, cmd=None, action="ok", disable_ipv6=False): if name in self.ipdbs: ns_ipdb = self.ipdbs[name] else: try: nl = NetNS(name) self.namespaces.append(nl) except KeyboardInterrupt: # remove the namespace if it has been created pyroute2.netns.remove(name) raise ns_ipdb = IPDB(nl) self.ipdbs[nl.netns] = ns_ipdb if disable_ipv6: cmd1 = [ "sysctl", "-q", "-w", "net.ipv6.conf.default.disable_ipv6=1" ] nsp = NSPopen(ns_ipdb.nl.netns, cmd1) nsp.wait() nsp.release() ns_ipdb.interfaces.lo.up().commit() if in_ifc: in_ifname = in_ifc.ifname with in_ifc as v: # move half of veth into namespace v.net_ns_fd = ns_ipdb.nl.netns else: # delete the potentially leaf-over veth interfaces ipr = IPRoute() for i in ipr.link_lookup(ifname='%sa' % ifc_base_name): ipr.link_remove(i) ipr.close() try: out_ifc = self.ipdb.create(ifname="%sa" % ifc_base_name, kind="veth", peer="%sb" % ifc_base_name).commit() in_ifc = self.ipdb.interfaces[out_ifc.peer] in_ifname = in_ifc.ifname with in_ifc as v: v.net_ns_fd = ns_ipdb.nl.netns except KeyboardInterrupt: # explicitly remove the interface out_ifname = "%sa" % ifc_base_name if out_ifname in self.ipdb.interfaces: self.ipdb.interfaces[out_ifname].remove().commit() raise if out_ifc: out_ifc.up().commit() ns_ipdb.interfaces.lo.up().commit() in_ifc = ns_ipdb.interfaces[in_ifname] with in_ifc as v: v.ifname = ns_ifc if ipaddr: v.add_ip("%s" % ipaddr) if macaddr: v.address = macaddr v.up() if disable_ipv6: cmd1 = [ "sysctl", "-q", "-w", "net.ipv6.conf.%s.disable_ipv6=1" % out_ifc.ifname ] subprocess.call(cmd1) if fn and out_ifc: self.ipdb.nl.tc("add", "ingress", out_ifc["index"], "ffff:") self.ipdb.nl.tc("add-filter", "bpf", out_ifc["index"], ":1", fd=fn.fd, name=fn.name, parent="ffff:", action=action, classid=1) if cmd: self.processes.append(NSPopen(ns_ipdb.nl.netns, cmd)) return (ns_ipdb, out_ifc, in_ifc)
class TestIPRoute(object): def setup(self): self.ip = IPRoute() self.ap = AddrPool() self.iftmp = 'pr2x{0}' try: self.dev, idx = self.create() self.ifaces = [idx] except IndexError: pass def get_ifname(self): return self.iftmp.format(self.ap.alloc()) def create(self, kind='dummy'): name = self.get_ifname() create_link(name, kind=kind) idx = self.ip.link_lookup(ifname=name)[0] return (name, idx) def teardown(self): if hasattr(self, 'ifaces'): for dev in self.ifaces: try: self.ip.link('delete', index=dev) except: pass self.ip.close() def _test_nla_operators(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) r = [x for x in self.ip.get_addr() if x['index'] == self.ifaces[0]] complement = r[0] - r[1] intersection = r[0] & r[1] assert complement.get_attr('IFA_ADDRESS') == '172.16.0.1' assert complement.get_attr('IFA_LABEL') is None assert complement['prefixlen'] == 0 assert complement['index'] == 0 assert intersection.get_attr('IFA_ADDRESS') is None assert intersection.get_attr('IFA_LABEL') == self.dev assert intersection['prefixlen'] == 24 assert intersection['index'] == self.ifaces[0] def test_add_addr(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) assert '172.16.0.1/24' in get_ip_addr() def _create(self, kind): name = self.get_ifname() self.ip.link_create(ifname=name, kind=kind) devs = self.ip.link_lookup(ifname=name) assert devs self.ifaces.extend(devs) def test_create_dummy(self): require_user('root') self._create('dummy') def test_create_bond(self): require_user('root') self._create('bond') def test_create_bridge(self): require_user('root') self._create('bridge') def test_neigh_real_links(self): links = set([x['index'] for x in self.ip.get_links()]) neigh = set([x['ifindex'] for x in self.ip.get_neighbors()]) assert neigh < links def test_mass_ipv6(self): # # Achtung! This test is time consuming. # It is really time consuming, I'm not not # kidding you. Beware. # require_user('root') base = 'fdb3:84e5:4ff4:55e4::{0}' limit = int(os.environ.get('PYROUTE2_SLIMIT', '0x800'), 16) # add addresses for idx in range(limit): self.ip.addr('add', self.ifaces[0], base.format(hex(idx)[2:]), 48) # assert addresses in two steps, to ease debug addrs = self.ip.get_addr(10) assert len(addrs) >= limit # clean up addresses # # it is not required, but if you don't do that, # you'll get this on the interface removal: # # >> kernel:BUG: soft lockup - CPU#0 stuck for ... # # so, not to scare people, remove addresses gracefully # one by one # # it also verifies all the addresses are in place for idx in reversed(range(limit)): self.ip.addr('delete', self.ifaces[0], base.format(hex(idx)[2:]), 48) def test_fail_not_permitted(self): try: self.ip.addr('add', 1, address='172.16.0.1', mask=24) except NetlinkError as e: if e.code != 1: # Operation not permitted raise finally: try: self.ip.addr('delete', 1, address='172.16.0.1', mask=24) except: pass def test_fail_no_such_device(self): require_user('root') dev = sorted([i['index'] for i in self.ip.get_links()])[-1] + 10 try: self.ip.addr('add', dev, address='172.16.0.1', mask=24) except NetlinkError as e: if e.code != 19: # No such device raise def test_remove_link(self): require_user('root') try: self.ip.link_remove(self.ifaces[0]) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 0 def test_get_route(self): if not self.ip.get_default_routes(table=254): return rts = self.ip.get_routes(family=socket.AF_INET, dst='8.8.8.8', table=254) assert len(rts) > 0 def test_flush_routes(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('add', prefix='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) self.ip.route('add', prefix='172.16.2.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.2.0/24.*172.16.0.1') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert not grep('ip route show table 100', pattern='172.16.2.0/24.*172.16.0.1') def test_route_table_2048(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('add', prefix='172.16.1.0', mask=24, gateway='172.16.0.1', table=2048) assert grep('ip route show table 2048', pattern='172.16.1.0/24.*172.16.0.1') remove_link('bala') def test_symbolic_flags_ifaddrmsg(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], '172.16.1.1', 24) addr = [x for x in self.ip.get_addr() if x.get_attr('IFA_LOCAL') == '172.16.1.1'][0] assert 'IFA_F_PERMANENT' in addr.flags2names(addr['flags']) def test_symbolic_flags_ifinfmsg(self): require_user('root') self.ip.link('set', index=self.ifaces[0], flags=['IFF_UP']) iface = self.ip.get_links(self.ifaces[0])[0] assert iface['flags'] & 1 assert 'IFF_UP' in iface.flags2names(iface['flags']) self.ip.link('set', index=self.ifaces[0], flags=['!IFF_UP']) assert not (self.ip.get_links(self.ifaces[0])[0]['flags'] & 1) def test_updown_link(self): require_user('root') try: self.ip.link_up(*self.ifaces) except NetlinkError: pass assert self.ip.get_links(*self.ifaces)[0]['flags'] & 1 try: self.ip.link_down(*self.ifaces) except NetlinkError: pass assert not (self.ip.get_links(*self.ifaces)[0]['flags'] & 1) def test_callbacks_positive(self): require_user('root') dev = self.ifaces[0] self.cb_counter = 0 self.ip.register_callback(_callback, lambda x: x.get('index', None) == dev, (self, )) self.test_updown_link() assert self.cb_counter > 0 self.ip.unregister_callback(_callback) def test_callbacks_negative(self): require_user('root') self.cb_counter = 0 self.ip.register_callback(_callback, lambda x: x.get('index', None) == -1, (self, )) self.test_updown_link() assert self.cb_counter == 0 self.ip.unregister_callback(_callback) def test_rename_link(self): require_user('root') dev = self.ifaces[0] try: self.ip.link_rename(dev, 'bala') except NetlinkError: pass assert len(self.ip.link_lookup(ifname='bala')) == 1 try: self.ip.link_rename(dev, self.dev) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 1 def test_rules(self): assert len(get_ip_rules('-4')) == \ len(self.ip.get_rules(socket.AF_INET)) assert len(get_ip_rules('-6')) == \ len(self.ip.get_rules(socket.AF_INET6)) def test_addr(self): assert len(get_ip_addr()) == len(self.ip.get_addr()) def test_links(self): assert len(get_ip_link()) == len(self.ip.get_links()) def test_one_link(self): lo = self.ip.get_links(1)[0] assert lo.get_attr('IFLA_IFNAME') == 'lo' def test_default_routes(self): assert len(get_ip_default_routes()) == \ len(self.ip.get_default_routes(family=socket.AF_INET, table=254)) def test_routes(self): assert len(get_ip_route()) == \ len(self.ip.get_routes(family=socket.AF_INET, table=255))
def _ns_add_ifc(self, name, ns_ifc, ifc_base_name=None, in_ifc=None, out_ifc=None, ipaddr=None, macaddr=None, fn=None, cmd=None, action="ok", disable_ipv6=False): if name in self.ipdbs: ns_ipdb = self.ipdbs[name] else: try: nl=NetNS(name) self.namespaces.append(nl) except KeyboardInterrupt: # remove the namespace if it has been created pyroute2.netns.remove(name) raise ns_ipdb = IPDB(nl) self.ipdbs[nl.netns] = ns_ipdb if disable_ipv6: cmd1 = ["sysctl", "-q", "-w", "net.ipv6.conf.default.disable_ipv6=1"] nsp = NSPopen(ns_ipdb.nl.netns, cmd1) nsp.wait(); nsp.release() ns_ipdb.interfaces.lo.up().commit() if in_ifc: in_ifname = in_ifc.ifname with in_ifc as v: # move half of veth into namespace v.net_ns_fd = ns_ipdb.nl.netns else: # delete the potentially leaf-over veth interfaces ipr = IPRoute() for i in ipr.link_lookup(ifname='%sa' % ifc_base_name): ipr.link_remove(i) ipr.close() try: out_ifc = self.ipdb.create(ifname="%sa" % ifc_base_name, kind="veth", peer="%sb" % ifc_base_name).commit() in_ifc = self.ipdb.interfaces[out_ifc.peer] in_ifname = in_ifc.ifname with in_ifc as v: v.net_ns_fd = ns_ipdb.nl.netns except KeyboardInterrupt: # explicitly remove the interface out_ifname = "%sa" % ifc_base_name if out_ifname in self.ipdb.interfaces: self.ipdb.interfaces[out_ifname].remove().commit() raise if out_ifc: out_ifc.up().commit() ns_ipdb.interfaces.lo.up().commit() ns_ipdb.initdb() in_ifc = ns_ipdb.interfaces[in_ifname] with in_ifc as v: v.ifname = ns_ifc if ipaddr: v.add_ip("%s" % ipaddr) if macaddr: v.address = macaddr v.up() if disable_ipv6: cmd1 = ["sysctl", "-q", "-w", "net.ipv6.conf.%s.disable_ipv6=1" % out_ifc.ifname] subprocess.call(cmd1) if fn and out_ifc: self.ipdb.nl.tc("add", "ingress", out_ifc["index"], "ffff:") self.ipdb.nl.tc("add-filter", "bpf", out_ifc["index"], ":1", fd=fn.fd, name=fn.name, parent="ffff:", action=action, classid=1) if cmd: self.processes.append(NSPopen(ns_ipdb.nl.netns, cmd)) return (ns_ipdb, out_ifc, in_ifc)
class TestRule(object): def setup(self): require_user('root') self.ip = IPRoute() self.ip.link('add', index=0, ifname='dummyX', linkinfo={'attrs': [['IFLA_INFO_KIND', 'dummy']]}) self.interface = self.ip.link_lookup(ifname='dummyX')[0] def teardown(self): self.ip.link('delete', index=self.interface) self.ip.close() def test_basic(self): self.ip.rule('add', 10, 32000) assert len([ x for x in self.ip.get_rules() if x.get_attr('RTA_PRIORITY') == 32000 and x.get_attr('RTA_TABLE') == 10 ]) == 1 self.ip.rule('delete', 10, 32000) assert len([ x for x in self.ip.get_rules() if x.get_attr('RTA_PRIORITY') == 32000 and x.get_attr('RTA_TABLE') == 10 ]) == 0 def test_fwmark(self): self.ip.rule('add', 15, 32006, fwmark=10) assert len([ x for x in self.ip.get_rules() if x.get_attr('RTA_PRIORITY') == 32006 and x.get_attr('RTA_TABLE') == 15 and x.get_attr('RTA_PROTOINFO') ]) == 1 self.ip.rule('delete', 15, 32006, fwmark=10) assert len([ x for x in self.ip.get_rules() if x.get_attr('RTA_PRIORITY') == 32006 and x.get_attr('RTA_TABLE') == 15 and x.get_attr('RTA_PROTOINFO') ]) == 0 def test_bad_table(self): try: self.ip.rule('add', -1, 32000) except ValueError: pass def test_big_table(self): self.ip.rule('add', 1024, 32000) assert len([ x for x in self.ip.get_rules() if x.get_attr('RTA_PRIORITY') == 32000 and x.get_attr('RTA_TABLE') == 1024 ]) == 1 self.ip.rule('delete', 1024, 32000) assert len([ x for x in self.ip.get_rules() if x.get_attr('RTA_PRIORITY') == 32000 and x.get_attr('RTA_TABLE') == 1024 ]) == 0 def test_src_dst(self): self.ip.rule('add', 17, 32005, src='10.0.0.0', src_len=24, dst='10.1.0.0', dst_len=24) assert len([ x for x in self.ip.get_rules() if x.get_attr('RTA_PRIORITY') == 32005 and x.get_attr( 'RTA_TABLE') == 17 and x.get_attr('RTA_SRC') == '10.0.0.0' and x.get_attr('RTA_DST') == '10.1.0.0' and x['src_len'] == 24 and x['dst_len'] == 24 ]) == 1 self.ip.rule('delete', 17, 32005, src='10.0.0.0', src_len=24, dst='10.1.0.0', dst_len=24) assert len([ x for x in self.ip.get_rules() if x.get_attr('RTA_PRIORITY') == 32005 and x.get_attr( 'RTA_TABLE') == 17 and x.get_attr('RTA_SRC') == '10.0.0.0' and x.get_attr('RTA_DST') == '10.1.0.0' and x['src_len'] == 24 and x['dst_len'] == 24 ]) == 0
class TestIPRoute(object): def setup(self): self.ip = IPRoute() try: self.dev, idx = self.create() self.ifaces = [idx] except IndexError: pass def create(self, kind='dummy'): name = uifname() create_link(name, kind=kind) idx = self.ip.link_lookup(ifname=name)[0] return (name, idx) def teardown(self): if hasattr(self, 'ifaces'): for dev in self.ifaces: try: self.ip.link('delete', index=dev) except: pass self.ip.close() def _test_nla_operators(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) r = [x for x in self.ip.get_addr() if x['index'] == self.ifaces[0]] complement = r[0] - r[1] intersection = r[0] & r[1] assert complement.get_attr('IFA_ADDRESS') == '172.16.0.1' assert complement.get_attr('IFA_LABEL') is None assert complement['prefixlen'] == 0 assert complement['index'] == 0 assert intersection.get_attr('IFA_ADDRESS') is None assert intersection.get_attr('IFA_LABEL') == self.dev assert intersection['prefixlen'] == 24 assert intersection['index'] == self.ifaces[0] def test_addr_add(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) assert '172.16.0.1/24' in get_ip_addr() def test_flush_addr(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.1.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.1.2', mask=24) assert len(self.ip.get_addr(index=self.ifaces[0], family=socket.AF_INET)) == 4 self.ip.flush_addr(index=self.ifaces[0]) assert len(self.ip.get_addr(index=self.ifaces[0], family=socket.AF_INET)) == 0 def test_flush_rules(self): require_user('root') init = len(self.ip.get_rules(family=socket.AF_INET)) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 self.ip.rule('add', table=10, priority=110) self.ip.rule('add', table=15, priority=150, action='FR_ACT_PROHIBIT') self.ip.rule('add', table=20, priority=200, src='172.16.200.1') self.ip.rule('add', table=25, priority=250, dst='172.16.250.1') assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 4 assert len(self.ip.get_rules(src='172.16.200.1')) == 1 assert len(self.ip.get_rules(dst='172.16.250.1')) == 1 self.ip.flush_rules(family=socket.AF_INET, priority=lambda x: 100 < x < 500) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 assert len(self.ip.get_rules(src='172.16.200.1')) == 0 assert len(self.ip.get_rules(dst='172.16.250.1')) == 0 assert len(self.ip.get_rules(family=socket.AF_INET)) == init def test_rules_deprecated(self): require_user('root') init = len(self.ip.get_rules(family=socket.AF_INET)) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 self.ip.rule('add', 10, 110) self.ip.rule('add', 15, 150, 'FR_ACT_PROHIBIT') assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 2 self.ip.flush_rules(family=socket.AF_INET, priority=lambda x: 100 < x < 500) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 assert len(self.ip.get_rules(family=socket.AF_INET)) == init def test_addr_filter(self): require_user('root') self.ip.addr('add', index=self.ifaces[0], address='172.16.0.1', prefixlen=24, broadcast='172.16.0.255') self.ip.addr('add', index=self.ifaces[0], address='172.16.0.2', prefixlen=24, broadcast='172.16.0.255') assert len(self.ip.get_addr(index=self.ifaces[0])) == 2 assert len(self.ip.get_addr(address='172.16.0.1')) == 1 assert len(self.ip.get_addr(broadcast='172.16.0.255')) == 2 assert len(self.ip.get_addr(match=lambda x: x['index'] == self.ifaces[0])) == 2 @skip_if_not_supported def _create(self, kind): name = uifname() self.ip.link_create(ifname=name, kind=kind) devs = self.ip.link_lookup(ifname=name) assert devs self.ifaces.extend(devs) def test_create_dummy(self): require_user('root') self._create('dummy') def test_create_bond(self): require_user('root') self._create('bond') def test_create_bridge(self): require_user('root') self._create('bridge') def test_create_ovs_bridge(self): require_user('root') self._create('ovs-bridge') def test_create_team(self): require_user('root') self._create('team') def test_ntables(self): setA = set(filter(lambda x: x is not None, [x.get_attr('NDTA_PARMS').get_attr('NDTPA_IFINDEX') for x in self.ip.get_ntables()])) setB = set([x['index'] for x in self.ip.get_links()]) assert setA == setB def test_neigh_real_links(self): links = set([x['index'] for x in self.ip.get_links()]) neigh = set([x['ifindex'] for x in self.ip.get_neighbours()]) assert neigh < links def test_neigh_filter(self): require_user('root') # inject arp records self.ip.neigh('add', dst='172.16.45.1', lladdr='00:11:22:33:44:55', ifindex=self.ifaces[0]) self.ip.neigh('add', dst='172.16.45.2', lladdr='00:11:22:33:44:55', ifindex=self.ifaces[0]) # assert two arp records on the interface assert len(self.ip.get_neighbours(ifindex=self.ifaces[0])) == 2 # filter by dst assert len(self.ip.get_neighbours(dst='172.16.45.1')) == 1 # filter with lambda assert len(self.ip.get_neighbours(match=lambda x: x['ifindex'] == self.ifaces[0])) == 2 def test_mass_ipv6(self): # # Achtung! This test is time consuming. # It is really time consuming, I'm not not # kidding you. Beware. # require_user('root') base = 'fdb3:84e5:4ff4:55e4::{0}' limit = int(os.environ.get('PYROUTE2_SLIMIT', '0x800'), 16) # add addresses for idx in range(limit): self.ip.addr('add', self.ifaces[0], base.format(hex(idx)[2:]), 48) # assert addresses in two steps, to ease debug addrs = self.ip.get_addr(10) assert len(addrs) >= limit # clean up addresses # # it is not required, but if you don't do that, # you'll get this on the interface removal: # # >> kernel:BUG: soft lockup - CPU#0 stuck for ... # # so, not to scare people, remove addresses gracefully # one by one # # it also verifies all the addresses are in place for idx in reversed(range(limit)): self.ip.addr('delete', self.ifaces[0], base.format(hex(idx)[2:]), 48) def test_fail_not_permitted(self): try: self.ip.addr('add', 1, address='172.16.0.1', mask=24) except NetlinkError as e: if e.code != errno.EPERM: # Operation not permitted raise finally: try: self.ip.addr('delete', 1, address='172.16.0.1', mask=24) except: pass def test_fail_no_such_device(self): require_user('root') dev = sorted([i['index'] for i in self.ip.get_links()])[-1] + 10 try: self.ip.addr('add', dev, address='172.16.0.1', mask=24) except NetlinkError as e: if e.code != errno.ENODEV: # No such device raise def test_remove_link(self): require_user('root') try: self.ip.link_remove(self.ifaces[0]) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 0 def test_route_get_target(self): if not self.ip.get_default_routes(table=254): return rts = self.ip.get_routes(family=socket.AF_INET, dst='8.8.8.8', table=254) assert len(rts) > 0 def test_route_get_by_spec(self): self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', index=self.ifaces[0], address='172.16.60.1', mask=24) self.ip.addr('add', index=self.ifaces[0], address='172.16.61.1', mask=24) rts = self.ip.get_routes(family=socket.AF_INET, dst=lambda x: x in ('172.16.60.0', '172.16.61.0')) assert len(rts) == 4 def test_route_multipath(self): require_user('root') self.ip.route('add', dst='172.16.241.0', mask=24, multipath=[{'hops': 20, 'ifindex': 1, 'attrs': [['RTA_GATEWAY', '127.0.0.2']]}, {'hops': 30, 'ifindex': 1, 'attrs': [['RTA_GATEWAY', '127.0.0.3']]}]) assert grep('ip route show', pattern='172.16.241.0/24') assert grep('ip route show', pattern='nexthop.*127.0.0.2.*weight 21') assert grep('ip route show', pattern='nexthop.*127.0.0.3.*weight 31') self.ip.route('del', dst='172.16.241.0', mask=24) def test_route_multipath_helper(self): require_user('root') req = IPRouteRequest({'dst': '172.16.242.0/24', 'multipath': [{'hops': 20, 'ifindex': 1, 'gateway': '127.0.0.2'}, {'hops': 30, 'ifindex': 1, 'gateway': '127.0.0.3'}]}) self.ip.route('add', **req) assert grep('ip route show', pattern='172.16.242.0/24') assert grep('ip route show', pattern='nexthop.*127.0.0.2.*weight 21') assert grep('ip route show', pattern='nexthop.*127.0.0.3.*weight 31') self.ip.route('del', dst='172.16.242.0', mask=24) def test_route_change_existing(self): # route('replace', ...) should succeed, if route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) self.ip.route('add', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.route('change', dst='172.16.1.0', mask=24, gateway='172.16.0.2', table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') def test_route_change_not_existing_fail(self): # route('change', ...) should fail, if no route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') try: self.ip.route('change', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) except NetlinkError as e: if e.code != errno.ENOENT: raise def test_route_replace_existing(self): # route('replace', ...) should succeed, if route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) self.ip.route('replace', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.route('replace', dst='172.16.1.0', mask=24, gateway='172.16.0.2', table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') def test_route_replace_not_existing(self): # route('replace', ...) should succeed, if route doesn't exist require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('replace', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') def test_flush_routes(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('add', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) self.ip.route('add', dst='172.16.2.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.2.0/24.*172.16.0.1') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert not grep('ip route show table 100', pattern='172.16.2.0/24.*172.16.0.1') def test_route_table_2048(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('add', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=2048) assert grep('ip route show table 2048', pattern='172.16.1.0/24.*172.16.0.1') remove_link('bala') def test_symbolic_flags_ifaddrmsg(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], '172.16.1.1', 24) addr = [x for x in self.ip.get_addr() if x.get_attr('IFA_LOCAL') == '172.16.1.1'][0] assert 'IFA_F_PERMANENT' in addr.flags2names(addr['flags']) def test_symbolic_flags_ifinfmsg(self): require_user('root') self.ip.link('set', index=self.ifaces[0], flags=['IFF_UP']) iface = self.ip.get_links(self.ifaces[0])[0] assert iface['flags'] & 1 assert 'IFF_UP' in iface.flags2names(iface['flags']) self.ip.link('set', index=self.ifaces[0], flags=['!IFF_UP']) assert not (self.ip.get_links(self.ifaces[0])[0]['flags'] & 1) def test_updown_link(self): require_user('root') try: self.ip.link_up(*self.ifaces) except NetlinkError: pass assert self.ip.get_links(*self.ifaces)[0]['flags'] & 1 try: self.ip.link_down(*self.ifaces) except NetlinkError: pass assert not (self.ip.get_links(*self.ifaces)[0]['flags'] & 1) def test_callbacks_positive(self): require_user('root') dev = self.ifaces[0] self.cb_counter = 0 self.ip.register_callback(_callback, lambda x: x.get('index', None) == dev, (self, )) self.test_updown_link() assert self.cb_counter > 0 self.ip.unregister_callback(_callback) def test_callbacks_negative(self): require_user('root') self.cb_counter = 0 self.ip.register_callback(_callback, lambda x: x.get('index', None) == -1, (self, )) self.test_updown_link() assert self.cb_counter == 0 self.ip.unregister_callback(_callback) def test_rename_link(self): require_user('root') dev = self.ifaces[0] try: self.ip.link_rename(dev, 'bala') except NetlinkError: pass assert len(self.ip.link_lookup(ifname='bala')) == 1 try: self.ip.link_rename(dev, self.dev) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 1 def test_rules(self): assert len(get_ip_rules('-4')) == \ len(self.ip.get_rules(socket.AF_INET)) assert len(get_ip_rules('-6')) == \ len(self.ip.get_rules(socket.AF_INET6)) def test_addr(self): assert len(get_ip_addr()) == len(self.ip.get_addr()) def test_links(self): assert len(get_ip_link()) == len(self.ip.get_links()) def test_one_link(self): lo = self.ip.get_links(1)[0] assert lo.get_attr('IFLA_IFNAME') == 'lo' def test_default_routes(self): assert len(get_ip_default_routes()) == \ len(self.ip.get_default_routes(family=socket.AF_INET, table=254)) def test_routes(self): assert len(get_ip_route()) == \ len(self.ip.get_routes(family=socket.AF_INET, table=255))
class Monitor(object): """Network status change monitor Monitor is an event drive network status monitor, it will trigger a read signal only if network has any change. This is a netlink API wrapper for fluxmonitord watcher. We implement a fake netlink API for darwin.""" def __init__(self, cb=None): self.callback = cb self.ipr = IPRoute() self.ipr.bind(groups=BIND_GROUPS) def fileno(self): return self.ipr.fileno() # Trigger when self.ipr has data in buffer (Call by event looper) def on_read(self, sender): # Read all message and drop it. Because it is hard to analyze incomming # message, we will query full information and collect it instead. for item in self.ipr.get(): if item.get('event') not in [ "RTM_NEWNEIGH", "RTM_NEWROUTE", "RTM_DELROUTE", "RTM_GETROUTE", ]: # Update status only if event is not RTM_NEWNEIGH or we will # get many dummy messages. status = self.full_status() self.callback(status) return def read(self): for change in self.ipr.get(): if change.get('event') not in [ "RTM_NEWNEIGH", "RTM_NEWROUTE", "RTM_DELROUTE", "RTM_GETROUTE", ]: logger.debug("NW EVENT: %s", change["event"]) return True return False # Query full network information and collect it to flux internal pattern. def full_status(self): status = {} for nic in self.ipr.get_links(): info = dict(nic['attrs']) ifname = info.get('IFLA_IFNAME', 'lo') if ifname.startswith('lo') or ifname.startswith("mon."): continue ifindex = nic.get('index', -1) ifmac = info.get('IFLA_ADDRESS', '??') ifstatus = info.get('IFLA_OPERSTATE', '??') ifcarrier = info.get('IFLA_CARRIER', 0) == 1 st = { 'ifindex': ifindex, 'ifmac': ifmac, 'ifstatus': ifstatus, 'ifcarrier': ifcarrier, 'ipaddr': [] } status[ifname] = st for addr in self.ipr.get_addr(): info = dict(addr['attrs']) ifname = info.get('IFA_LABEL', 'lo') ifname = ifname.split(':')[0] if ifname in status: status[ifname]['ipaddr'].append([ info.get('IFA_ADDRESS', '0.0.0.0'), addr.get('prefixlen', 32) ]) return status def get_ipaddresses(self, ifname="wlan0"): addresses = [] for addr in self.ipr.get_addr(): info = dict(addr['attrs']) if info.get("IFA_LABEL") == ifname: addr = info.get('IFA_ADDRESS') if addr and addr != "127.0.0.1" and addr != "::1": addresses.append(addr) return addresses def get_all_ipaddresses(self): addresses = {} for addr in self.ipr.get_addr(): info = dict(addr['attrs']) ifname = info.get("IFA_LABEL") if ifname and ifname != "lo": addr = info.get('IFA_ADDRESS') if addr and addr != "127.0.0.1" and addr != "::1": addresses[ifname] = addr return addresses def get_all_links(self): linkaddr = {} for addr in self.ipr.get_links(): info = dict(addr['attrs']) ifname = info.get("IFLA_IFNAME") if ifname and ifname != "lo": addr = info.get('IFLA_ADDRESS') if addr: linkaddr[ifname] = addr return linkaddr def close(self): self.ipr.close()
class TestIPRoute(object): ipnets = [] ipranges = [] ifnames = [] def setup(self): self.ip = IPRoute() self.ipnets = [allocate_network() for _ in range(3)] self.ipranges = [[str(x) for x in net] for net in self.ipnets] self.ifaces = [] self.ifnames = [] try: self.dev, idx = self.create() except IndexError: pass def create(self, kind='dummy'): require_user('root') name = uifname() self.ip.link('add', ifname=name, kind=kind) idx = None while not idx: idx = self.ip.link_lookup(ifname=name) idx = idx[0] self.ifaces.append(idx) return (name, idx) def uifname(self): ifname = uifname() self.ifnames.append(ifname) return ifname def teardown(self): for net in self.ipnets: free_network(net) if hasattr(self, 'ifaces'): for dev in reversed(self.ifaces): try: self.ip.link('delete', index=dev) except: pass for name in reversed(self.ifnames): try: (self .ip .link('del', index=(self .ip .link_lookup(ifname=name)[0]))) except: pass self.ip.close() def ifaddr(self, r=0): return str(self.ipranges[r].pop()) def _test_nla_operators(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) r = [x for x in self.ip.get_addr() if x['index'] == self.ifaces[0]] complement = r[0] - r[1] intersection = r[0] & r[1] assert complement.get_attr('IFA_ADDRESS') == '172.16.0.1' assert complement.get_attr('IFA_LABEL') is None assert complement['prefixlen'] == 0 assert complement['index'] == 0 assert intersection.get_attr('IFA_ADDRESS') is None assert intersection.get_attr('IFA_LABEL') == self.dev assert intersection['prefixlen'] == 24 assert intersection['index'] == self.ifaces[0] def test_addr_add(self): require_user('root') ifaddr = self.ifaddr() self.ip.addr('add', self.ifaces[0], address=ifaddr, mask=24) assert '{0}/24'.format(ifaddr) in get_ip_addr() def test_vlan_filter_dump(self): require_user('root') (an, ax) = self.create('bridge') (bn, bx) = self.create('bridge') self.ip.link('set', index=ax, state='up') self.ip.link('set', index=bx, state='up') assert len(self.ip.get_vlans()) >= 2 for name in (an, bn): assert len(self.ip.get_vlans(ifname=name)) == 1 assert (self .ip .get_vlans(ifname=name)[0] .get_attr('IFLA_IFNAME')) == name assert (self .ip .get_vlans(ifname=name)[0] .get_nested('IFLA_AF_SPEC', 'IFLA_BRIDGE_VLAN_INFO'))['vid'] == 1 def test_vlan_filter_add(self): require_user('root') (bn, bx) = self.create('bridge') (sn, sx) = self.create('dummy') self.ip.link('set', index=sx, master=bx) assert not grep('bridge vlan show', pattern=' 568') self.ip.vlan_filter('add', index=sx, vlan_info={'vid': 568}) assert grep('bridge vlan show', pattern=' 568') self.ip.vlan_filter('del', index=sx, vlan_info={'vid': 568}) assert not grep('bridge vlan show', pattern=' 568') def test_vlan_filter_add_raw(self): require_user('root') (bn, bx) = self.create('bridge') (sn, sx) = self.create('dummy') self.ip.link('set', index=sx, master=bx) assert not grep('bridge vlan show', pattern=' 567') self.ip.vlan_filter('add', index=sx, af_spec={'attrs': [['IFLA_BRIDGE_VLAN_INFO', {'vid': 567}]]}) assert grep('bridge vlan show', pattern=' 567') self.ip.vlan_filter('del', index=sx, af_spec={'attrs': [['IFLA_BRIDGE_VLAN_INFO', {'vid': 567}]]}) assert not grep('bridge vlan show', pattern=' 567') def test_brport_basic(self): require_user('root') (bn, bx) = self.create('bridge') (sn, sx) = self.create('dummy') self.ip.link('set', index=sx, master=bx) self.ip.link('set', index=sx, state='up') self.ip.link('set', index=bx, state='up') self.ip.brport('set', index=sx, unicast_flood=0, cost=200, proxyarp=1) port = self.ip.brport('show', index=sx)[0] protinfo = port.get_attr('IFLA_PROTINFO') assert protinfo.get_attr('IFLA_BRPORT_COST') == 200 assert protinfo.get_attr('IFLA_BRPORT_PROXYARP') == 1 assert protinfo.get_attr('IFLA_BRPORT_UNICAST_FLOOD') == 0 def test_local_add(self): require_user('root') ifaddr1 = self.ifaddr() ifaddr2 = self.ifaddr() self.ip.addr('add', self.ifaces[0], address=ifaddr1, local=ifaddr2, mask=24) link = self.ip.get_addr(index=self.ifaces[0])[0] address = link.get_attr('IFA_ADDRESS') local = link.get_attr('IFA_LOCAL') assert address == ifaddr1 assert local == ifaddr2 def test_addr_broadcast(self): require_user('root') ifaddr1 = self.ifaddr() ifaddr2 = self.ifaddr() self.ip.addr('add', self.ifaces[0], address=ifaddr1, mask=24, broadcast=ifaddr2) assert ifaddr2 in get_ip_brd() def test_addr_broadcast_default(self): require_user('root') ifaddr1 = self.ifaddr() # -> 255 ifaddr2 = self.ifaddr() # -> 254 self.ip.addr('add', self.ifaces[0], address=ifaddr2, mask=24, broadcast=True) assert ifaddr1 in get_ip_brd() def test_flush_addr(self): require_user('root') ifaddr1 = self.ifaddr(0) ifaddr2 = self.ifaddr(0) ifaddr3 = self.ifaddr(1) ifaddr4 = self.ifaddr(1) self.ip.addr('add', self.ifaces[0], address=ifaddr1, mask=24) self.ip.addr('add', self.ifaces[0], address=ifaddr2, mask=24) self.ip.addr('add', self.ifaces[0], address=ifaddr3, mask=24) self.ip.addr('add', self.ifaces[0], address=ifaddr4, mask=24) assert len(self.ip.get_addr(index=self.ifaces[0], family=socket.AF_INET)) == 4 self.ip.flush_addr(index=self.ifaces[0]) assert len(self.ip.get_addr(index=self.ifaces[0], family=socket.AF_INET)) == 0 def test_flush_rules(self): require_user('root') ifaddr1 = self.ifaddr(0) ifaddr2 = self.ifaddr(1) init = len(self.ip.get_rules(family=socket.AF_INET)) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 self.ip.rule('add', table=10, priority=110) self.ip.rule('add', table=15, priority=150, action='FR_ACT_PROHIBIT') self.ip.rule('add', table=20, priority=200, src=ifaddr1) self.ip.rule('add', table=25, priority=250, dst=ifaddr2) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 4 assert len(self.ip.get_rules(src=ifaddr1)) == 1 assert len(self.ip.get_rules(dst=ifaddr2)) == 1 self.ip.flush_rules(family=socket.AF_INET, priority=lambda x: 100 < x < 500) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 assert len(self.ip.get_rules(src=ifaddr1)) == 0 assert len(self.ip.get_rules(dst=ifaddr2)) == 0 assert len(self.ip.get_rules(family=socket.AF_INET)) == init def test_rules_deprecated(self): require_user('root') init = len(self.ip.get_rules(family=socket.AF_INET)) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 self.ip.rule('add', 10, 110) self.ip.rule('add', 15, 150, 'FR_ACT_PROHIBIT') assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 2 self.ip.flush_rules(family=socket.AF_INET, priority=lambda x: 100 < x < 500) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 assert len(self.ip.get_rules(family=socket.AF_INET)) == init def test_match_callable(self): assert len(self.ip.get_links(match=partial(lambda x: x))) > 0 def test_addr_filter(self): require_user('root') ifaddr_brd = self.ifaddr() ifaddr1 = self.ifaddr() ifaddr2 = self.ifaddr() self.ip.addr('add', index=self.ifaces[0], address=ifaddr1, prefixlen=24, broadcast=ifaddr_brd) self.ip.addr('add', index=self.ifaces[0], address=ifaddr2, prefixlen=24, broadcast=ifaddr_brd) assert len(self.ip.get_addr(index=self.ifaces[0])) == 2 assert len(self.ip.get_addr(address=ifaddr1)) == 1 assert len(self.ip.get_addr(broadcast=ifaddr_brd)) == 2 assert len(self.ip.get_addr(match=lambda x: x['index'] == self.ifaces[0])) == 2 @skip_if_not_supported def _create_ipvlan(self, smode): require_user('root') master = self.uifname() ipvlan = self.uifname() # create the master link self.ip.link('add', ifname=master, kind='dummy') midx = self.ip.link_lookup(ifname=master)[0] # check modes # maybe move modes dict somewhere else? cmode = ifinfmsg.ifinfo.data_map['ipvlan'].modes[smode] assert ifinfmsg.ifinfo.data_map['ipvlan'].modes[cmode] == smode # create ipvlan self.ip.link('add', ifname=ipvlan, kind='ipvlan', link=midx, mode=cmode) devs = self.ip.link_lookup(ifname=ipvlan) assert devs def test_create_ipvlan_l2(self): return self._create_ipvlan('IPVLAN_MODE_L2') def test_create_ipvlan_l3(self): return self._create_ipvlan('IPVLAN_MODE_L3') def _create_veth(self, peer): ifname = self.uifname() self.ip.link('add', kind='veth', ifname=ifname, peer=peer) assert len(self.ip.link_lookup(ifname=ifname)) > 0 return ifname def test_create_veth_simple(self): require_user('root') peer = self.uifname() self._create_veth(peer) assert len(self.ip.link_lookup(ifname=peer)) > 0 def test_create_veth_attrs(self): require_user('root') nsname = str(uuid.uuid4()) netns = NetNS(nsname) try: peer = {'ifname': self.uifname(), 'net_ns_fd': nsname} self._create_veth(peer) assert len(self.ip.link_lookup(ifname=peer['ifname'])) == 0 assert len(netns.link_lookup(ifname=peer['ifname'])) > 0 finally: netns.close() netns.remove() def test_get_netns_info(self): require_user('root') nsname = str(uuid.uuid4()) netns = NetNS(nsname) try: peer = {'ifname': self.uifname(), 'net_ns_fd': nsname} ifname = self._create_veth(peer) # get veth veth = self.ip.link('get', ifname=ifname)[0] target = veth.get_attr('IFLA_LINK_NETNSID') for info in self.ip.get_netns_info(): path = info.get_attr('NSINFO_PATH') assert path.endswith(nsname) netnsid = info['netnsid'] if target == netnsid: break else: raise KeyError('peer netns not found') finally: netns.close() netns.remove() @skip_if_not_supported def _create(self, kind, **kwarg): name = self.uifname() self.ip.link('add', ifname=name, kind=kind, **kwarg) devs = self.ip.link_lookup(ifname=name) assert devs return (name, devs[0]) def test_create_dummy(self): require_user('root') self._create('dummy') def test_create_bond(self): require_user('root') self._create('bond') def test_create_bridge(self): require_user('root') self._create('bridge') def test_create_team(self): require_user('root') self._create('team') def test_create_vti(self): require_user('root') ifaddr1 = self.ifaddr() ifaddr2 = self.ifaddr() (ifname, idx) = self.create() self.ip.link('set', index=idx, state='up') self.ip.addr('add', index=idx, address=ifaddr1, mask=24) self._create('vti', vti_link=idx, vti_local=ifaddr1, vti_remote=ifaddr2, vti_ikey=64, vti_okey=72) def test_create_vti6(self): require_user('root') self._create('vti6', vti_link=2, vti_local='fd00:1:2:3:4:1::1', vti_remote='fd00:1:2:3:4:2::1', vti_ikey=80, vti_okey=88) def test_create_xfrm(self): require_user('root') # XXX: Currently does not work on top of a dummy device idx = self.ip.link_lookup(ifname='lo')[0] # # Create Dummy for Parent # (_, idx) = self.create() self.ip.link('set', index=idx, state='up') # Create XFRM Interface on It self._create('xfrm', xfrm_link=idx, xfrm_if_id=555) def _test_ntables(self): setA = set(filter(lambda x: x is not None, [x.get_attr('NDTA_PARMS').get_attr('NDTPA_IFINDEX') for x in self.ip.get_ntables()])) setB = set([x['index'] for x in self.ip.get_links()]) assert setA == setB def test_fdb_vxlan(self): require_kernel(4, 4) require_user('root') ifaddr = self.ifaddr() # create dummy (dn, dx) = self._create('dummy') # create vxlan on it (vn, vx) = self._create('vxlan', vxlan_link=dx, vxlan_id=500) # create FDB record l2 = '00:11:22:33:44:55' self.ip.fdb('add', lladdr=l2, ifindex=vx, vni=600, port=5678, dst=ifaddr) # dump r = self.ip.fdb('dump', ifindex=vx, lladdr=l2) assert len(r) == 1 assert r[0]['ifindex'] == vx assert r[0].get_attr('NDA_LLADDR') == l2 assert r[0].get_attr('NDA_DST') == ifaddr assert r[0].get_attr('NDA_PORT') == 5678 assert r[0].get_attr('NDA_VNI') == 600 def test_fdb_bridge_simple(self): require_kernel(4, 4) require_user('root') require_kernel(4, 4) # create bridge (bn, bx) = self._create('bridge') # create FDB record l2 = '00:11:22:33:44:55' self.ip.fdb('add', lladdr=l2, ifindex=bx) # dump FDB r = self.ip.fdb('dump', ifindex=bx, lladdr=l2) # one vlan == 1, one w/o vlan assert len(r) == 2 assert len(list(filter(lambda x: x['ifindex'] == bx, r))) == 2 assert len(list(filter(lambda x: x.get_attr('NDA_VLAN'), r))) == 1 assert len(list(filter(lambda x: x.get_attr('NDA_MASTER') == bx, r))) == 2 assert len(list(filter(lambda x: x.get_attr('NDA_LLADDR') == l2, r))) == 2 r = self.ip.fdb('dump', ifindex=bx, lladdr=l2, vlan=1) assert len(r) == 1 assert r[0].get_attr('NDA_VLAN') == 1 assert r[0].get_attr('NDA_MASTER') == bx assert r[0].get_attr('NDA_LLADDR') == l2 def test_neigh_real_links(self): links = set([x['index'] for x in self.ip.get_links()]) neigh = set([x['ifindex'] for x in self.ip.get_neighbours()]) assert neigh < links def test_neigh_filter(self): require_user('root') ifaddr1 = self.ifaddr(1) ifaddr2 = self.ifaddr(1) # inject arp records self.ip.neigh('add', dst=ifaddr1, lladdr='00:11:22:33:44:55', ifindex=self.ifaces[0]) self.ip.neigh('add', dst=ifaddr2, lladdr='00:11:22:33:44:55', ifindex=self.ifaces[0]) # assert two arp records on the interface assert len(self.ip.get_neighbours(ifindex=self.ifaces[0])) == 2 # filter by dst assert len(self.ip.get_neighbours(dst=ifaddr1)) == 1 # filter with lambda assert len(self.ip.get_neighbours(match=lambda x: x['ifindex'] == self.ifaces[0])) == 2 def test_mass_ipv6(self): # # Achtung! This test is time consuming. # It is really time consuming, I'm not not # kidding you. Beware. # require_user('root') ipv6net = allocate_network('ipv6') base = str(ipv6net.network) + '{0}' limit = int(os.environ.get('PYROUTE2_SLIMIT', '0x800'), 16) # add addresses for idx in range(limit): self.ip.addr('add', self.ifaces[0], base.format(hex(idx)[2:]), 48) # assert addresses in two steps, to ease debug addrs = self.ip.get_addr(10) assert len(addrs) >= limit # clean up addresses # # it is not required, but if you don't do that, # you'll get this on the interface removal: # # >> kernel:BUG: soft lockup - CPU#0 stuck for ... # # so, not to scare people, remove addresses gracefully # one by one # # it also verifies all the addresses are in place for idx in reversed(range(limit)): self.ip.addr('delete', self.ifaces[0], base.format(hex(idx)[2:]), 48) free_network(ipv6net, 'ipv6') def test_fail_not_permitted(self): ifaddr = self.ifaddr() try: self.ip.addr('add', 1, address=ifaddr, mask=24) except NetlinkError as e: if e.code != errno.EPERM: # Operation not permitted raise finally: try: self.ip.addr('delete', 1, address=ifaddr, mask=24) except: pass def test_fail_no_such_device(self): require_user('root') ifaddr = self.ifaddr() dev = sorted([i['index'] for i in self.ip.get_links()])[-1] + 10 try: self.ip.addr('add', dev, address=ifaddr, mask=24) except NetlinkError as e: if e.code != errno.ENODEV: # No such device raise def test_remove_link(self): require_user('root') try: self.ip.link('del', index=self.ifaces[0]) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 0 def _test_route_proto(self, proto, fake, spec=''): require_user('root') naddr = str(self.ipnets[1].network) os.system('ip route add %s/24 via 127.0.0.1 %s' % (naddr, spec)) time.sleep(1) assert grep('ip ro', pattern='%s/24.*127.0.0.1' % naddr) try: self.ip.route('del', dst='%s/24' % naddr, gateway='127.0.0.1', proto=fake) except NetlinkError: pass self.ip.route('del', dst='%s/24' % naddr, gateway='127.0.0.1', proto=proto) assert not grep('ip ro', pattern='%s/24.*127.0.0.1' % naddr) def test_route_proto_static(self): return self._test_route_proto('static', 'boot', 'proto static') def test_route_proto_static_num(self): return self._test_route_proto(4, 3, 'proto static') def test_route_proto_boot(self): return self._test_route_proto('boot', 4) def test_route_proto_boot_num(self): return self._test_route_proto(3, 'static') def test_route_oif_as_iterable(self): require_user('root') naddr = str(self.ipnets[1].network) spec = {'dst': naddr, 'dst_len': 24, 'oif': (1, )} self.ip.route('add', **spec) rts = self.ip.get_routes(family=socket.AF_INET, dst=naddr) self.ip.route('del', **spec) assert len(rts) == 1 assert rts[0].get_attr('RTA_OIF') == 1 def test_route_get_target(self): if not self.ip.get_default_routes(table=254): raise SkipTest('no default IPv4 routes') rts = self.ip.get_routes(family=socket.AF_INET, dst='8.8.8.8', table=254) assert len(rts) > 0 def test_route_get_target_default_ipv4(self): rts = self.ip.get_routes(dst='127.0.0.1') assert len(rts) > 0 def test_route_get_target_default_ipv6(self): rts = self.ip.get_routes(dst='::1') assert len(rts) > 0 def test_route_get_by_spec(self): require_user('root') ifaddr1 = self.ifaddr(0) ifaddr2 = self.ifaddr(1) self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', index=self.ifaces[0], address=ifaddr1, mask=24) self.ip.addr('add', index=self.ifaces[0], address=ifaddr2, mask=24) rts = self.ip.get_routes(family=socket.AF_INET, dst=lambda x: x in (ifaddr1, ifaddr2)) assert len(rts) == 4 @skip_if_not_supported def _test_route_mpls_via_ipv(self, family, address, label): require_kernel(4, 4) require_user('root') self.ip.route('add', **{'family': AF_MPLS, 'oif': self.ifaces[0], 'via': {'family': family, 'addr': address}, 'newdst': {'label': label, 'bos': 1}}) rt = self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)[0] assert rt.get_attr('RTA_VIA')['addr'] == address assert rt.get_attr('RTA_VIA')['family'] == family assert rt.get_attr('RTA_NEWDST')[0]['label'] == label assert len(rt.get_attr('RTA_NEWDST')) == 1 self.ip.route('del', **{'family': AF_MPLS, 'oif': self.ifaces[0], 'dst': {'label': 0x10, 'bos': 1}, 'via': {'family': family, 'addr': address}, 'newdst': {'label': label, 'bos': 1}}) assert len(self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)) == 0 def test_route_mpls_via_ipv4(self): ifaddr = self.ifaddr() self._test_route_mpls_via_ipv(socket.AF_INET, ifaddr, 0x20) def test_route_mpls_via_ipv6(self): ipv6net = allocate_network('ipv6') address = str(ipv6net.network) + '7c32' self._test_route_mpls_via_ipv(socket.AF_INET6, address, 0x20) free_network(ipv6net, 'ipv6') @skip_if_not_supported def test_route_mpls_swap_newdst_simple(self): require_kernel(4, 4) require_user('root') req = {'family': AF_MPLS, 'oif': self.ifaces[0], 'dst': {'label': 0x20, 'bos': 1}, 'newdst': {'label': 0x21, 'bos': 1}} self.ip.route('add', **req) rt = self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)[0] assert rt.get_attr('RTA_DST')[0]['label'] == 0x20 assert len(rt.get_attr('RTA_DST')) == 1 assert rt.get_attr('RTA_NEWDST')[0]['label'] == 0x21 assert len(rt.get_attr('RTA_NEWDST')) == 1 self.ip.route('del', **req) assert len(self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)) == 0 @skip_if_not_supported def test_route_mpls_swap_newdst_list(self): require_kernel(4, 4) require_user('root') req = {'family': AF_MPLS, 'oif': self.ifaces[0], 'dst': {'label': 0x20, 'bos': 1}, 'newdst': [{'label': 0x21, 'bos': 1}]} self.ip.route('add', **req) rt = self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)[0] assert rt.get_attr('RTA_DST')[0]['label'] == 0x20 assert len(rt.get_attr('RTA_DST')) == 1 assert rt.get_attr('RTA_NEWDST')[0]['label'] == 0x21 assert len(rt.get_attr('RTA_NEWDST')) == 1 self.ip.route('del', **req) assert len(self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)) == 0 def test_route_multipath_raw(self): require_user('root') naddr = str(self.ipnets[1].network) self.ip.route('add', dst=naddr, mask=24, multipath=[{'hops': 20, 'oif': 1, 'attrs': [['RTA_GATEWAY', '127.0.0.2']]}, {'hops': 30, 'oif': 1, 'attrs': [['RTA_GATEWAY', '127.0.0.3']]}]) assert grep('ip route show', pattern='%s/24' % naddr) assert grep('ip route show', pattern='nexthop.*127.0.0.2.*weight 21') assert grep('ip route show', pattern='nexthop.*127.0.0.3.*weight 31') self.ip.route('del', dst=naddr, mask=24) def test_route_multipath_helper(self): require_user('root') naddr = str(self.ipnets[1].network) req = IPRouteRequest({'dst': '%s/24' % naddr, 'multipath': [{'hops': 20, 'oif': 1, 'gateway': '127.0.0.2'}, {'hops': 30, 'oif': 1, 'gateway': '127.0.0.3'}]}) self.ip.route('add', **req) assert grep('ip route show', pattern='%s/24' % naddr) assert grep('ip route show', pattern='nexthop.*127.0.0.2.*weight 21') assert grep('ip route show', pattern='nexthop.*127.0.0.3.*weight 31') self.ip.route('del', dst=naddr, mask=24) def test_route_multipath(self): require_user('root') naddr = str(self.ipnets[1].network) self.ip.route('add', dst='%s/24' % naddr, multipath=[{'gateway': '127.0.0.2'}, {'gateway': '127.0.0.3'}]) assert grep('ip route show', pattern='%s/24' % naddr) assert grep('ip route show', pattern='nexthop.*127.0.0.2') assert grep('ip route show', pattern='nexthop.*127.0.0.3') self.ip.route('del', dst=naddr, mask=24) def test_route_onlink(self): naddr = str(self.ipnets[1].network) ifaddr = self.ifaddr(0) require_user('root') self.ip.route('add', dst='%s/24' % naddr, gateway=ifaddr, oif=1, flags=RTNH_F_ONLINK) assert grep('ip route show', pattern='%s.*onlink' % ifaddr) self.ip.route('del', dst='%s/24' % naddr) def test_route_onlink_multipath(self): require_user('root') naddr = str(self.ipnets[1].network) ifaddr1 = self.ifaddr() ifaddr2 = self.ifaddr() self.ip.route('add', dst='%s/24' % naddr, multipath=[{'gateway': ifaddr1, 'oif': 1, 'flags': RTNH_F_ONLINK}, {'gateway': ifaddr2, 'oif': 1, 'flags': RTNH_F_ONLINK}]) assert grep('ip route show', pattern='%s/24' % naddr) assert grep('ip route show', pattern='nexthop.*%s.*onlink' % ifaddr1) assert grep('ip route show', pattern='nexthop.*%s.*onlink' % ifaddr2) self.ip.route('del', dst=naddr, mask=24) def test_route_onlink_strflags(self): require_user('root') naddr = str(self.ipnets[1].network) ifaddr = self.ifaddr() self.ip.route('add', dst='%s/24' % naddr, gateway=ifaddr, oif=1, flags=['onlink']) assert grep('ip route show', pattern='%s.*onlink' % ifaddr) self.ip.route('del', dst='%s/24' % naddr) def test_route_onlink_multipath_strflags(self): require_user('root') naddr = str(self.ipnets[1].network) ifaddr1 = self.ifaddr() ifaddr2 = self.ifaddr() self.ip.route('add', dst='%s/24' % naddr, multipath=[{'gateway': ifaddr1, 'oif': 1, 'flags': ['onlink']}, {'gateway': ifaddr2, 'oif': 1, 'flags': RTNH_F_ONLINK}]) assert grep('ip route show', pattern='%s/24' % naddr) assert grep('ip route show', pattern='nexthop.*%s.*onlink' % ifaddr1) assert grep('ip route show', pattern='nexthop.*%s.*onlink' % ifaddr2) self.ip.route('del', dst=naddr, mask=24) @skip_if_not_supported def test_lwtunnel_multipath_mpls(self): require_kernel(4, 4) require_user('root') require_kernel(4, 5) naddr = str(self.ipnets[1].network) self.ip.route('add', dst='%s/24' % naddr, multipath=[{'encap': {'type': 'mpls', 'labels': 500}, 'oif': 1}, {'encap': {'type': 'mpls', 'labels': '600/700'}, 'gateway': '127.0.0.4'}]) routes = self.ip.route('dump', dst='%s/24' % naddr) assert len(routes) == 1 mp = routes[0].get_attr('RTA_MULTIPATH') assert len(mp) == 2 assert mp[0]['oif'] == 1 assert mp[0].get_attr('RTA_ENCAP_TYPE') == 1 labels = mp[0].get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 1 assert labels[0]['bos'] == 1 assert labels[0]['label'] == 500 assert mp[1].get_attr('RTA_ENCAP_TYPE') == 1 labels = mp[1].get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 2 assert labels[0]['bos'] == 0 assert labels[0]['label'] == 600 assert labels[1]['bos'] == 1 assert labels[1]['label'] == 700 self.ip.route('del', dst='%s/24' % naddr) @skip_if_not_supported def test_lwtunnel_mpls_dict_label(self): require_kernel(4, 4) require_user('root') require_kernel(4, 3) naddr = str(self.ipnets[1].network) self.ip.route('add', dst='%s/24' % naddr, encap={'type': 'mpls', 'labels': [{'bos': 0, 'label': 226}, {'bos': 1, 'label': 227}]}, gateway='127.0.0.2') routes = self.ip.route('dump', dst='%s/24' % naddr) assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_GATEWAY') == '127.0.0.2' labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 2 assert labels[0]['bos'] == 0 assert labels[0]['label'] == 226 assert labels[1]['bos'] == 1 assert labels[1]['label'] == 227 self.ip.route('del', dst='%s/24' % naddr) @skip_if_not_supported def test_lwtunnel_mpls_2_int_label(self): require_kernel(4, 4) require_user('root') require_kernel(4, 3) naddr = str(self.ipnets[1].network) self.ip.route('add', dst='%s/24' % naddr, encap={'type': 'mpls', 'labels': [206, 207]}, oif=1) routes = self.ip.route('dump', dst='%s/24' % naddr) assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_OIF') == 1 labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 2 assert labels[0]['bos'] == 0 assert labels[0]['label'] == 206 assert labels[1]['bos'] == 1 assert labels[1]['label'] == 207 self.ip.route('del', dst='%s/24' % naddr) @skip_if_not_supported def test_lwtunnel_mpls_2_str_label(self): require_kernel(4, 4) require_user('root') require_kernel(4, 3) naddr = str(self.ipnets[1].network) self.ip.route('add', dst='%s/24' % naddr, encap={'type': 'mpls', 'labels': "246/247"}, oif=1) routes = self.ip.route('dump', dst='%s/24' % naddr) assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_OIF') == 1 labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 2 assert labels[0]['bos'] == 0 assert labels[0]['label'] == 246 assert labels[1]['bos'] == 1 assert labels[1]['label'] == 247 self.ip.route('del', dst='%s/24' % naddr) @skip_if_not_supported def test_lwtunnel_mpls_1_str_label(self): require_kernel(4, 4) require_user('root') require_kernel(4, 3) naddr = str(self.ipnets[1].network) self.ip.route('add', dst='%s/24' % naddr, encap={'type': 'mpls', 'labels': "244"}, oif=1) routes = self.ip.route('dump', dst='%s/24' % naddr) assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_OIF') == 1 labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 1 assert labels[0]['bos'] == 1 assert labels[0]['label'] == 244 self.ip.route('del', dst='%s/24' % naddr) @skip_if_not_supported def test_lwtunnel_mpls_1_int_label(self): require_kernel(4, 4) require_user('root') require_kernel(4, 3) naddr = str(self.ipnets[1].network) self.ip.route('add', dst='%s/24' % naddr, encap={'type': 'mpls', 'labels': 245}, oif=1) routes = self.ip.route('dump', dst='%s/24' % naddr) assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_OIF') == 1 labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 1 assert labels[0]['bos'] == 1 assert labels[0]['label'] == 245 self.ip.route('del', dst='%s/24' % naddr) def test_route_change_existing(self): # route('replace', ...) should succeed, if route exists require_user('root') naddr = str(self.ipnets[1].network) ifaddr1 = self.ifaddr() ifaddr2 = self.ifaddr() ifaddr3 = self.ifaddr() self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address=ifaddr1, mask=24) self.ip.route('add', dst=naddr, mask=24, gateway=ifaddr2, table=100) assert grep('ip route show table 100', pattern='%s/24.*%s' % (naddr, ifaddr2)) self.ip.route('change', dst=naddr, mask=24, gateway=ifaddr3, table=100) assert not grep('ip route show table 100', pattern='%s/24.*%s' % (naddr, ifaddr2)) assert grep('ip route show table 100', pattern='%s/24.*%s' % (naddr, ifaddr3)) self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='%s/24.*%s' % (naddr, ifaddr3)) def test_route_change_not_existing_fail(self): # route('change', ...) should fail, if no route exists require_user('root') naddr = str(self.ipnets[1].network) ifaddr1 = self.ifaddr() ifaddr2 = self.ifaddr() self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address=ifaddr1, mask=24) assert not grep('ip route show table 100', pattern='%s.*%s' % (naddr, ifaddr2)) try: self.ip.route('change', dst=naddr, mask=24, gateway=ifaddr2, table=100) except NetlinkError as e: if e.code != errno.ENOENT: raise def test_route_replace_existing(self): # route('replace', ...) should succeed, if route exists require_user('root') naddr = str(self.ipnets[1].network) ifaddr1 = self.ifaddr() ifaddr2 = self.ifaddr() ifaddr3 = self.ifaddr() self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address=ifaddr1, mask=24) self.ip.route('replace', dst=naddr, mask=24, gateway=ifaddr2, table=100) assert grep('ip route show table 100', pattern='%s/24.*%s' % (naddr, ifaddr2)) self.ip.route('replace', dst=naddr, mask=24, gateway=ifaddr3, table=100) assert not grep('ip route show table 100', pattern='%s/24.*%s' % (naddr, ifaddr2)) assert grep('ip route show table 100', pattern='%s/24.*%s' % (naddr, ifaddr3)) self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='%s/24.*%s' % (naddr, ifaddr3)) def test_route_replace_not_existing(self): # route('replace', ...) should succeed, if route doesn't exist require_user('root') naddr = str(self.ipnets[1].network) ifaddr1 = self.ifaddr() ifaddr2 = self.ifaddr() self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address=ifaddr1, mask=24) self.ip.route('replace', dst=naddr, mask=24, gateway=ifaddr2, table=100) assert grep('ip route show table 100', pattern='%s/24.*%s' % (naddr, ifaddr2)) self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='%s/24.*%s' % (naddr, ifaddr2)) def test_flush_routes(self): require_user('root') naddr1 = str(self.ipnets[1].network) naddr2 = str(self.ipnets[2].network) ifaddr1 = self.ifaddr() ifaddr2 = self.ifaddr() self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address=ifaddr1, mask=24) self.ip.route('add', dst=naddr1, mask=24, gateway=ifaddr2, table=100) self.ip.route('add', dst=naddr2, mask=24, gateway=ifaddr2, table=100) assert grep('ip route show table 100', pattern='%s/24.*%s' % (naddr1, ifaddr2)) assert grep('ip route show table 100', pattern='%s/24.*%s' % (naddr2, ifaddr2)) self.ip.flush_routes(table=100, family=socket.AF_INET6) assert grep('ip route show table 100', pattern='%s/24.*%s' % (naddr1, ifaddr2)) assert grep('ip route show table 100', pattern='%s/24.*%s' % (naddr2, ifaddr2)) self.ip.flush_routes(table=100, family=socket.AF_INET) assert not grep('ip route show table 100', pattern='%s/24.*%s' % (naddr1, ifaddr2)) assert not grep('ip route show table 100', pattern='%s/24.*%s' % (naddr2, ifaddr2)) def test_route_table_2048(self): require_user('root') naddr = str(self.ipnets[1].network) ifaddr1 = self.ifaddr() ifaddr2 = self.ifaddr() self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address=ifaddr1, mask=24) self.ip.route('add', dst=naddr, mask=24, gateway=ifaddr2, table=2048) assert grep('ip route show table 2048', pattern='%s/24.*%s' % (naddr, ifaddr2)) remove_link('bala') def test_symbolic_flags_ifaddrmsg(self): require_user('root') ifaddr = self.ifaddr() self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], ifaddr, 24) addr = [x for x in self.ip.get_addr() if x.get_attr('IFA_LOCAL') == ifaddr][0] assert 'IFA_F_PERMANENT' in addr.flags2names(addr['flags']) def test_symbolic_flags_ifinfmsg(self): require_user('root') self.ip.link('set', index=self.ifaces[0], flags=['IFF_UP']) iface = self.ip.get_links(self.ifaces[0])[0] assert iface['flags'] & 1 assert 'IFF_UP' in iface.flags2names(iface['flags']) self.ip.link('set', index=self.ifaces[0], flags=['!IFF_UP']) assert not (self.ip.get_links(self.ifaces[0])[0]['flags'] & 1) def test_updown_link(self): require_user('root') try: for i in self.ifaces: self.ip.link('set', index=i, state='up') except NetlinkError: pass assert self.ip.get_links(*self.ifaces)[0]['flags'] & 1 try: for i in self.ifaces: self.ip.link('set', index=i, state='down') except NetlinkError: pass assert not (self.ip.get_links(*self.ifaces)[0]['flags'] & 1) def test_callbacks_positive(self): require_user('root') dev = self.ifaces[0] self.cb_counter = 0 self.ip.register_callback(_callback, lambda x: x.get('index', None) == dev, (self, )) self.test_updown_link() assert self.cb_counter > 0 self.ip.unregister_callback(_callback) def test_callbacks_negative(self): require_user('root') self.cb_counter = 0 self.ip.register_callback(_callback, lambda x: x.get('index', None) == -1, (self, )) self.test_updown_link() assert self.cb_counter == 0 self.ip.unregister_callback(_callback) def test_link_filter(self): links = self.ip.link('dump', ifname='lo') assert len(links) == 1 assert links[0].get_attr('IFLA_IFNAME') == 'lo' def test_link_legacy_nla(self): require_user('root') dev = self.ifaces[0] try: self.ip.link('set', index=dev, state='down') self.ip.link('set', index=dev, IFLA_IFNAME='bala') except NetlinkError: pass assert len(self.ip.link_lookup(ifname='bala')) == 1 try: self.ip.link('set', index=dev, ifname=self.dev) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 1 def test_link_rename(self): require_user('root') dev = self.ifaces[0] try: self.ip.link('set', index=dev, ifname='bala') except NetlinkError: pass assert len(self.ip.link_lookup(ifname='bala')) == 1 try: self.ip.link('set', index=dev, ifname=self.dev) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 1 def test_link_arp_flag(self): require_user('root') dev = self.ifaces[0] # by default dummy interface have NOARP set assert self.ip.get_links(dev)[0]['flags'] & IFF_NOARP self.ip.link('set', index=dev, arp=True) assert not self.ip.get_links(dev)[0]['flags'] & IFF_NOARP self.ip.link('set', index=dev, arp=False) assert self.ip.get_links(dev)[0]['flags'] & IFF_NOARP def test_rules(self): assert len(get_ip_rules('-4')) == \ len(self.ip.get_rules(socket.AF_INET)) assert len(get_ip_rules('-6')) == \ len(self.ip.get_rules(socket.AF_INET6)) def test_one_link(self): lo = self.ip.get_links(1)[0] assert lo.get_attr('IFLA_IFNAME') == 'lo' def test_default_routes(self): assert len(get_ip_default_routes()) == \ len(self.ip.get_default_routes(family=socket.AF_INET, table=254)) def test_routes(self): routes = list(self.ip.get_routes(family=socket.AF_INET, table=255)) assert len(routes) assert all([isinstance(x, dict) for x in routes]) assert all([x['event'] == 'RTM_NEWROUTE' for x in routes])
class TestNSPopen(object): def setup(self): self.ip = IPRoute() self.names = [] def teardown(self): self.ip.close() for ns in self.names: netnsmod.remove(ns) def alloc_nsname(self): nsid = str(uuid4()) self.names.append(nsid) return nsid def test_stdio(self): require_user('root') nsid = self.alloc_nsname() nsp = NSPopen(nsid, ['ip', 'ad'], flags=os.O_CREAT, stdout=subprocess.PIPE) output = nsp.stdout.read() nsp.release() assert output is not None def test_fcntl(self): require_user('root') nsid = self.alloc_nsname() nsp = NSPopen(nsid, ['ip', 'ad'], flags=os.O_CREAT, stdout=subprocess.PIPE) flags = nsp.stdout.fcntl(fcntl.F_GETFL) nsp.release() assert flags == 0 def test_api_class(self): api_nspopen = set(dir(NSPopenDirect)) api_popen = set(dir(subprocess.Popen)) assert api_nspopen & api_popen == api_popen def test_api_object(self): require_user('root') nsid = self.alloc_nsname() nsp = NSPopen(nsid, ['true'], flags=os.O_CREAT, stdout=subprocess.PIPE) smp = subprocess.Popen(['true'], stdout=subprocess.PIPE) nsp.communicate() smp.communicate() api_nspopen = set(dir(nsp)) api_popen = set(dir(smp)) minimal = set(('communicate', 'kill', 'wait')) assert minimal & (api_nspopen & api_popen) == minimal smp.wait() nsp.wait() assert nsp.returncode == smp.returncode == 0 nsp.release() def test_release(self): require_user('root') nsid = self.alloc_nsname() nsp = NSPopen(nsid, ['true'], flags=os.O_CREAT, stdout=subprocess.PIPE) nsp.communicate() nsp.wait() nsp.release() try: print(nsp.returncode) except RuntimeError: pass def test_basic(self): require_user('root') nsid = self.alloc_nsname() # create NS and run a child nsp = NSPopen(nsid, ['ip', '-o', 'link'], stdout=subprocess.PIPE, flags=os.O_CREAT) ret = nsp.communicate()[0].decode('utf-8') host_links = [x.get_attr('IFLA_IFNAME') for x in self.ip.get_links()] netns_links = [x.split(':')[1].split('@')[0].strip() for x in ret.split('\n') if len(x)] assert nsp.wait() == nsp.returncode == 0 assert set(host_links) & set(netns_links) == set(netns_links) assert set(netns_links) < set(host_links) assert not set(netns_links) > set(host_links) nsp.release()
import os from fcntl import ioctl import struct from pyroute2 import IPRoute TUNSETIFF = 0x400454ca IFF_TUN = 0x0001 IFF_NO_PI = 0x1000 ftun = os.open("/dev/net/tun", os.O_RDWR) ioctl(ftun, TUNSETIFF, struct.pack("16sH", b"tun0", IFF_TUN | IFF_NO_PI)) ip = IPRoute() idx = ip.link_lookup(ifname='tun0')[0] ip.addr('add', index=idx, address='192.168.137.1', prefixlen=28) ip.link('set', index=idx, state='up') while True: packet = list(os.read(ftun, 2048)) packet[12:16], packet[16:20] = packet[16:20], packet[12:16] os.write(ftun, ''.join(packet)) ip.close()
def test_freeze(self): require_user('root') interface = self.ip.interfaces[self.ifd] # set up the interface with interface as i: i.add_ip('172.16.0.1/24') i.add_ip('172.16.1.1/24') i.up() # check assert ('172.16.0.1', 24) in interface.ipaddr assert ('172.16.1.1', 24) in interface.ipaddr assert interface.flags & 1 # assert routine def probe(): # The freeze results are dynamic: it is not a real freeze, # it is a restore routine. So it takes time for results # to stabilize err = None for _ in range(3): err = None interface.ipaddr.set_target((('172.16.0.1', 24), ('172.16.1.1', 24))) interface.ipaddr.target.wait() try: assert ('172.16.0.1', 24) in interface.ipaddr assert ('172.16.1.1', 24) in interface.ipaddr assert interface.flags & 1 break except AssertionError as e: err = e continue except Exception as e: err = e break if err is not None: interface.unfreeze() i2.close() raise err # freeze interface.freeze() # change the interface somehow i2 = IPRoute() i2.addr('delete', interface.index, '172.16.0.1', 24) i2.addr('delete', interface.index, '172.16.1.1', 24) probe() # unfreeze self.ip.interfaces[self.ifd].unfreeze() try: i2.addr('delete', interface.index, '172.16.0.1', 24) i2.addr('delete', interface.index, '172.16.1.1', 24) except: pass finally: i2.close() # should be up, but w/o addresses interface.ipaddr.set_target(set()) interface.ipaddr.target.wait(3) assert ('172.16.0.1', 24) not in self.ip.interfaces[self.ifd].ipaddr assert ('172.16.1.1', 24) not in self.ip.interfaces[self.ifd].ipaddr assert self.ip.interfaces[self.ifd].flags & 1
class NetNameSpace(object): """Instantiate a network namespace connected to a bridge Args: log (object): Log """ def __init__(self, name, bridge, addr): """ Args: name (str): namespace name bridge (str): name of bridge to attach to addr (str): cidr of namespace address """ self.log = logger.getlogger() self.addr = addr self.bridge = bridge self.vlan = bridge.split('-')[-1] self.name = name + self.vlan self.ip = IPRoute() self._disconnect_container() self.log.debug('Creating network namespace {}'.format(self.name)) stdout, stderr, rc = sub_proc_exec('ip netns add {}'.format(self.name)) if rc: self.log.debug('An error occurred while creating namespace ' f' {self.name}.\nreturn code: {rc}\nWarning: {stderr}') if stderr: if 'File exists' in stderr: self.log.debug(stderr) else: self.log.error('Unable to create namespace') sys.exit(1) self.br_ifc = 'veth-br-' + self.name.split('-')[0] + '-' + self.vlan self.peer_ifc = 'veth-' + self.name try: self.ip.link( "add", ifname=self.br_ifc, peer=self.peer_ifc, kind='veth') except NetlinkError as exc: if 'File exists' not in str(exc): self.log.error('Failed creating veth pair. {}'.format(exc)) sys.exit(1) try: # peer interface side disappears from host space once attached to # the namespace idx_ns_ifc = self.ip.link_lookup(ifname=self.peer_ifc)[0] self.ip.link('set', index=idx_ns_ifc, net_ns_fd=self.name) except IndexError: self.log.debug('Peer ifc already attached.') except NetlinkError: self.log.debug('Peer ifc already attached.') idx_br = self.ip.link_lookup(ifname=bridge)[0] self.idx_br_ifc = self.ip.link_lookup(ifname=self.br_ifc)[0] self.ip.link('set', index=self.idx_br_ifc, master=idx_br) # bring up the interfaces cmd = 'ip netns exec {} ip link set dev {} up'.format( self.name, self.peer_ifc) stdout, stderr, rc = sub_proc_exec(cmd) cmd = 'ip netns exec {} ip link set dev lo up'.format(self.name) stdout, stderr, rc = sub_proc_exec(cmd) cmd = 'ip netns exec {} ip addr add {} dev {} brd +' \ .format(self.name, addr, self.peer_ifc) stdout, stderr, rc = sub_proc_exec(cmd) # verify address setup # cmd = 'ip netns exec {} ip addr show'.format(self.name) # proc = Popen(cmd.split(), stdout=PIPE, stderr=PIPE) # stdout, stderr = proc.communicate() self.ip.link('set', index=self.idx_br_ifc, state='up') def _get_name_sp_ifc_name(self): return self.peer_ifc def _get_name_sp_name(self): return self.name def _get_name_sp_addr(self): return self.addr def _launch_cmd(self, cmd, stdout=PIPE, stderr=PIPE): """Execute a command in the namespace Args: log (object): Log cmd (string) """ cmd = 'ip netns exec {} {}'.format(self.name, cmd) data = sub_proc_launch(cmd, stdout, stderr) return data def _exec_cmd(self, cmd, stdout=PIPE, stderr=PIPE): """Execute a command in the namespace Args: log (object): Log cmd (string) """ cmd = 'ip netns exec {} {}'.format(self.name, cmd) std_out, std_err, rc = sub_proc_exec(cmd, stdout, stderr) return std_out, std_err, rc def _destroy_name_sp(self): self.ip.link('set', index=self.idx_br_ifc, state='down') self.ip.link('del', index=self.idx_br_ifc) self.ip.close() stdout, stderr, rc = sub_proc_exec('ip netns del {}'.format(self.name)) def _disconnect_container(self): """ Disconnects any attached containers by bringing down all veth pairs attached to the bridge. """ br_idx = self.ip.link_lookup(ifname=self.bridge) for link in self.ip.get_links(): if br_idx[0] == link.get_attr('IFLA_MASTER'): link_name = link.get_attr('IFLA_IFNAME') if 'veth' in link_name: self.log.debug('Bringing down veth pair {} on bridge: {}' .format(link_name, self.bridge)) self.ip.link('set', index=link['index'], state='down') def _reconnect_container(self): """ Disconnects any attached containers by bringing down all veth pairs attached to the bridge. """ br_idx = self.ip.link_lookup(ifname=self.bridge) for link in self.ip.get_links(): if br_idx[0] == link.get_attr('IFLA_MASTER'): link_name = link.get_attr('IFLA_IFNAME') if 'veth' in link.get_attr('IFLA_IFNAME'): self.log.debug('Bringing up veth pair {} on bridge: {}' .format(link_name, self.bridge)) self.log.debug('link:' + link.get_attr('IFLA_IFNAME')) self.ip.link('set', index=link['index'], state='up')
class AtcdLinuxShaper(AtcdThriftHandlerTask): ID_MANAGER_ID_MIN = HANDLE_MIN ID_MANAGER_ID_MAX = HANDLE_MAX def initTask(self): self.ipr = IPRoute() super(AtcdLinuxShaper, self).initTask() def stop(self): self._release_ipr() def _release_ipr(self): self.ipr.close() def _links_lookup(self): try: self.lan['id'] = self.ipr.link_lookup(ifname=self.lan_name)[0] self.wan['id'] = self.ipr.link_lookup(ifname=self.wan_name)[0] except IndexError: self._release_ipr() msg = 'One of the following interfaces does not exist:' \ ' {0}, {1}'.format(self.lan_name, self.wan_name) self.logger.critical(msg) raise Exception(msg) def initialize_shaping_system(self): """Initialize Iptables and TC subsystems Only call once as this will FLUSH all current shapings... """ self.logger.info("Calling initialize_shaping_system") self._initialize_iptables() self._initialize_tc() def _initialize_iptables(self): """Initialize IPTables by flushing all rules in FORWARD chain from mangle table. """ cmd = "{0} -t mangle -F FORWARD".format(self.iptables) self.run_cmd(cmd) def _initialize_tc_for_interface(self, eth): """Initialize TC on a given interface. If an exception is thrown, it will be forwarded to the main loop unless it can be ignored. Args: eth: the interface to flush TC on. Raises: NetlinkError: An error occured initializing TC subsystem. Exception: Any other exception thrown during initialization. """ idx = 0x10000 eth_name = eth['name'] eth_id = eth['id'] try: self.logger.info("deleting root QDisc on {0}".format(eth_name)) self.ipr.tc(RTM_DELQDISC, None, eth_id, 0, parent=TC_H_ROOT) except Exception as e: # a (2, 'No such file or directory') can be thrown if there is # nothing to delete. Ignore such error, return the error otherwise if isinstance(e, NetlinkError) and e.code == 2: self.logger.warning( "could not delete root QDisc. There might " "have been nothing to delete") else: self.logger.exception( 'Initializing root Qdisc for {0}'.format(eth_name) ) raise try: self.logger.info("setting root qdisc on {0}".format(eth_name)) self.ipr.tc(RTM_NEWQDISC, "htb", eth_id, idx, default=0) except Exception as e: self.logger.exception( 'Setting root Qdisc for {0}'.format(eth_name) ) raise return TrafficControlRc(code=ReturnCode.OK) def _initialize_tc(self): """Initialize TC root qdisc on both LAN and WAN interface. """ for netif in [self.lan, self.wan]: self._initialize_tc_for_interface(netif) def _unset_htb_class(self, mark, eth): """Given a mark and an interface, unset the HTB class. Args: mark: The mark based on which we delete the class. eth: The interface on which to delete that class id. Returns: A TrafficControlRc containing information on success/failure. """ ifid = eth['id'] idx = 0x10000 + mark try: self.logger.info( "deleting class on IFID {0}, classid {1}".format( eth['name'], int_to_classid(idx) ) ) self.ipr.tc(RTM_DELTCLASS, 'htb', ifid, idx) except NetlinkError as e: return TrafficControlRc( code=ReturnCode.NETLINK_HTB_ERROR, message=str(e)) except Exception as e: self.logger.exception('_unset_htb_class') exc_info = sys.exc_info() return TrafficControlRc( code=ReturnCode.UNKNOWN_HTB_ERROR, message=str(exc_info)) return TrafficControlRc(code=ReturnCode.OK) def _set_htb_class(self, mark, eth, shaping): """Given a mark, an interface and shaping settings, set the HTB class. Args: mark: The mark based on which we create the class eth: The interface on which to create that class id. shaping: The shaping settings to set. Returns: A TrafficControlRc containing information on success/failure. """ ifid = eth['id'] idx = 0x10000 + mark parent = 0x10000 self.logger.info( "create new HTB class on IFID {0}, classid {1}," "parent {2}, rate {3}kbits".format( eth['name'], int_to_classid(idx), int_to_classid(parent), shaping.rate or 2**22 - 1) ) try: self.ipr.tc( RTM_NEWTCLASS, 'htb', ifid, idx, parent=parent, rate="{}kbit".format(shaping.rate or (2**22 - 1)), ) except NetlinkError as e: return TrafficControlRc( code=ReturnCode.NETLINK_HTB_ERROR, message=str(e)) except Exception as e: self.logger.exception('_set_htb_class') exc_info = sys.exc_info() return TrafficControlRc( code=ReturnCode.UNKNOWN_HTB_ERROR, message=str(exc_info)) return TrafficControlRc(code=ReturnCode.OK) def _unset_netem_qdisc(self, mark, eth): """This is not needed as deleting the HTB class is sufficient to remove the netem qdisc""" pass def _set_netem_qdisc(self, mark, eth, shaping): """Given a mark, interface and shaping settings, create the NetEm Qdisc. Args: mark: The mark based on which we create the Qdisc. eth: The interface on which we will create the Qdisc. shaping: The shaping settings for that interface. Returns: A TrafficControlRc containing information on success/failure. """ ifid = eth['id'] parent = 0x10000 + mark idx = 0 # automatically assign a handleid self.logger.info( "create new Netem qdisc on IFID {0}, parent {1}," " loss {2}%, delay {3}".format( eth['name'], int_to_classid(parent), shaping.loss.percentage, shaping.delay.delay * 1000) ) try: self.ipr.tc( RTM_NEWQDISC, 'netem', ifid, idx, parent=parent, loss=shaping.loss.percentage, delay=shaping.delay.delay * 1000, jitter=shaping.delay.jitter * 1000, delay_corr=shaping.delay.correlation, loss_corr=shaping.loss.correlation, prob_reorder=shaping.reorder.percentage, corr_reorder=shaping.reorder.correlation, gap=shaping.reorder.gap, prob_corrupt=shaping.corruption.percentage, corr_corrupt=shaping.corruption.correlation, ) except NetlinkError as e: return TrafficControlRc( code=ReturnCode.NETLINK_NETEM_ERROR, message=str(e)) except Exception as e: self.logger.exception('_set_netem_qdisc') exc_info = sys.exc_info() return TrafficControlRc( code=ReturnCode.UNKNOWN_NETEM_ERROR, message=str(exc_info)) return TrafficControlRc(code=ReturnCode.OK) def _unset_filter(self, mark, eth): """Given a mark and an interface, delete the filter. Args: mark: The mark based on which we delete the filter. eth: The interface on which we delete the filter. Returns: A TrafficControlRc containing information on success/failure. """ ifid = eth['id'] parent = 0x10000 self.logger.info( "deleting filter on IFID {0}, handle {1:X}".format( eth['name'], mark ) ) try: self.ipr.tc( RTM_DELTFILTER, 'fw', ifid, mark, parent=parent, protocol=ETH_P_IP, prio=PRIO ) except NetlinkError as e: return TrafficControlRc( code=ReturnCode.NETLINK_FW_ERROR, message=str(e)) except Exception as e: self.logger.exception('_unset_filter') exc_info = sys.exc_info() return TrafficControlRc( code=ReturnCode.UNKNOWN_FW_ERROR, message=str(exc_info)) return TrafficControlRc(code=ReturnCode.OK) def _set_filter(self, mark, eth, shaping): """Given a mark, interface and shaping settings, create a TC filter. Args: mark: The mark based on which we create the filter. eth: The interface on which we create the filter. shaping: The shaping associated to this interface. Returns: A TrafficControlRc containing information on success/failure. """ ifid = eth['id'] idx = 0x10000 + mark parent = 0x10000 self.logger.info( "create new FW filter on IFID {0}, classid {1}," " handle {2:X}, rate: {3}kbits".format( eth['name'], int_to_classid(idx), mark, shaping.rate ) ) try: extra_args = {} if not self.dont_drop_packets: extra_args.update({ 'rate': "{}kbit".format(shaping.rate or 2**22 - 1), 'burst': self.burst_size, 'action': 'drop', }) self.ipr.tc(RTM_NEWTFILTER, 'fw', ifid, mark, parent=parent, protocol=ETH_P_IP, prio=PRIO, classid=idx, **extra_args ) except NetlinkError as e: return TrafficControlRc( code=ReturnCode.NETLINK_FW_ERROR, message=str(e)) except Exception as e: self.logger.exception('_set_filter') exc_info = sys.exc_info() return TrafficControlRc( code=ReturnCode.UNKNOWN_FW_ERROR, message=str(exc_info)) return TrafficControlRc(code=ReturnCode.OK) def _unset_iptables(self, mark, eth, ip, options=None): """Given a mark, interface, IP and options, clear iptables rules. Args: mark: The mark to delete. eth: The interface on which to delete the mark. ip: The IP address to shape. options: An array of iptables options for more specific filtering. Returns: A TrafficControlRc containing information on success/failure. """ if options is None or len(options) == 0: options = [''] for opt in options: cmd = "{0} -t mangle -D FORWARD {1} {2} -i {3} {option} " \ "-j MARK --set-mark {4}".format( self.iptables, "-s" if eth['name'] == self.wan['name'] else "-d", ip, self.lan['name'] if eth['name'] == self.wan['name'] else self.wan['name'] , mark, option=opt) self.run_cmd(cmd) def _set_iptables(self, mark, eth, ip, options=None): """Given a mark, interface, IP and options, create iptables rules. Those rules will mark packets which will be filtered by TC filter and put in the right shaping bucket. Args: mark: The mark to delete. eth: The interface on which to delete the mark. ip: The IP address to shape. options: An array of iptables options for more specific filtering. Returns: A TrafficControlRc containing information on success/failure. """ if options is None or len(options) == 0: options = [''] for opt in options: cmd = "{0} -t mangle -A FORWARD {1} {2} -i {3} {option} " \ "-j MARK --set-mark {4}".format( self.iptables, "-s" if eth['name'] == self.wan['name'] else "-d", ip, self.lan['name'] if eth['name'] == self.wan['name'] else self.wan['name'], mark, option=opt) self.run_cmd(cmd) def _shape_interface(self, mark, eth, ip, shaping): """Shape the traffic for a given interface. Shape the traffic for a given IP on a given interface, given the mark and the shaping settings. There is a few steps to shape the traffic of an IP: 1. Create an HTB class that limit the throughput. 2. Create a NetEm QDisc that adds corruption, loss, reordering, loss and delay. 3. Create the TC filter that will bucket packets with a given mark in the right HTB class. 4. Set an iptables rule that mark packets going to/coming from IP Args: mark: The mark to set on IP packets. eth: The network interface. ip: The IP to shape traffic for. shaping: The shaping setting to set. Returns: A TrafficControlRc containing information on success/failure. """ self.logger.info( "Shaping ip {0} on interface {1}".format(ip, eth['name'])) # HTB class tcrc = self._set_htb_class(mark, eth, shaping) if tcrc.code != ReturnCode.OK: self.logger.error( "adding HTB class on IFID {0}, mark {1}, err: {2}".format( eth['name'], mark, tcrc.message)) return tcrc # NetemQdisc tcrc = self._set_netem_qdisc(mark, eth, shaping) if tcrc.code != ReturnCode.OK: self.logger.error( "adding NetEm qdisc on IFID {0}, mark {1}, err: {2}".format( eth['name'], mark, tcrc.message)) # delete class self._unset_htb_class(mark, eth) return tcrc # filter tcrc = self._set_filter(mark, eth, shaping) if tcrc.code != ReturnCode.OK: self.logger.error( "adding filter FW on IFID {0}, mark {1}, err: {2}".format( eth['name'], mark, tcrc.message)) # delete class self._unset_htb_class(mark, eth) return tcrc # iptables self._set_iptables(mark, eth, ip, shaping.iptables_options) return TrafficControlRc(code=ReturnCode.OK) def _unshape_interface(self, mark, eth, ip, settings): """Unshape the traffic for a given interface. Unshape the traffic for a given IP on a given interface, given the mark and the shaping settings. There is a few steps to unshape the traffic of an IP: 1. Remove the iptables rule. 2. Remove the TC filter. 3. Remove the HTB class. Args: mark: The mark to set on IP packets. eth: The network interface. ip: The IP to shape traffic for. shaping: The shaping setting to set. Returns: A TrafficControlRc containing information on success/failure. """ self.logger.info( "Unshaping ip {0} on interface {1}".format(ip, eth['name'])) # iptables self._unset_iptables(mark, eth, ip, settings.iptables_options) # filter tcrc = self._unset_filter(mark, eth) if tcrc.code != ReturnCode.OK: self.logger.error( "deleting FW filter on IFID {0}, mark {1}, err: {2}".format( eth['name'], mark, tcrc.message) ) return tcrc # HTB class tcrc = self._unset_htb_class(mark, eth) if tcrc.code != ReturnCode.OK: self.logger.error( "deleting HTB class on IFID {0}, mark {1}, err: {2}".format( eth['name'], mark, tcrc.message) ) return tcrc return TrafficControlRc(code=ReturnCode.OK)
from pyroute2 import IW from pyroute2 import IPRoute from pyroute2.netlink import NetlinkError # interface name to check ifname = 'lo' ip = IPRoute() iw = IW() index = ip.link_lookup(ifname=ifname)[0] try: iw.get_interface_by_ifindex(index) print("wireless interface") except NetlinkError as e: if e.code == 19: # 19 'No such device' print("not a wireless interface") finally: iw.close() ip.close()
class TestIPRoute(object): def setup(self): self.ip = IPRoute() try: self.ifaces = [] self.dev, idx = self.create() except IndexError: pass def create(self, kind='dummy'): name = uifname() create_link(name, kind=kind) idx = self.ip.link_lookup(ifname=name)[0] self.ifaces.append(idx) return (name, idx) def teardown(self): if hasattr(self, 'ifaces'): for dev in self.ifaces: try: self.ip.link('delete', index=dev) except: pass self.ip.close() def _test_nla_operators(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) r = [x for x in self.ip.get_addr() if x['index'] == self.ifaces[0]] complement = r[0] - r[1] intersection = r[0] & r[1] assert complement.get_attr('IFA_ADDRESS') == '172.16.0.1' assert complement.get_attr('IFA_LABEL') is None assert complement['prefixlen'] == 0 assert complement['index'] == 0 assert intersection.get_attr('IFA_ADDRESS') is None assert intersection.get_attr('IFA_LABEL') == self.dev assert intersection['prefixlen'] == 24 assert intersection['index'] == self.ifaces[0] def test_addr_add(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) assert '172.16.0.1/24' in get_ip_addr() def test_vlan_filter_dump(self): require_user('root') (an, ax) = self.create('bridge') (bn, bx) = self.create('bridge') self.ip.link('set', index=ax, state='up') self.ip.link('set', index=bx, state='up') assert len(self.ip.get_vlans()) >= 2 for name in (an, bn): assert len(self.ip.get_vlans(ifname=name)) == 1 assert (self.ip.get_vlans( ifname=name)[0].get_attr('IFLA_IFNAME')) == name assert (self.ip.get_vlans(ifname=name)[0].get_nested( 'IFLA_AF_SPEC', 'IFLA_BRIDGE_VLAN_INFO'))['vid'] == 1 def test_vlan_filter_add(self): require_user('root') (bn, bx) = self.create('bridge') (sn, sx) = self.create('dummy') self.ip.link('set', index=sx, master=bx) assert not grep('bridge vlan show', pattern='567') self.ip.vlan_filter('add', index=sx, vlan_info={'vid': 567}) assert grep('bridge vlan show', pattern='567') self.ip.vlan_filter('del', index=sx, vlan_info={'vid': 567}) assert not grep('bridge vlan show', pattern='567') def test_vlan_filter_add_raw(self): require_user('root') (bn, bx) = self.create('bridge') (sn, sx) = self.create('dummy') self.ip.link('set', index=sx, master=bx) assert not grep('bridge vlan show', pattern='567') self.ip.vlan_filter( 'add', index=sx, af_spec={'attrs': [['IFLA_BRIDGE_VLAN_INFO', { 'vid': 567 }]]}) assert grep('bridge vlan show', pattern='567') self.ip.vlan_filter( 'del', index=sx, af_spec={'attrs': [['IFLA_BRIDGE_VLAN_INFO', { 'vid': 567 }]]}) assert not grep('bridge vlan show', pattern='567') def test_brport_basic(self): require_user('root') (bn, bx) = self.create('bridge') (sn, sx) = self.create('dummy') self.ip.link('set', index=sx, master=bx) self.ip.link('set', index=sx, state='up') self.ip.link('set', index=bx, state='up') self.ip.brport('set', index=sx, unicast_flood=0, cost=200, proxyarp=1) port = self.ip.brport('show', index=sx)[0] protinfo = port.get_attr('IFLA_PROTINFO') assert protinfo.get_attr('IFLA_BRPORT_COST') == 200 assert protinfo.get_attr('IFLA_BRPORT_PROXYARP') == 1 assert protinfo.get_attr('IFLA_BRPORT_UNICAST_FLOOD') == 0 def test_local_add(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', local='172.16.0.1', mask=24) link = self.ip.get_addr(index=self.ifaces[0])[0] address = link.get_attr('IFA_ADDRESS') local = link.get_attr('IFA_LOCAL') assert address == '172.16.0.2' assert local == '172.16.0.1' def test_addr_broadcast(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24, broadcast='172.16.0.250') assert '172.16.0.250' in get_ip_brd() def test_addr_broadcast_default(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24, broadcast=True) assert '172.16.0.255' in get_ip_brd() def test_flush_addr(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.1.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.1.2', mask=24) assert len( self.ip.get_addr(index=self.ifaces[0], family=socket.AF_INET)) == 4 self.ip.flush_addr(index=self.ifaces[0]) assert len( self.ip.get_addr(index=self.ifaces[0], family=socket.AF_INET)) == 0 def test_flush_rules(self): require_user('root') init = len(self.ip.get_rules(family=socket.AF_INET)) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 self.ip.rule('add', table=10, priority=110) self.ip.rule('add', table=15, priority=150, action='FR_ACT_PROHIBIT') self.ip.rule('add', table=20, priority=200, src='172.16.200.1') self.ip.rule('add', table=25, priority=250, dst='172.16.250.1') assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 4 assert len(self.ip.get_rules(src='172.16.200.1')) == 1 assert len(self.ip.get_rules(dst='172.16.250.1')) == 1 self.ip.flush_rules(family=socket.AF_INET, priority=lambda x: 100 < x < 500) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 assert len(self.ip.get_rules(src='172.16.200.1')) == 0 assert len(self.ip.get_rules(dst='172.16.250.1')) == 0 assert len(self.ip.get_rules(family=socket.AF_INET)) == init def test_rules_deprecated(self): require_user('root') init = len(self.ip.get_rules(family=socket.AF_INET)) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 self.ip.rule('add', 10, 110) self.ip.rule('add', 15, 150, 'FR_ACT_PROHIBIT') assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 2 self.ip.flush_rules(family=socket.AF_INET, priority=lambda x: 100 < x < 500) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 assert len(self.ip.get_rules(family=socket.AF_INET)) == init def test_match_callable(self): assert len(self.ip.get_links(match=partial(lambda x: x))) > 0 def test_addr_filter(self): require_user('root') self.ip.addr('add', index=self.ifaces[0], address='172.16.0.1', prefixlen=24, broadcast='172.16.0.255') self.ip.addr('add', index=self.ifaces[0], address='172.16.0.2', prefixlen=24, broadcast='172.16.0.255') assert len(self.ip.get_addr(index=self.ifaces[0])) == 2 assert len(self.ip.get_addr(address='172.16.0.1')) == 1 assert len(self.ip.get_addr(broadcast='172.16.0.255')) == 2 assert len( self.ip.get_addr( match=lambda x: x['index'] == self.ifaces[0])) == 2 @skip_if_not_supported def _create_ipvlan(self, smode): master = uifname() ipvlan = uifname() # create the master link self.ip.link('add', ifname=master, kind='dummy') midx = self.ip.link_lookup(ifname=master)[0] # check modes # maybe move modes dict somewhere else? cmode = ifinfmsg.ifinfo.data_map['ipvlan'].modes[smode] assert ifinfmsg.ifinfo.data_map['ipvlan'].modes[cmode] == smode # create ipvlan self.ip.link('add', ifname=ipvlan, kind='ipvlan', link=midx, mode=cmode) devs = self.ip.link_lookup(ifname=ipvlan) assert devs self.ifaces.extend(devs) def test_create_ipvlan_l2(self): return self._create_ipvlan('IPVLAN_MODE_L2') def test_create_ipvlan_l3(self): return self._create_ipvlan('IPVLAN_MODE_L3') @skip_if_not_supported def _create(self, kind, **kwarg): name = uifname() self.ip.link('add', ifname=name, kind=kind, **kwarg) devs = self.ip.link_lookup(ifname=name) assert devs self.ifaces.extend(devs) return (name, devs[0]) def test_create_dummy(self): require_user('root') self._create('dummy') def test_create_bond(self): require_user('root') self._create('bond') def test_create_bridge(self): require_user('root') self._create('bridge') def test_create_team(self): require_user('root') self._create('team') def test_create_vti(self): require_user('root') (ifname, idx) = self.create() self.ip.link('set', index=idx, state='up') self.ip.addr('add', index=idx, address='172.16.128.10', mask=24) self._create('vti', vti_link=idx, vti_local='172.16.128.10', vti_remote='172.16.128.11', vti_ikey=64, vti_okey=72) def test_create_vti6(self): require_user('root') self._create('vti6', vti_link=2, vti_local='fd00:1:2:3:4:1::1', vti_remote='fd00:1:2:3:4:2::1', vti_ikey=80, vti_okey=88) def test_ntables(self): setA = set( filter(lambda x: x is not None, [ x.get_attr('NDTA_PARMS').get_attr('NDTPA_IFINDEX') for x in self.ip.get_ntables() ])) setB = set([x['index'] for x in self.ip.get_links()]) assert setA == setB def test_fdb_vxlan(self): require_kernel(4, 4) require_user('root') # create dummy (dn, dx) = self._create('dummy') # create vxlan on it (vn, vx) = self._create('vxlan', vxlan_link=dx, vxlan_id=500) # create FDB record l2 = '00:11:22:33:44:55' self.ip.fdb('add', lladdr=l2, ifindex=vx, vni=600, port=5678, dst='172.16.40.40') # dump r = self.ip.fdb('dump', ifindex=vx, lladdr=l2) assert len(r) == 1 assert r[0]['ifindex'] == vx assert r[0].get_attr('NDA_LLADDR') == l2 assert r[0].get_attr('NDA_DST') == '172.16.40.40' assert r[0].get_attr('NDA_PORT') == 5678 assert r[0].get_attr('NDA_VNI') == 600 def test_fdb_bridge_simple(self): require_kernel(4, 4) require_user('root') require_kernel(4, 4) # create bridge (bn, bx) = self._create('bridge') # create FDB record l2 = '00:11:22:33:44:55' self.ip.fdb('add', lladdr=l2, ifindex=bx) # dump FDB r = self.ip.fdb('dump', ifindex=bx, lladdr=l2) # one vlan == 1, one w/o vlan assert len(r) == 2 assert len(list(filter(lambda x: x['ifindex'] == bx, r))) == 2 assert len(list(filter(lambda x: x.get_attr('NDA_VLAN'), r))) == 1 assert len(list(filter(lambda x: x.get_attr('NDA_MASTER') == bx, r))) == 2 assert len(list(filter(lambda x: x.get_attr('NDA_LLADDR') == l2, r))) == 2 r = self.ip.fdb('dump', ifindex=bx, lladdr=l2, vlan=1) assert len(r) == 1 assert r[0].get_attr('NDA_VLAN') == 1 assert r[0].get_attr('NDA_MASTER') == bx assert r[0].get_attr('NDA_LLADDR') == l2 def test_neigh_real_links(self): links = set([x['index'] for x in self.ip.get_links()]) neigh = set([x['ifindex'] for x in self.ip.get_neighbours()]) assert neigh < links def test_neigh_filter(self): require_user('root') # inject arp records self.ip.neigh('add', dst='172.16.45.1', lladdr='00:11:22:33:44:55', ifindex=self.ifaces[0]) self.ip.neigh('add', dst='172.16.45.2', lladdr='00:11:22:33:44:55', ifindex=self.ifaces[0]) # assert two arp records on the interface assert len(self.ip.get_neighbours(ifindex=self.ifaces[0])) == 2 # filter by dst assert len(self.ip.get_neighbours(dst='172.16.45.1')) == 1 # filter with lambda assert len( self.ip.get_neighbours( match=lambda x: x['ifindex'] == self.ifaces[0])) == 2 def test_mass_ipv6(self): # # Achtung! This test is time consuming. # It is really time consuming, I'm not not # kidding you. Beware. # require_user('root') base = 'fdb3:84e5:4ff4:55e4::{0}' limit = int(os.environ.get('PYROUTE2_SLIMIT', '0x800'), 16) # add addresses for idx in range(limit): self.ip.addr('add', self.ifaces[0], base.format(hex(idx)[2:]), 48) # assert addresses in two steps, to ease debug addrs = self.ip.get_addr(10) assert len(addrs) >= limit # clean up addresses # # it is not required, but if you don't do that, # you'll get this on the interface removal: # # >> kernel:BUG: soft lockup - CPU#0 stuck for ... # # so, not to scare people, remove addresses gracefully # one by one # # it also verifies all the addresses are in place for idx in reversed(range(limit)): self.ip.addr('delete', self.ifaces[0], base.format(hex(idx)[2:]), 48) def test_fail_not_permitted(self): try: self.ip.addr('add', 1, address='172.16.0.1', mask=24) except NetlinkError as e: if e.code != errno.EPERM: # Operation not permitted raise finally: try: self.ip.addr('delete', 1, address='172.16.0.1', mask=24) except: pass def test_fail_no_such_device(self): require_user('root') dev = sorted([i['index'] for i in self.ip.get_links()])[-1] + 10 try: self.ip.addr('add', dev, address='172.16.0.1', mask=24) except NetlinkError as e: if e.code != errno.ENODEV: # No such device raise def test_remove_link(self): require_user('root') try: self.ip.link('del', index=self.ifaces[0]) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 0 def _test_route_proto(self, proto, fake, spec=''): require_user('root') os.system('ip route add 172.16.3.0/24 via 127.0.0.1 %s' % spec) time.sleep(1) assert grep('ip ro', pattern='172.16.3.0/24.*127.0.0.1') try: self.ip.route('del', dst='172.16.3.0/24', gateway='127.0.0.1', proto=fake) except NetlinkError: pass self.ip.route('del', dst='172.16.3.0/24', gateway='127.0.0.1', proto=proto) assert not grep('ip ro', pattern='172.16.3.0/24.*127.0.0.1') def test_route_proto_static(self): return self._test_route_proto('static', 'boot', 'proto static') def test_route_proto_static_num(self): return self._test_route_proto(4, 3, 'proto static') def test_route_proto_boot(self): return self._test_route_proto('boot', 4) def test_route_proto_boot_num(self): return self._test_route_proto(3, 'static') def test_route_oif_as_iterable(self): require_user('root') spec = {'dst': '172.16.0.0', 'dst_len': 24, 'oif': (1, )} self.ip.route('add', **spec) rts = self.ip.get_routes(family=socket.AF_INET, dst='172.16.0.0') self.ip.route('del', **spec) assert len(rts) == 1 assert rts[0].get_attr('RTA_OIF') == 1 def test_route_get_target(self): if not self.ip.get_default_routes(table=254): raise SkipTest('no default IPv4 routes') rts = self.ip.get_routes(family=socket.AF_INET, dst='8.8.8.8', table=254) assert len(rts) > 0 def test_route_get_target_default_ipv4(self): rts = self.ip.get_routes(dst='127.0.0.1') assert len(rts) > 0 def test_route_get_target_default_ipv6(self): rts = self.ip.get_routes(dst='::1') assert len(rts) > 0 def test_route_get_by_spec(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', index=self.ifaces[0], address='172.16.60.1', mask=24) self.ip.addr('add', index=self.ifaces[0], address='172.16.61.1', mask=24) rts = self.ip.get_routes(family=socket.AF_INET, dst=lambda x: x in ('172.16.60.0', '172.16.61.0')) assert len(rts) == 4 @skip_if_not_supported def _test_route_mpls_via_ipv(self, family, address, label): require_kernel(4, 4) require_user('root') self.ip.route( 'add', **{ 'family': AF_MPLS, 'oif': self.ifaces[0], 'via': { 'family': family, 'addr': address }, 'newdst': { 'label': label, 'bos': 1 } }) rt = self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)[0] assert rt.get_attr('RTA_VIA')['addr'] == address assert rt.get_attr('RTA_VIA')['family'] == family assert rt.get_attr('RTA_NEWDST')[0]['label'] == label assert len(rt.get_attr('RTA_NEWDST')) == 1 self.ip.route( 'del', **{ 'family': AF_MPLS, 'oif': self.ifaces[0], 'dst': { 'label': 0x10, 'bos': 1 }, 'via': { 'family': family, 'addr': address }, 'newdst': { 'label': label, 'bos': 1 } }) assert len(self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)) == 0 def test_route_mpls_via_ipv4(self): self._test_route_mpls_via_ipv(socket.AF_INET, '172.16.0.1', 0x20) def test_route_mpls_via_ipv6(self): self._test_route_mpls_via_ipv(socket.AF_INET6, 'fe80::5054:ff:fe4b:7c32', 0x20) @skip_if_not_supported def test_route_mpls_swap_newdst_simple(self): require_kernel(4, 4) require_user('root') req = { 'family': AF_MPLS, 'oif': self.ifaces[0], 'dst': { 'label': 0x20, 'bos': 1 }, 'newdst': { 'label': 0x21, 'bos': 1 } } self.ip.route('add', **req) rt = self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)[0] assert rt.get_attr('RTA_DST')[0]['label'] == 0x20 assert len(rt.get_attr('RTA_DST')) == 1 assert rt.get_attr('RTA_NEWDST')[0]['label'] == 0x21 assert len(rt.get_attr('RTA_NEWDST')) == 1 self.ip.route('del', **req) assert len(self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)) == 0 @skip_if_not_supported def test_route_mpls_swap_newdst_list(self): require_kernel(4, 4) require_user('root') req = { 'family': AF_MPLS, 'oif': self.ifaces[0], 'dst': { 'label': 0x20, 'bos': 1 }, 'newdst': [{ 'label': 0x21, 'bos': 1 }] } self.ip.route('add', **req) rt = self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)[0] assert rt.get_attr('RTA_DST')[0]['label'] == 0x20 assert len(rt.get_attr('RTA_DST')) == 1 assert rt.get_attr('RTA_NEWDST')[0]['label'] == 0x21 assert len(rt.get_attr('RTA_NEWDST')) == 1 self.ip.route('del', **req) assert len(self.ip.get_routes(oif=self.ifaces[0], family=AF_MPLS)) == 0 def test_route_multipath_raw(self): require_user('root') self.ip.route('add', dst='172.16.241.0', mask=24, multipath=[{ 'hops': 20, 'oif': 1, 'attrs': [['RTA_GATEWAY', '127.0.0.2']] }, { 'hops': 30, 'oif': 1, 'attrs': [['RTA_GATEWAY', '127.0.0.3']] }]) assert grep('ip route show', pattern='172.16.241.0/24') assert grep('ip route show', pattern='nexthop.*127.0.0.2.*weight 21') assert grep('ip route show', pattern='nexthop.*127.0.0.3.*weight 31') self.ip.route('del', dst='172.16.241.0', mask=24) def test_route_multipath_helper(self): require_user('root') req = IPRouteRequest({ 'dst': '172.16.242.0/24', 'multipath': [{ 'hops': 20, 'oif': 1, 'gateway': '127.0.0.2' }, { 'hops': 30, 'oif': 1, 'gateway': '127.0.0.3' }] }) self.ip.route('add', **req) assert grep('ip route show', pattern='172.16.242.0/24') assert grep('ip route show', pattern='nexthop.*127.0.0.2.*weight 21') assert grep('ip route show', pattern='nexthop.*127.0.0.3.*weight 31') self.ip.route('del', dst='172.16.242.0', mask=24) def test_route_multipath(self): require_user('root') self.ip.route('add', dst='172.16.243.0/24', multipath=[{ 'gateway': '127.0.0.2' }, { 'gateway': '127.0.0.3' }]) assert grep('ip route show', pattern='172.16.243.0/24') assert grep('ip route show', pattern='nexthop.*127.0.0.2') assert grep('ip route show', pattern='nexthop.*127.0.0.3') self.ip.route('del', dst='172.16.243.0', mask=24) def test_route_onlink(self): require_user('root') self.ip.route('add', dst='172.16.244.0/24', gateway='10.100.1.1', oif=1, flags=RTNH_F_ONLINK) assert grep('ip route show', pattern='10.100.1.1.*onlink') self.ip.route('del', dst='172.16.244.0/24') def test_route_onlink_multipath(self): require_user('root') self.ip.route('add', dst='172.16.245.0/24', multipath=[{ 'gateway': '10.100.1.1', 'oif': 1, 'flags': RTNH_F_ONLINK }, { 'gateway': '10.100.1.2', 'oif': 1, 'flags': RTNH_F_ONLINK }]) assert grep('ip route show', pattern='172.16.245.0/24') assert grep('ip route show', pattern='nexthop.*10.100.1.1.*onlink') assert grep('ip route show', pattern='nexthop.*10.100.1.2.*onlink') self.ip.route('del', dst='172.16.245.0', mask=24) def test_route_onlink_strflags(self): require_user('root') self.ip.route('add', dst='172.16.244.0/24', gateway='10.100.1.1', oif=1, flags=['onlink']) assert grep('ip route show', pattern='10.100.1.1.*onlink') self.ip.route('del', dst='172.16.244.0/24') def test_route_onlink_multipath_strflags(self): require_user('root') self.ip.route('add', dst='172.16.245.0/24', multipath=[{ 'gateway': '10.100.1.1', 'oif': 1, 'flags': ['onlink'] }, { 'gateway': '10.100.1.2', 'oif': 1, 'flags': RTNH_F_ONLINK }]) assert grep('ip route show', pattern='172.16.245.0/24') assert grep('ip route show', pattern='nexthop.*10.100.1.1.*onlink') assert grep('ip route show', pattern='nexthop.*10.100.1.2.*onlink') self.ip.route('del', dst='172.16.245.0', mask=24) @skip_if_not_supported def test_lwtunnel_multipath_mpls(self): require_kernel(4, 4) require_user('root') require_kernel(4, 5) self.ip.route('add', dst='172.16.216.0/24', multipath=[{ 'encap': { 'type': 'mpls', 'labels': 500 }, 'oif': 1 }, { 'encap': { 'type': 'mpls', 'labels': '600/700' }, 'gateway': '127.0.0.4' }]) routes = self.ip.route('dump', dst='172.16.216.0/24') assert len(routes) == 1 mp = routes[0].get_attr('RTA_MULTIPATH') assert len(mp) == 2 assert mp[0]['oif'] == 1 assert mp[0].get_attr('RTA_ENCAP_TYPE') == 1 labels = mp[0].get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 1 assert labels[0]['bos'] == 1 assert labels[0]['label'] == 500 assert mp[1].get_attr('RTA_ENCAP_TYPE') == 1 labels = mp[1].get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 2 assert labels[0]['bos'] == 0 assert labels[0]['label'] == 600 assert labels[1]['bos'] == 1 assert labels[1]['label'] == 700 self.ip.route('del', dst='172.16.216.0/24') @skip_if_not_supported def test_lwtunnel_mpls_dict_label(self): require_kernel(4, 4) require_user('root') require_kernel(4, 3) self.ip.route('add', dst='172.16.226.0/24', encap={ 'type': 'mpls', 'labels': [{ 'bos': 0, 'label': 226 }, { 'bos': 1, 'label': 227 }] }, gateway='127.0.0.2') routes = self.ip.route('dump', dst='172.16.226.0/24') assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_GATEWAY') == '127.0.0.2' labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 2 assert labels[0]['bos'] == 0 assert labels[0]['label'] == 226 assert labels[1]['bos'] == 1 assert labels[1]['label'] == 227 self.ip.route('del', dst='172.16.226.0/24') @skip_if_not_supported def test_lwtunnel_mpls_2_int_label(self): require_kernel(4, 4) require_user('root') require_kernel(4, 3) self.ip.route('add', dst='172.16.206.0/24', encap={ 'type': 'mpls', 'labels': [206, 207] }, oif=1) routes = self.ip.route('dump', dst='172.16.206.0/24') assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_OIF') == 1 labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 2 assert labels[0]['bos'] == 0 assert labels[0]['label'] == 206 assert labels[1]['bos'] == 1 assert labels[1]['label'] == 207 self.ip.route('del', dst='172.16.206.0/24') @skip_if_not_supported def test_lwtunnel_mpls_2_str_label(self): require_kernel(4, 4) require_user('root') require_kernel(4, 3) self.ip.route('add', dst='172.16.246.0/24', encap={ 'type': 'mpls', 'labels': "246/247" }, oif=1) routes = self.ip.route('dump', dst='172.16.246.0/24') assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_OIF') == 1 labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 2 assert labels[0]['bos'] == 0 assert labels[0]['label'] == 246 assert labels[1]['bos'] == 1 assert labels[1]['label'] == 247 self.ip.route('del', dst='172.16.246.0/24') @skip_if_not_supported def test_lwtunnel_mpls_1_str_label(self): require_kernel(4, 4) require_user('root') require_kernel(4, 3) self.ip.route('add', dst='172.16.244.0/24', encap={ 'type': 'mpls', 'labels': "244" }, oif=1) routes = self.ip.route('dump', dst='172.16.244.0/24') assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_OIF') == 1 labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 1 assert labels[0]['bos'] == 1 assert labels[0]['label'] == 244 self.ip.route('del', dst='172.16.244.0/24') @skip_if_not_supported def test_lwtunnel_mpls_1_int_label(self): require_kernel(4, 4) require_user('root') require_kernel(4, 3) self.ip.route('add', dst='172.16.245.0/24', encap={ 'type': 'mpls', 'labels': 245 }, oif=1) routes = self.ip.route('dump', dst='172.16.245.0/24') assert len(routes) == 1 route = routes[0] assert route.get_attr('RTA_ENCAP_TYPE') == 1 assert route.get_attr('RTA_OIF') == 1 labels = route.get_attr('RTA_ENCAP').get_attr('MPLS_IPTUNNEL_DST') assert len(labels) == 1 assert labels[0]['bos'] == 1 assert labels[0]['label'] == 245 self.ip.route('del', dst='172.16.245.0/24') def test_route_change_existing(self): # route('replace', ...) should succeed, if route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) self.ip.route('add', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.route('change', dst='172.16.1.0', mask=24, gateway='172.16.0.2', table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') def test_route_change_not_existing_fail(self): # route('change', ...) should fail, if no route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') try: self.ip.route('change', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) except NetlinkError as e: if e.code != errno.ENOENT: raise def test_route_replace_existing(self): # route('replace', ...) should succeed, if route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) self.ip.route('replace', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.route('replace', dst='172.16.1.0', mask=24, gateway='172.16.0.2', table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') def test_route_replace_not_existing(self): # route('replace', ...) should succeed, if route doesn't exist require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('replace', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') def test_flush_routes(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('add', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) self.ip.route('add', dst='172.16.2.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.2.0/24.*172.16.0.1') self.ip.flush_routes(table=100, family=socket.AF_INET6) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.2.0/24.*172.16.0.1') self.ip.flush_routes(table=100, family=socket.AF_INET) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert not grep('ip route show table 100', pattern='172.16.2.0/24.*172.16.0.1') def test_route_table_2048(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('add', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=2048) assert grep('ip route show table 2048', pattern='172.16.1.0/24.*172.16.0.1') remove_link('bala') def test_symbolic_flags_ifaddrmsg(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], '172.16.1.1', 24) addr = [ x for x in self.ip.get_addr() if x.get_attr('IFA_LOCAL') == '172.16.1.1' ][0] assert 'IFA_F_PERMANENT' in addr.flags2names(addr['flags']) def test_symbolic_flags_ifinfmsg(self): require_user('root') self.ip.link('set', index=self.ifaces[0], flags=['IFF_UP']) iface = self.ip.get_links(self.ifaces[0])[0] assert iface['flags'] & 1 assert 'IFF_UP' in iface.flags2names(iface['flags']) self.ip.link('set', index=self.ifaces[0], flags=['!IFF_UP']) assert not (self.ip.get_links(self.ifaces[0])[0]['flags'] & 1) def test_updown_link(self): require_user('root') try: for i in self.ifaces: self.ip.link('set', index=i, state='up') except NetlinkError: pass assert self.ip.get_links(*self.ifaces)[0]['flags'] & 1 try: for i in self.ifaces: self.ip.link('set', index=i, state='down') except NetlinkError: pass assert not (self.ip.get_links(*self.ifaces)[0]['flags'] & 1) def test_callbacks_positive(self): require_user('root') dev = self.ifaces[0] self.cb_counter = 0 self.ip.register_callback(_callback, lambda x: x.get('index', None) == dev, (self, )) self.test_updown_link() assert self.cb_counter > 0 self.ip.unregister_callback(_callback) def test_callbacks_negative(self): require_user('root') self.cb_counter = 0 self.ip.register_callback(_callback, lambda x: x.get('index', None) == -1, (self, )) self.test_updown_link() assert self.cb_counter == 0 self.ip.unregister_callback(_callback) def test_link_filter(self): links = self.ip.link('dump', ifname='lo') assert len(links) == 1 assert links[0].get_attr('IFLA_IFNAME') == 'lo' def test_link_legacy_nla(self): require_user('root') dev = self.ifaces[0] try: self.ip.link('set', index=dev, state='down') self.ip.link('set', index=dev, IFLA_IFNAME='bala') except NetlinkError: pass assert len(self.ip.link_lookup(ifname='bala')) == 1 try: self.ip.link('set', index=dev, ifname=self.dev) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 1 def test_link_rename(self): require_user('root') dev = self.ifaces[0] try: self.ip.link('set', index=dev, ifname='bala') except NetlinkError: pass assert len(self.ip.link_lookup(ifname='bala')) == 1 try: self.ip.link('set', index=dev, ifname=self.dev) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 1 def test_rules(self): assert len(get_ip_rules('-4')) == \ len(self.ip.get_rules(socket.AF_INET)) assert len(get_ip_rules('-6')) == \ len(self.ip.get_rules(socket.AF_INET6)) def test_addr(self): assert len(get_ip_addr()) == len(self.ip.get_addr()) def test_links(self): assert len(get_ip_link()) == len(self.ip.get_links()) def test_one_link(self): lo = self.ip.get_links(1)[0] assert lo.get_attr('IFLA_IFNAME') == 'lo' def test_default_routes(self): assert len(get_ip_default_routes()) == \ len(self.ip.get_default_routes(family=socket.AF_INET, table=254)) def test_routes(self): assert len(get_ip_route()) == \ len(self.ip.get_routes(family=socket.AF_INET, table=255))
class AIPRoute(): def __init__(self, loop=None, executor=None): self.loop = asyncio.get_event_loop() if loop is None else loop self.executor = executor self.ipr = IPRoute() def close(self): if not self.ipr.closed: self.ipr.close() def _get_id(self, device_name: str) -> None: try: device_ids = self.ipr.link_lookup(ifname=device_name) except OSError as exc: # sometimes calling this will lose the link for some reason if exc.errno == 9: self.ipr.close() self.ipr = IPRoute() device_ids = self.ipr.link_lookup(ifname=device_name) try: return device_ids[0] except IndexError: return 0 def _get_name(self, device_id: int) -> str: try: links = self.ipr.get_links(device_id) except NetlinkError: return None try: return links[0].get_attr('IFLA_IFNAME') except (IndexError, KeyError): return None def _get_up(self, device_id: int) -> str: try: links = self.ipr.get_links(device_id) except NetlinkError: return False try: ifi_flags = links[0]['flags'] except (IndexError, KeyError): return False return (ifi_flags & (IFF_UP | IFF_LOWER_UP)) == (IFF_UP | IFF_LOWER_UP) def _delete_device(self, device_name: str) -> None: try: self.ipr.link('del', ifname=device_name) except NetlinkError as exc: if exc.code == 19: # if it doesn't exist that's okay pass else: raise def _set_master(self, device_id: int, master_id: int) -> None: try: self.ipr.link('set', index=device_id, master=master_id) except (OSError, NetlinkError): raise RuntimeError( f"Failed to add {device_id} to bridge {master_id}") def _set_stp(self, device_id: int, stp: int) -> None: try: self.ipr.link( 'set', **IPLinkRequest({ 'index': device_id, 'kind': 'bridge', 'br_stp_state': stp })) except (OSError, NetlinkError): raise RuntimeError(f"Failed to set {device_id} stp to {stp}") def _add_device(self, device_name: str, device_type: str, **kwargs) -> None: try: self.ipr.link('add', ifname=device_name, kind=device_type, **kwargs) except NetlinkError as exc: if exc.code == 17: raise FileExistsError( f"Device {device_name} already exists") from exc else: raise def _set_address(self, device_id: int, address: netaddr.IPNetwork) -> None: self.ipr.flush_addr(index=device_id) if bool(address.ip) and bool(address.prefixlen): self.ipr.addr('add', index=device_id, address=str(address.ip), mask=address.prefixlen) def _set_mac(self, device_id: int, mac: netaddr.EUI) -> None: if mac: info = self.ipr.get_links(device_id)[0] existing_mac = netaddr.EUI(info.get_attr("IFLA_ADDRESS")) if existing_mac != mac: self.ipr.link('set', index=device_id, address=str(mac)) def _set_device_name(self, device_id: int, device_name: str) -> None: if not device_name: return try: self.ipr.link("set", index=device_id, ifname=device_name) except NetlinkError as exc: if exc.code == 16: time.sleep(2) self.ipr.link("set", index=device_id, ifname=device_name) else: raise def _set_mtu(self, device_id: int, mtu: int) -> None: self.ipr.link('set', index=device_id, mtu=mtu) def _set_up(self, device_id: int, state: bool) -> None: self.ipr.link('set', index=device_id, state='up' if state else 'down') def _flush_rules(self, **kwargs) -> None: try: self.ipr.flush_rules(**kwargs) except (OSError, NetlinkError): pass def _delete_rule(self, **kwargs) -> None: try: self.ipr.rule('delete', **kwargs) except NetlinkError: pass def _add_rule(self, **kwargs) -> None: try: self.ipr.rule('add', **kwargs) except NetlinkError as exc: if exc.code == 17: pass else: raise RuntimeError(f"Failed to add rule {kwargs}: {exc}") def _flush_routes(self, **kwargs) -> None: try: self.ipr.flush_routes(**kwargs) except (OSError, NetlinkError): pass def _delete_route(self, **kwargs) -> None: try: self.ipr.route('delete', **kwargs) except NetlinkError: pass def _add_route(self, **kwargs) -> None: try: self.ipr.route('add', **kwargs) except NetlinkError as exc: if exc.code == 17: time.sleep(0.250) try: self.ipr.route('add', **kwargs) except NetlinkError as exc: raise RuntimeError(f"Failed to add route {kwargs}: {exc}") else: raise RuntimeError(f"Failed to add route {kwargs}: {exc}") def _replace_tc(self, kind: str, device_id: int, handle: int, **kwargs) -> None: self.ipr.tc("replace", kind, device_id, handle, **kwargs) def _delete_tc(self, kind: str, device_id: int, handle: int, **kwargs) -> None: try: self.ipr.tc("del", kind, device_id, handle, **kwargs) except NetlinkError: pass def _add_filter_tc(self, kind: str, device_id: int, **kwargs) -> None: self.ipr.tc("add-filter", kind, device_id, **kwargs) def _run_routine(self, routine): try: routine(self.ipr) except NetlinkError as exc: raise RuntimeError(f"Netlink error in {routine}: {exc}") async def get_id(self, device_name: str) -> int: if not device_name: return 0 return await self.loop.run_in_executor( self.executor, partial(self._get_id, device_name)) async def get_name(self, device_id: int) -> str: if device_id <= 0: return None return await self.loop.run_in_executor( self.executor, partial(self._get_name, device_id)) async def get_up(self, device_id: int) -> str: if device_id <= 0: return None return await self.loop.run_in_executor( self.executor, partial(self._get_up, device_id)) async def delete_device(self, device_name: str) -> None: if not device_name: return await self.loop.run_in_executor( self.executor, partial(self._delete_device, device_name)) async def add_device(self, device_name: str, device_type: str, **kwargs) -> None: if not device_name or not device_type: return await self.loop.run_in_executor( self.executor, partial(self._add_device, device_name, device_type, **kwargs)) async def set_master(self, device_id: int, master_id: int) -> None: if device_id <= 0 or master_id < 0: return await self.loop.run_in_executor( self.executor, partial(self._set_master, device_id, master_id)) async def set_stp(self, device_id: int, stp: bool) -> None: if device_id <= 0: return await self.loop.run_in_executor( self.executor, partial(self._set_stp, device_id, 1 if stp else 0)) async def set_address(self, device_id: int, address: netaddr.IPNetwork) -> None: if device_id <= 0: return await self.loop.run_in_executor( self.executor, partial(self._set_address, device_id, address)) async def set_mtu(self, device_id: int, mtu: int) -> None: if device_id <= 0 or mtu <= 0: return await self.loop.run_in_executor(self.executor, partial(self._set_mtu, device_id, mtu)) async def set_mac(self, device_id: int, mac: netaddr.EUI) -> None: if device_id <= 0: return await self.loop.run_in_executor(self.executor, partial(self._set_mac, device_id, mac)) async def set_device_name(self, device_id: int, device_name: str) -> None: if device_id <= 0: return await self.loop.run_in_executor( self.executor, partial(self._set_device_name, device_id, device_name)) async def set_up(self, device_id: int, state: bool) -> None: if device_id <= 0: return await self.loop.run_in_executor( self.executor, partial(self._set_up, device_id, state)) async def flush_rules(self, **kwargs) -> None: await self.loop.run_in_executor(self.executor, partial(self._flush_rules, **kwargs)) async def delete_rule(self, **kwargs) -> None: await self.loop.run_in_executor(self.executor, partial(self._delete_rule, **kwargs)) async def add_rule(self, **kwargs) -> None: await self.loop.run_in_executor(self.executor, partial(self._add_rule, **kwargs)) async def flush_routes(self, **kwargs) -> None: await self.loop.run_in_executor(self.executor, partial(self._flush_routes, **kwargs)) async def delete_route(self, **kwargs) -> None: await self.loop.run_in_executor(self.executor, partial(self._delete_route, **kwargs)) async def add_route(self, **kwargs) -> None: await self.loop.run_in_executor(self.executor, partial(self._add_route, **kwargs)) async def replace_tc(self, kind: str, device_id: int, handle: int = None, **kwargs) -> None: await self.loop.run_in_executor( self.executor, partial(self._replace_tc, kind, device_id, handle, **kwargs)) async def delete_tc(self, kind: str, device_id: int, handle: int, **kwargs) -> None: await self.loop.run_in_executor( self.executor, partial(self._delete_tc, kind, device_id, handle, **kwargs)) async def add_filter_tc(self, kind: str, device_id: int, **kwargs) -> None: await self.loop.run_in_executor( self.executor, partial(self._add_filter_tc, kind, device_id, **kwargs)) async def run_routine(self, routine): return await self.loop.run_in_executor( self.executor, partial(self._run_routine, routine))
class TestIPRoute(object): def setup(self): self.ip = IPRoute() try: self.dev, idx = self.create() self.ifaces = [idx] except IndexError: pass def create(self, kind='dummy'): name = uifname() create_link(name, kind=kind) idx = self.ip.link_lookup(ifname=name)[0] return (name, idx) def teardown(self): if hasattr(self, 'ifaces'): for dev in self.ifaces: try: self.ip.link('delete', index=dev) except: pass self.ip.close() def _test_nla_operators(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) r = [x for x in self.ip.get_addr() if x['index'] == self.ifaces[0]] complement = r[0] - r[1] intersection = r[0] & r[1] assert complement.get_attr('IFA_ADDRESS') == '172.16.0.1' assert complement.get_attr('IFA_LABEL') is None assert complement['prefixlen'] == 0 assert complement['index'] == 0 assert intersection.get_attr('IFA_ADDRESS') is None assert intersection.get_attr('IFA_LABEL') == self.dev assert intersection['prefixlen'] == 24 assert intersection['index'] == self.ifaces[0] def test_addr_add(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) assert '172.16.0.1/24' in get_ip_addr() def test_flush_addr(self): require_user('root') self.ip.addr('add', self.ifaces[0], address='172.16.0.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.1.1', mask=24) self.ip.addr('add', self.ifaces[0], address='172.16.1.2', mask=24) assert len( self.ip.get_addr(index=self.ifaces[0], family=socket.AF_INET)) == 4 self.ip.flush_addr(index=self.ifaces[0]) assert len( self.ip.get_addr(index=self.ifaces[0], family=socket.AF_INET)) == 0 def test_flush_rules(self): require_user('root') init = len(self.ip.get_rules(family=socket.AF_INET)) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 self.ip.rule('add', table=10, priority=110) self.ip.rule('add', table=15, priority=150, action='FR_ACT_PROHIBIT') self.ip.rule('add', table=20, priority=200, src='172.16.200.1') self.ip.rule('add', table=25, priority=250, dst='172.16.250.1') assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 4 assert len(self.ip.get_rules(src='172.16.200.1')) == 1 assert len(self.ip.get_rules(dst='172.16.250.1')) == 1 self.ip.flush_rules(family=socket.AF_INET, priority=lambda x: 100 < x < 500) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 assert len(self.ip.get_rules(src='172.16.200.1')) == 0 assert len(self.ip.get_rules(dst='172.16.250.1')) == 0 assert len(self.ip.get_rules(family=socket.AF_INET)) == init def test_rules_deprecated(self): require_user('root') init = len(self.ip.get_rules(family=socket.AF_INET)) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 self.ip.rule('add', 10, 110) self.ip.rule('add', 15, 150, 'FR_ACT_PROHIBIT') assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 2 self.ip.flush_rules(family=socket.AF_INET, priority=lambda x: 100 < x < 500) assert len(self.ip.get_rules(priority=lambda x: 100 < x < 500)) == 0 assert len(self.ip.get_rules(family=socket.AF_INET)) == init def test_addr_filter(self): require_user('root') self.ip.addr('add', index=self.ifaces[0], address='172.16.0.1', prefixlen=24, broadcast='172.16.0.255') self.ip.addr('add', index=self.ifaces[0], address='172.16.0.2', prefixlen=24, broadcast='172.16.0.255') assert len(self.ip.get_addr(index=self.ifaces[0])) == 2 assert len(self.ip.get_addr(address='172.16.0.1')) == 1 assert len(self.ip.get_addr(broadcast='172.16.0.255')) == 2 assert len( self.ip.get_addr( match=lambda x: x['index'] == self.ifaces[0])) == 2 @skip_if_not_supported def _create(self, kind): name = uifname() self.ip.link_create(ifname=name, kind=kind) devs = self.ip.link_lookup(ifname=name) assert devs self.ifaces.extend(devs) def test_create_dummy(self): require_user('root') self._create('dummy') def test_create_bond(self): require_user('root') self._create('bond') def test_create_bridge(self): require_user('root') self._create('bridge') def test_create_ovs_bridge(self): require_user('root') self._create('ovs-bridge') def test_create_team(self): require_user('root') self._create('team') def test_ntables(self): setA = set( filter(lambda x: x is not None, [ x.get_attr('NDTA_PARMS').get_attr('NDTPA_IFINDEX') for x in self.ip.get_ntables() ])) setB = set([x['index'] for x in self.ip.get_links()]) assert setA == setB def test_neigh_real_links(self): links = set([x['index'] for x in self.ip.get_links()]) neigh = set([x['ifindex'] for x in self.ip.get_neighbours()]) assert neigh < links def test_neigh_filter(self): require_user('root') # inject arp records self.ip.neigh('add', dst='172.16.45.1', lladdr='00:11:22:33:44:55', ifindex=self.ifaces[0]) self.ip.neigh('add', dst='172.16.45.2', lladdr='00:11:22:33:44:55', ifindex=self.ifaces[0]) # assert two arp records on the interface assert len(self.ip.get_neighbours(ifindex=self.ifaces[0])) == 2 # filter by dst assert len(self.ip.get_neighbours(dst='172.16.45.1')) == 1 # filter with lambda assert len( self.ip.get_neighbours( match=lambda x: x['ifindex'] == self.ifaces[0])) == 2 def test_mass_ipv6(self): # # Achtung! This test is time consuming. # It is really time consuming, I'm not not # kidding you. Beware. # require_user('root') base = 'fdb3:84e5:4ff4:55e4::{0}' limit = int(os.environ.get('PYROUTE2_SLIMIT', '0x800'), 16) # add addresses for idx in range(limit): self.ip.addr('add', self.ifaces[0], base.format(hex(idx)[2:]), 48) # assert addresses in two steps, to ease debug addrs = self.ip.get_addr(10) assert len(addrs) >= limit # clean up addresses # # it is not required, but if you don't do that, # you'll get this on the interface removal: # # >> kernel:BUG: soft lockup - CPU#0 stuck for ... # # so, not to scare people, remove addresses gracefully # one by one # # it also verifies all the addresses are in place for idx in reversed(range(limit)): self.ip.addr('delete', self.ifaces[0], base.format(hex(idx)[2:]), 48) def test_fail_not_permitted(self): try: self.ip.addr('add', 1, address='172.16.0.1', mask=24) except NetlinkError as e: if e.code != errno.EPERM: # Operation not permitted raise finally: try: self.ip.addr('delete', 1, address='172.16.0.1', mask=24) except: pass def test_fail_no_such_device(self): require_user('root') dev = sorted([i['index'] for i in self.ip.get_links()])[-1] + 10 try: self.ip.addr('add', dev, address='172.16.0.1', mask=24) except NetlinkError as e: if e.code != errno.ENODEV: # No such device raise def test_remove_link(self): require_user('root') try: self.ip.link_remove(self.ifaces[0]) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 0 def test_route_oif_as_iterable(self): require_user('root') spec = {'dst': '172.16.0.0', 'dst_len': 24, 'oif': (1, )} self.ip.route('add', **spec) rts = self.ip.get_routes(family=socket.AF_INET, dst='172.16.0.0') self.ip.route('del', **spec) assert len(rts) == 1 assert rts[0].get_attr('RTA_OIF') == 1 def test_route_get_target(self): if not self.ip.get_default_routes(table=254): return rts = self.ip.get_routes(family=socket.AF_INET, dst='8.8.8.8', table=254) assert len(rts) > 0 def test_route_get_by_spec(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', index=self.ifaces[0], address='172.16.60.1', mask=24) self.ip.addr('add', index=self.ifaces[0], address='172.16.61.1', mask=24) rts = self.ip.get_routes(family=socket.AF_INET, dst=lambda x: x in ('172.16.60.0', '172.16.61.0')) assert len(rts) == 4 @skip_if_not_supported def _test_route_mpls_via_ipv(self, family, address, label): require_user('root') require_kernel(3) self.ip.route( 'add', **{ 'family': AF_MPLS, 'oif': self.ifaces[0], 'via': { 'family': family, 'addr': address }, 'newdst': { 'label': label, 'bos': 1 } }) rt = self.ip.get_routes(oif=self.ifaces[0])[0] assert rt.get_attr('RTA_VIA')['addr'] == address assert rt.get_attr('RTA_VIA')['family'] == family assert rt.get_attr('RTA_NEWDST')[0]['label'] == label assert len(rt.get_attr('RTA_NEWDST')) == 1 self.ip.route( 'del', **{ 'family': AF_MPLS, 'oif': self.ifaces[0], 'dst': { 'label': 0x10, 'bos': 1 }, 'via': { 'family': family, 'addr': address }, 'newdst': { 'label': label, 'bos': 1 } }) assert len(self.ip.get_routes(oif=self.ifaces[0])) == 0 def test_route_mpls_via_ipv4(self): self._test_route_mpls_via_ipv(socket.AF_INET, '172.16.0.1', 0x20) def test_route_mpls_via_ipv6(self): self._test_route_mpls_via_ipv(socket.AF_INET6, 'fe80::5054:ff:fe4b:7c32', 0x20) @skip_if_not_supported def test_route_mpls_swap_newdst_simple(self): require_user('root') require_kernel(3) req = { 'family': AF_MPLS, 'oif': self.ifaces[0], 'dst': { 'label': 0x20, 'bos': 1 }, 'newdst': { 'label': 0x21, 'bos': 1 } } self.ip.route('add', **req) rt = self.ip.get_routes(oif=self.ifaces[0])[0] assert rt.get_attr('RTA_DST')[0]['label'] == 0x20 assert len(rt.get_attr('RTA_DST')) == 1 assert rt.get_attr('RTA_NEWDST')[0]['label'] == 0x21 assert len(rt.get_attr('RTA_NEWDST')) == 1 self.ip.route('del', **req) assert len(self.ip.get_routes(oif=self.ifaces[0])) == 0 @skip_if_not_supported def test_route_mpls_swap_newdst_list(self): require_user('root') require_kernel(3) req = { 'family': AF_MPLS, 'oif': self.ifaces[0], 'dst': { 'label': 0x20, 'bos': 1 }, 'newdst': [{ 'label': 0x21, 'bos': 1 }] } self.ip.route('add', **req) rt = self.ip.get_routes(oif=self.ifaces[0])[0] assert rt.get_attr('RTA_DST')[0]['label'] == 0x20 assert len(rt.get_attr('RTA_DST')) == 1 assert rt.get_attr('RTA_NEWDST')[0]['label'] == 0x21 assert len(rt.get_attr('RTA_NEWDST')) == 1 self.ip.route('del', **req) assert len(self.ip.get_routes(oif=self.ifaces[0])) == 0 def test_route_multipath(self): require_user('root') self.ip.route('add', dst='172.16.241.0', mask=24, multipath=[{ 'hops': 20, 'ifindex': 1, 'attrs': [['RTA_GATEWAY', '127.0.0.2']] }, { 'hops': 30, 'ifindex': 1, 'attrs': [['RTA_GATEWAY', '127.0.0.3']] }]) assert grep('ip route show', pattern='172.16.241.0/24') assert grep('ip route show', pattern='nexthop.*127.0.0.2.*weight 21') assert grep('ip route show', pattern='nexthop.*127.0.0.3.*weight 31') self.ip.route('del', dst='172.16.241.0', mask=24) def test_route_multipath_helper(self): require_user('root') req = IPRouteRequest({ 'dst': '172.16.242.0/24', 'multipath': [{ 'hops': 20, 'ifindex': 1, 'gateway': '127.0.0.2' }, { 'hops': 30, 'ifindex': 1, 'gateway': '127.0.0.3' }] }) self.ip.route('add', **req) assert grep('ip route show', pattern='172.16.242.0/24') assert grep('ip route show', pattern='nexthop.*127.0.0.2.*weight 21') assert grep('ip route show', pattern='nexthop.*127.0.0.3.*weight 31') self.ip.route('del', dst='172.16.242.0', mask=24) def test_route_change_existing(self): # route('replace', ...) should succeed, if route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) self.ip.route('add', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.route('change', dst='172.16.1.0', mask=24, gateway='172.16.0.2', table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') def test_route_change_not_existing_fail(self): # route('change', ...) should fail, if no route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') try: self.ip.route('change', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) except NetlinkError as e: if e.code != errno.ENOENT: raise def test_route_replace_existing(self): # route('replace', ...) should succeed, if route exists require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.50', mask=24) self.ip.route('replace', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.route('replace', dst='172.16.1.0', mask=24, gateway='172.16.0.2', table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.2') def test_route_replace_not_existing(self): # route('replace', ...) should succeed, if route doesn't exist require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('replace', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') def test_flush_routes(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('add', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=100) self.ip.route('add', dst='172.16.2.0', mask=24, gateway='172.16.0.1', table=100) assert grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert grep('ip route show table 100', pattern='172.16.2.0/24.*172.16.0.1') self.ip.flush_routes(table=100) assert not grep('ip route show table 100', pattern='172.16.1.0/24.*172.16.0.1') assert not grep('ip route show table 100', pattern='172.16.2.0/24.*172.16.0.1') def test_route_table_2048(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], address='172.16.0.2', mask=24) self.ip.route('add', dst='172.16.1.0', mask=24, gateway='172.16.0.1', table=2048) assert grep('ip route show table 2048', pattern='172.16.1.0/24.*172.16.0.1') remove_link('bala') def test_symbolic_flags_ifaddrmsg(self): require_user('root') self.ip.link('set', index=self.ifaces[0], state='up') self.ip.addr('add', self.ifaces[0], '172.16.1.1', 24) addr = [ x for x in self.ip.get_addr() if x.get_attr('IFA_LOCAL') == '172.16.1.1' ][0] assert 'IFA_F_PERMANENT' in addr.flags2names(addr['flags']) def test_symbolic_flags_ifinfmsg(self): require_user('root') self.ip.link('set', index=self.ifaces[0], flags=['IFF_UP']) iface = self.ip.get_links(self.ifaces[0])[0] assert iface['flags'] & 1 assert 'IFF_UP' in iface.flags2names(iface['flags']) self.ip.link('set', index=self.ifaces[0], flags=['!IFF_UP']) assert not (self.ip.get_links(self.ifaces[0])[0]['flags'] & 1) def test_updown_link(self): require_user('root') try: self.ip.link_up(*self.ifaces) except NetlinkError: pass assert self.ip.get_links(*self.ifaces)[0]['flags'] & 1 try: self.ip.link_down(*self.ifaces) except NetlinkError: pass assert not (self.ip.get_links(*self.ifaces)[0]['flags'] & 1) def test_callbacks_positive(self): require_user('root') dev = self.ifaces[0] self.cb_counter = 0 self.ip.register_callback(_callback, lambda x: x.get('index', None) == dev, (self, )) self.test_updown_link() assert self.cb_counter > 0 self.ip.unregister_callback(_callback) def test_callbacks_negative(self): require_user('root') self.cb_counter = 0 self.ip.register_callback(_callback, lambda x: x.get('index', None) == -1, (self, )) self.test_updown_link() assert self.cb_counter == 0 self.ip.unregister_callback(_callback) def test_rename_link(self): require_user('root') dev = self.ifaces[0] try: self.ip.link_rename(dev, 'bala') except NetlinkError: pass assert len(self.ip.link_lookup(ifname='bala')) == 1 try: self.ip.link_rename(dev, self.dev) except NetlinkError: pass assert len(self.ip.link_lookup(ifname=self.dev)) == 1 def test_rules(self): assert len(get_ip_rules('-4')) == \ len(self.ip.get_rules(socket.AF_INET)) assert len(get_ip_rules('-6')) == \ len(self.ip.get_rules(socket.AF_INET6)) def test_addr(self): assert len(get_ip_addr()) == len(self.ip.get_addr()) def test_links(self): assert len(get_ip_link()) == len(self.ip.get_links()) def test_one_link(self): lo = self.ip.get_links(1)[0] assert lo.get_attr('IFLA_IFNAME') == 'lo' def test_default_routes(self): assert len(get_ip_default_routes()) == \ len(self.ip.get_default_routes(family=socket.AF_INET, table=254)) def test_routes(self): assert len(get_ip_route()) == \ len(self.ip.get_routes(family=socket.AF_INET, table=255))
def test_simple(self): ip = IPRoute() ip.close()
def test_simple(self): ip = IPRoute() ip.close()
def test_multiple_instances(self): ip1 = IPRoute() ip2 = IPRoute() ip1.close() ip2.close()
def RemoveRoute(self, removeRouteMessage, context): ip = IPRoute() idx = ip.link_lookup(ifname=removeRouteMessage.device)[0] ip.route('delete', dst=removeRouteMessage.prefix + '/128', oif=idx) ip.close() return parameters_pb2.RmRouteReply(message=str(0))
class NetworkBase(Base): def __init__(self): Base.__init__(self) self.ipr = IPRoute() self.nodes = [] def add_node(self, node): assert isinstance(node, Node) self.nodes.append(node) def get_interface_name(self, source, dest): assert isinstance(source, Node) assert isinstance(dest, Node) interface_name = "veth-" + source.name + "-" + dest.name return interface_name def get_interface(self, ifname): interfaces = self.ipr.link_lookup(ifname=ifname) if len(interfaces) != 1: raise Exception("Could not identify interface " + ifname) ix = interfaces[0] assert isinstance(ix, int) return ix def set_interface_ipaddress(self, node, ifname, address, mask): # Ask a node to set the specified interface address if address is None: return assert isinstance(node, Node) command = [ "ip", "addr", "add", str(address) + "/" + str(mask), "dev", str(ifname) ] result = node.execute(command) assert (result == 0) def create_link(self, src, dest): assert isinstance(src, Endpoint) assert isinstance(dest, Endpoint) ifname = self.get_interface_name(src.parent, dest.parent) destname = self.get_interface_name(dest.parent, src.parent) self.ipr.link_create(ifname=ifname, kind="veth", peer=destname) self.message("Create", ifname, "link") # Set source endpoint information ix = self.get_interface(ifname) self.ipr.link("set", index=ix, address=src.mac_addr) # push source endpoint into source namespace self.ipr.link("set", index=ix, net_ns_fd=src.parent.get_ns_name(), state="up") # Set interface ip address; seems to be # lost of set prior to moving to namespace self.set_interface_ipaddress(src.parent, ifname, src.ipaddress, src.prefixlen) # Sef destination endpoint information ix = self.get_interface(destname) self.ipr.link("set", index=ix, address=dest.mac_addr) # push destination endpoint into the destination namespace self.ipr.link("set", index=ix, net_ns_fd=dest.parent.get_ns_name(), state="up") # Set interface ip address self.set_interface_ipaddress(dest.parent, destname, dest.ipaddress, dest.prefixlen) def show_interfaces(self, node): cmd = ["ip", "addr"] if node is None: # Run with no namespace subprocess.call(cmd) else: # Run in node's namespace assert isinstance(node, Node) self.message("Enumerating all interfaces in ", node.name) node.execute(cmd) def delete(self): self.message("Deleting virtual network") for n in self.nodes: n.remove() self.ipr.close()
def addroute(self): ip = IPRoute() ip.route('add', dst='1.1.0.0',gateway='10.197.124.254') ip.close()