Exemplo n.º 1
0
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
Exemplo n.º 2
0
 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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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)
Exemplo n.º 5
0
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
Exemplo n.º 6
0
    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()
Exemplo n.º 7
0
 def test_iproute(self):
     ip = IPRoute()
     try:
         assert len(ip.get_links()) > 1
     except:
         raise
     finally:
         ip.close()
Exemplo n.º 8
0
        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)
Exemplo n.º 9
0
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
Exemplo n.º 10
0
 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__()
Exemplo n.º 11
0
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']})
Exemplo n.º 12
0
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
Exemplo n.º 13
0
    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()
Exemplo n.º 14
0
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
Exemplo n.º 15
0
    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
Exemplo n.º 16
0
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
Exemplo n.º 17
0
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
Exemplo n.º 18
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()
Exemplo n.º 19
0
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
Exemplo n.º 20
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)
Exemplo n.º 21
0
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)
Exemplo n.º 22
0
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)
Exemplo n.º 23
0
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()
Exemplo n.º 24
0
 def test_multiple_instances(self):
     ip1 = IPRoute()
     ip2 = IPRoute()
     ip1.close()
     ip2.close()
Exemplo n.º 25
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()
Exemplo n.º 26
0
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))
Exemplo n.º 27
0
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:
Exemplo n.º 28
0
    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)
Exemplo n.º 29
0
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
Exemplo n.º 30
0
    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
Exemplo n.º 31
0
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()
Exemplo n.º 32
0
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']})
Exemplo n.º 33
0
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)
Exemplo n.º 34
0
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))
Exemplo n.º 35
0
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']
                    })
Exemplo n.º 36
0
    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)
Exemplo n.º 37
0
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))
Exemplo n.º 38
0
    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)
Exemplo n.º 39
0
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
Exemplo n.º 40
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))
Exemplo n.º 41
0
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()
Exemplo n.º 42
0
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])
Exemplo n.º 43
0
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()
Exemplo n.º 44
0
Arquivo: tun.py Projeto: rshch1/tuntap
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()
Exemplo n.º 45
0
    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
Exemplo n.º 46
0
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')
Exemplo n.º 47
0
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)
Exemplo n.º 48
0
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()
Exemplo n.º 49
0
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))
Exemplo n.º 50
0
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))
Exemplo n.º 51
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_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))
Exemplo n.º 52
0
 def test_simple(self):
     ip = IPRoute()
     ip.close()
Exemplo n.º 53
0
 def test_simple(self):
     ip = IPRoute()
     ip.close()
Exemplo n.º 54
0
 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))
Exemplo n.º 56
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()
Exemplo n.º 57
0
 def addroute(self):
     ip = IPRoute()
     ip.route('add', dst='1.1.0.0',gateway='10.197.124.254')
     ip.close()