Example #1
0
    def create(self, do_apply, excpts, oper="add"):
        logger.info(oper,
                    extra={
                        'iface': self.settings['ifname'],
                        'style': IfStateLogging.STYLE_CHG
                    })

        logger.debug("ip link add: {}".format(" ".join(
            "{}={}".format(k, v) for k, v in self.settings.items())))
        if do_apply:
            try:
                state = self.settings.pop('state', None)
                ipr.link('add', **(self.settings))
                self.idx = next(
                    iter(ipr.link_lookup(ifname=self.settings['ifname'])),
                    None)
                if not state is None and not self.idx is None:
                    try:
                        ipr.link('set', index=self.idx, state=state)
                    except Exception as err:
                        if not isinstance(err, netlinkerror_classes):
                            raise
                        excpts.add('set', err, state=state)
            except Exception as err:
                if not isinstance(err, netlinkerror_classes):
                    raise
                excpts.add('add', err, **(self.settings))

        if not self.ethtool is None:
            logger.debug("ethtool: {}".format(self.ethtool))
            self.set_ethtool_state(self.settings['ifname'],
                                   self.ethtool.keys(), do_apply)
Example #2
0
    def apply(self, do_apply):
        excpts = ExceptionCollector(ifname=self.iface)

        # get ifindex
        self.idx = next(iter(ipr.link_lookup(ifname=self.iface)), None)

        if self.idx == None:
            logger.warning('link missing', extra={'iface': self.iface})
            return

        changes = []
        ipr_qdiscs = None

        # apply ingress qdics
        if "ingress" in self.tc:
            ipr_qdiscs = ipr.get_qdiscs(index=self.idx)
            if self.apply_ingress(self.tc["ingress"],
                                  self.get_qdisc(
                                      ipr_qdiscs, TC.INGRESS_PARENT),
                                  excpts,
                                  do_apply):
                changes.append("ingress")

        # apply qdisc tree
        if "qdisc" in self.tc:
            if ipr_qdiscs is None:
                ipr_qdiscs = ipr.get_qdiscs(index=self.idx)
            logger.debug('checking qdisc tree', extra={'iface': self.iface})
            if self.apply_qtree(
                    self.tc["qdisc"],
                    self.get_qdisc(ipr_qdiscs, TC.ROOT_HANDLE),
                    ipr_qdiscs,
                    TC.ROOT_HANDLE,
                    excpts,
                    do_apply):
                changes.append("qdisc")

        # apply filters
        if "filter" in self.tc:
            ipr_filters = ipr.get_filters(
                index=self.idx) + ipr.get_filters(index=self.idx, parent=TC.INGRESS_HANDLE)
            logger.debug('checking filters', extra={'iface': self.iface})
            if self.apply_filter(
                    self.tc["filter"],
                    ipr_filters,
                    excpts,
                    do_apply):
                changes.append("filter")

        if len(changes) > 0:
            logger.info(
                'change ({})'.format(", ".join(changes)),
                extra={'iface': self.iface, 'style': IfStateLogging.STYLE_CHG})
        else:
            logger.info(
                'ok',
                extra={'iface': self.iface, 'style': IfStateLogging.STYLE_OK})

        return excpts
Example #3
0
    def apply(self, ignores, do_apply):
        logger.info('\nconfiguring routing rules...')
        krules = self.kernel_rules()
        for rule in self.rules:
            found = False
            for i, krule in enumerate(krules):
                if rule_matches(rule, krule, rule.keys()):
                    del krules[i]
                    found = True
                    break

            if found:
                logger.info('ok',
                            extra={
                                'iface': '#{}'.format(rule['priority']),
                                'style': IfStateLogging.STYLE_OK
                            })
            else:
                logger.info('add',
                            extra={
                                'iface': '#{}'.format(rule['priority']),
                                'style': IfStateLogging.STYLE_CHG
                            })

                logger.debug("ip rule add: {}".format(" ".join(
                    "{}={}".format(k, v) for k, v in rule.items())))
                try:
                    if do_apply:
                        ipr.rule('add', **rule)
                except Exception as err:
                    if not isinstance(err, netlinkerror_classes):
                        raise
                    logger.warning('rule setup failed: {}'.format(err.args[1]))

        for rule in krules:
            ignore = False
            for irule in ignores:
                if 'proto' in irule:
                    irule['protocol'] = irule['proto']
                    del irule['proto']
                if rule_matches(rule, irule, irule.keys()):
                    ignore = True
                    break
            if ignore:
                continue

            logger.info('del',
                        extra={
                            'iface': '#{}'.format(rule['priority']),
                            'style': IfStateLogging.STYLE_DEL
                        })
            try:
                if do_apply:
                    ipr.rule('del', **rule)
            except Exception as err:
                if not isinstance(err, netlinkerror_classes):
                    raise
                logger.warning('removing rule failed: {}'.format(err.args[1]))
Example #4
0
    def __init__(self, name):
        self.name = name
        self.str2id = {}
        self.id2str = {}

        try:
            fn = os.path.join('/etc/iproute2', name)
            with open(fn, 'r') as fp:
                self._parse(fp)

            for fn in glob(
                    os.path.join('/etc/iproute2', "{}.d".format(name),
                                 "*.conf")):
                with open(fn, 'r') as fp:
                    self._parse(fp)
        except:
            logger.info('could not open {}'.format(fn))
Example #5
0
    def set_ethtool_state(self, ifname, settings, do_apply):
        if len(settings) == 0:
            return

        logger.info('change (ethtool)',
                    extra={
                        'iface': self.settings['ifname'],
                        'style': IfStateLogging.STYLE_CHG
                    })

        if not do_apply:
            return

        for setting in settings:
            cmd = [ethtool_path]
            if setting in ['coalesce', 'features', 'pause', 'rxfh']:
                cmd.append("--{}".format(setting))
            elif setting in ['nfc']:
                cmd.append("--config-{}".format(setting))
            else:
                cmd.append("--set-{}".format(setting))
            cmd.append(ifname)
            for option, value in self.ethtool[setting].items():
                cmd.extend([option] + self.fmt_ethtool_opt(value))
            logger.debug("{}".format(" ".join(cmd)))
            try:
                res = subprocess.run(cmd)
                if res.returncode != 0:
                    logger.warning('`{}` has failed'.format(" ".join(
                        cmd[0:3])))
                    return
            except Exception as err:
                logger.warning('failed to run `{}`: {}'.format(
                    " ".join(cmd[0:3]), err.args[1]))
                return

            fn = self.get_ethtool_fn(setting)
            try:
                with open(fn, 'w') as fh:
                    yaml.dump(self.ethtool[setting], fh)
            except Exception as err:
                logger.warning('failed write `{}`: {}'.format(fn, err.args[1]))
Example #6
0
 def set_sysctl(self, iface, family, key, val, do_apply):
     fn = '/proc/sys/net/{}/conf/{}/{}'.format(family, iface, key)
     with open(fn) as fh:
         current = fh.readline().rstrip()
     if current == str(val):
         logger.info('ok',
                     extra={
                         'iface': "{}/{}".format(family, key),
                         'style': IfStateLogging.STYLE_OK
                     })
     else:
         logger.info('set',
                     extra={
                         'iface': "{}/{}".format(family, key),
                         'style': IfStateLogging.STYLE_CHG
                     })
         if do_apply:
             try:
                 with open(fn, 'w') as fh:
                     fh.writelines([str(val)])
             except OSError as err:
                 logger.warning('updating sysctl {}/{} failed: {}'.format(
                     family, key, err.args[1]))
Example #7
0
    def apply(self, do_apply):
        logger.debug('getting neighbours', extra={'iface': self.iface})

        # get ifindex
        idx = next(iter(ipr.link_lookup(ifname=self.iface)), None)

        if idx == None:
            logger.warning('link missing', extra={'iface': self.iface})
            return

        # get neighbour entries (only NUD_PERMANENT)
        ipr_neigh = {}
        neigh_add = {}
        for neigh in ipr.get_neighbours(ifindex=idx, state=128):
            ip = ip_address(neigh.get_attr('NDA_DST'))
            ipr_neigh[ip] = neigh.get_attr('NDA_LLADDR')

        for ip, lladdr in self.neighbours.items():
            if ip in ipr_neigh and lladdr == ipr_neigh[ip]:
                logger.info(' %s', ip, extra={
                            'iface': self.iface, 'style': IfStateLogging.STYLE_OK})
                del ipr_neigh[ip]
            else:
                neigh_add[ip] = lladdr

        for ip, lladdr in ipr_neigh.items():
            logger.info(
                '-%s', str(ip), extra={'iface': self.iface, 'style': IfStateLogging.STYLE_DEL})
            try:
                if do_apply:
                    ipr.neigh("del", ifindex=idx, dst=str(
                        ip))
            except Exception as err:
                if not isinstance(err, netlinkerror_classes):
                    raise
                logger.warning('removing neighbour {} failed: {}'.format(
                    str(ip), err.args[1]))

        for ip, lladdr in neigh_add.items():
            logger.info('+%s', str(ip),
                        extra={'iface': self.iface, 'style': IfStateLogging.STYLE_CHG})
            if do_apply:
                try:
                    opts = {
                        'ifindex': idx,
                        'dst': str(ip),
                        'lladdr': lladdr,
                        'state': 128
                    }

                    ipr.neigh('replace', **opts)
                except Exception as err:
                    if not isinstance(err, netlinkerror_classes):
                        raise
                    logger.warning('adding neighbour {} failed: {}'.format(
                        str(ip), err.args[1]))
Example #8
0
    def apply(self, ignore, ign_dynamic, do_apply):
        logger.debug('getting addresses', extra={'iface': self.iface})

        # get ifindex
        idx = next(iter(ipr.link_lookup(ifname=self.iface)), None)

        if idx == None:
            logger.warning('link missing', extra={'iface': self.iface})
            return

        # get active ip addresses
        ipr_addr = {}
        addr_add = []
        for addr in ipr.get_addr(index=idx):
            ip = ip_interface(
                addr.get_attr('IFA_ADDRESS') + '/' + str(addr['prefixlen']))
            ipr_addr[ip] = addr

        for addr in self.addresses:
            if addr in ipr_addr:
                logger.info(' %s',
                            addr.with_prefixlen,
                            extra={
                                'iface': self.iface,
                                'style': IfStateLogging.STYLE_OK
                            })
                del ipr_addr[addr]
            else:
                addr_add.append(addr)

        for ip, addr in ipr_addr.items():
            if not any(ip in net for net in ignore):
                if not ign_dynamic or ipr_addr[ip][
                        'flags'] & IFA_F_PERMANENT == IFA_F_PERMANENT:
                    logger.info('-%s',
                                ip.with_prefixlen,
                                extra={
                                    'iface': self.iface,
                                    'style': IfStateLogging.STYLE_DEL
                                })
                    try:
                        if do_apply:
                            ipr.addr("del",
                                     index=idx,
                                     address=str(ip.ip),
                                     mask=ip.network.prefixlen)
                    except Exception as err:
                        if not isinstance(err, netlinkerror_classes):
                            raise
                        logger.warning('removing ip {}/{} failed: {}'.format(
                            str(ip.ip), ip.network.prefixlen, err.args[1]))

        for addr in addr_add:
            logger.info('+%s',
                        addr.with_prefixlen,
                        extra={
                            'iface': self.iface,
                            'style': IfStateLogging.STYLE_CHG
                        })
            if do_apply:
                try:
                    ipr.addr("add",
                             index=idx,
                             address=str(addr.ip),
                             mask=addr.network.prefixlen)
                except Exception as err:
                    if not isinstance(err, netlinkerror_classes):
                        raise
                    logger.warning('adding ip {}/{} failed: {}'.format(
                        str(addr.ip), addr.network.prefixlen, err.args[1]))
Example #9
0
    def apply(self, ignores, do_apply):
        for table, croutes in self.tables.items():
            pfx = RTLookups.tables.lookup_str(table)
            logger.info('\nconfiguring routing table {}...'.format(pfx))

            kroutes = self.kernel_routes(table)

            for route in croutes:
                if 'oif' in route and type(route['oif']) == str:
                    route['oif'] = next(
                        iter(ipr.link_lookup(ifname=route['oif'])), None)
                found = False
                identical = False
                for i, kroute in enumerate(kroutes):
                    if route_matches(route, kroute):
                        del kroutes[i]
                        found = True
                        if route_matches(route,
                                         kroute,
                                         route.keys(),
                                         indent=route['dst']):
                            identical = True
                            break

                if identical:
                    logger.info('ok',
                                extra={
                                    'iface': route['dst'],
                                    'style': IfStateLogging.STYLE_OK
                                })
                else:
                    if found:
                        logger.info('change',
                                    extra={
                                        'iface': route['dst'],
                                        'style': IfStateLogging.STYLE_CHG
                                    })
                    else:
                        logger.info('add',
                                    extra={
                                        'iface': route['dst'],
                                        'style': IfStateLogging.STYLE_CHG
                                    })

                    logger.debug("ip route replace: {}".format(" ".join(
                        "{}={}".format(k, v) for k, v in route.items())))
                    try:
                        if do_apply:
                            ipr.route('replace', **route)
                    except Exception as err:
                        if not isinstance(err, netlinkerror_classes):
                            raise
                        logger.warning('route setup {} failed: {}'.format(
                            route['dst'], err.args[1]))

            for route in kroutes:
                ignore = False
                for iroute in ignores:
                    if route_matches(route, iroute, iroute.keys()):
                        ignore = True
                        break
                if ignore:
                    continue

                logger.info('del',
                            extra={
                                'iface': route['dst'],
                                'style': IfStateLogging.STYLE_DEL
                            })
                try:
                    if do_apply:
                        ipr.route('del', **route)
                except Exception as err:
                    if not isinstance(err, netlinkerror_classes):
                        raise
                    logger.warning('removing route {} failed: {}'.format(
                        route['dst'], err.args[1]))
Example #10
0
    def apply(self, do_apply):
        # get kernel's wireguard settings for the interface
        try:
            state = wg.get_interface(
                self.iface, spill_private_key=True, spill_preshared_keys=True)
        except Exception as err:
            logger.warning('WireGuard on {} failed: {}'.format(
                self.iface, err.args[1]))
            return

        # check base settings (not the peers, yet)
        has_changes = False
        for setting in [x for x in self.wireguard.keys() if x != 'peers']:
            logger.debug('  %s: %s => %s', setting, getattr(
                state, setting), self.wireguard[setting], extra={'iface': self.iface})
            has_changes |= self.wireguard[setting] != getattr(state, setting)

        if has_changes:
            logger.info('change [iface]', extra={
                        'iface': self.iface, 'style': IfStateLogging.STYLE_CHG})
            if do_apply:
                try:
                    wg.set_interface(
                        self.iface, **{k: v for k, v in self.wireguard.items() if k != "peers"})
                except Exception as err:
                    if not isinstance(err, netlinkerror_classes):
                        raise
                    logger.warning('updating iface {} failed: {}'.format(
                        self.iface, err.args[1]))
        else:
            logger.info('ok [iface]', extra={
                        'iface': self.iface, 'style': IfStateLogging.STYLE_OK})

        # check peers list if provided
        if 'peers' in self.wireguard:
            peers = getattr(state, 'peers')
            has_pchanges = False

            avail = []
            for peer in self.wireguard['peers']:
                avail.append(peer['public_key'])
                pubkey = next(
                    iter([x for x in peers.keys() if x == peer['public_key']]), None)
                if pubkey is None:
                    has_pchanges = True
                    if do_apply:
                        try:
                            wg.set_peer(self.iface, **peer)
                        except Exception as err:
                            if not isinstance(err, netlinkerror_classes):
                                raise
                            logger.warning('add peer to {} failed: {}'.format(
                                self.iface, err.args[1]))
                else:
                    pchange = False
                    for setting in peer.keys():
                        attr = getattr(peers[pubkey], setting)
                        if setting == 'allowedips':
                            attr = set(attr)
                        logger.debug('  peer.%s: %s => %s', setting, attr,
                                     peer[setting], extra={'iface': self.iface})
                        if type(attr) == set:
                            pchange |= not (attr == peer[setting])
                        else:
                            pchange |= str(peer[setting]) != str(getattr(
                                peers[pubkey], setting))

                    if pchange:
                        has_pchanges = True
                        if do_apply:
                            try:
                                wg.set_peer(self.iface, **peer)
                            except Exception as err:
                                if not isinstance(err, netlinkerror_classes):
                                    raise
                                logger.warning('change peer at {} failed: {}'.format(
                                    self.iface, err.args[1]))

            for peer in peers:
                if not peer in avail:
                    has_pchanges = True
                    if do_apply:
                        try:
                            wg.remove_peers(self.iface, peer)
                        except Exception as err:
                            if not isinstance(err, netlinkerror_classes):
                                raise
                            logger.warning('remove peer from {} failed: {}'.format(
                                self.iface, err.args[1]))
            if has_pchanges:
                logger.info('change [peers]', extra={
                            'iface': self.iface, 'style': IfStateLogging.STYLE_CHG})
            else:
                logger.info('ok [peers]', extra={
                            'iface': self.iface, 'style': IfStateLogging.STYLE_OK})
Example #11
0
    def update(self, do_apply, excpts):
        logger.debug('checking link', extra={'iface': self.settings['ifname']})

        old_state = self.iface['state']
        has_link_changes = False
        has_state_changes = False
        for setting in self.settings.keys():
            logger.debug('  %s: %s => %s',
                         setting,
                         self.get_if_attr(setting),
                         self.settings[setting],
                         extra={'iface': self.settings['ifname']})
            if setting == "state":
                has_state_changes = self.get_if_attr(
                    setting) != self.settings[setting]
            else:
                if setting != 'kind' or self.cap_create:
                    has_link_changes |= self.get_if_attr(
                        setting) != self.settings[setting]

        has_ethtool_changes = set()
        if not self.ethtool is None:
            logger.debug('checking ethtool',
                         extra={'iface': self.settings['ifname']})
            ethtool = self.get_ethtool_state(self.ethtool.keys())
            if ethtool is None:
                has_ethtool_changes.add(self.ethtool.keys())
            else:
                for setting, options in self.ethtool.items():
                    if not setting in ethtool:
                        has_ethtool_changes.add(setting)
                    else:
                        for option in options.keys():
                            logger.debug(
                                '  %s.%s: %s => %s',
                                setting,
                                option,
                                ethtool[setting].get(option),
                                self.ethtool[setting][option],
                                extra={'iface': self.settings['ifname']})
                            if self.ethtool[setting][option] != ethtool[
                                    setting].get(option):
                                has_ethtool_changes.add(setting)

        if has_link_changes:
            logger.debug('needs to be configured',
                         extra={'iface': self.settings['ifname']})
            if old_state:
                logger.debug('shutting down',
                             extra={'iface': self.settings['ifname']})
                if do_apply:
                    try:
                        ipr.link('set', index=self.idx, state='down')
                    except Exception as err:
                        if not isinstance(err, netlinkerror_classes):
                            raise
                        excpts.add('set', err, state='down')
                if not 'state' in self.settings:
                    self.settings['state'] = 'up'

            self.set_ethtool_state(self.get_if_attr('ifname'),
                                   has_ethtool_changes, do_apply)

            if self.get_if_attr('ifname') == self.settings['ifname']:
                logger.info('change',
                            extra={
                                'iface': self.settings['ifname'],
                                'style': IfStateLogging.STYLE_CHG
                            })
            else:
                logger.info('change (was {})'.format(
                    self.get_if_attr('ifname')),
                            extra={
                                'iface': self.settings['ifname'],
                                'style': IfStateLogging.STYLE_CHG
                            })
            if do_apply:
                try:
                    state = self.settings.pop('state', None)
                    ipr.link('set', index=self.idx, **(self.settings))
                except Exception as err:
                    if not isinstance(err, netlinkerror_classes):
                        raise
                    excpts.add('set', err, state=state)

                try:
                    if not state is None:
                        # restore state setting for recreate
                        self.settings['state'] = state
                        ipr.link('set', index=self.idx, state=state)
                except Exception as err:
                    if not isinstance(err, netlinkerror_classes):
                        raise
                    excpts.add('set', err, state=state)
        else:
            self.set_ethtool_state(self.get_if_attr('ifname'),
                                   has_ethtool_changes, do_apply)

            if has_state_changes:
                try:
                    ipr.link('set',
                             index=self.idx,
                             state=self.settings["state"])
                except Exception as err:
                    if not isinstance(err, netlinkerror_classes):
                        raise
                    excpts.add('set', err, state=state)
                logger.info('change',
                            extra={
                                'iface': self.settings['ifname'],
                                'style': IfStateLogging.STYLE_CHG
                            })

            else:
                logger.info('ok',
                            extra={
                                'iface': self.settings['ifname'],
                                'style': IfStateLogging.STYLE_OK
                            })
Example #12
0
    def _apply(self, do_apply, vrrp_type, vrrp_name, vrrp_state):
        vrrp_ignore = []
        vrrp_remove = []

        by_vrrp = not None in [vrrp_type, vrrp_name, vrrp_state]

        for ifname, link in self.links.items():
            if ifname in self.vrrp['links']:
                if not by_vrrp:
                    vrrp_ignore.append(ifname)
                else:
                    if not link.match_vrrp_select(vrrp_type, vrrp_name):
                        vrrp_ignore.append(ifname)
                    elif not vrrp_name in self.vrrp[
                            vrrp_type] or not vrrp_state in self.vrrp[
                                vrrp_type][
                                    vrrp_name] or not ifname in self.vrrp[
                                        vrrp_type][vrrp_name][vrrp_state]:
                        vrrp_remove.append(ifname)
            elif by_vrrp:
                vrrp_ignore.append(ifname)

        self.ipaddr_ignore = set()
        for ip in self.ignore.get('ipaddr', []):
            self.ipaddr_ignore.add(ip_network(ip))

        if not any(not x is None for x in self.links.values()):
            logger.error("DANGER: Not a single link config has been found!")
            raise LinkNoConfigFound()

        for iface in ['all', 'default']:
            if self.sysctl.has_settings(iface):
                logger.info("\nconfiguring {} interface sysctl".format(iface))
                self.sysctl.apply(iface, do_apply)

        for stage in range(2):
            if stage == 0:
                logger.info("\nconfiguring interface links")

                for ifname in vrrp_remove:
                    logger.debug('to be removed due to vrrp constraint',
                                 extra={'iface': ifname})
                    del self.links[ifname]
            else:
                logger.info("\nconfiguring interface links (stage 2)")

            retry = False
            applied = []
            while len(applied) + len(vrrp_ignore) < len(self.links):
                last = len(applied)
                for name, link in self.links.items():
                    if name in applied:
                        continue

                    if name in vrrp_ignore:
                        logger.debug('skipped due to vrrp constraint',
                                     extra={'iface': name})
                        continue

                    if link is None:
                        logger.debug('skipped due to no link settings',
                                     extra={'iface': name})
                        applied.append(name)
                    else:
                        deps = link.depends()
                        if all(x in applied for x in deps):
                            self.sysctl.apply(name, do_apply)
                            excpts = link.apply(do_apply)
                            if excpts.has_errno(errno.EEXIST):
                                retry = True
                            applied.append(name)
                if last == len(applied):
                    raise LinkCircularLinked()

            for link in ipr.get_links():
                name = link.get_attr('IFLA_IFNAME')
                # skip links on ignore list
                if not name in self.links and not any(
                        re.match(regex, name)
                        for regex in self.ignore.get('ifname', [])):
                    info = link.get_attr('IFLA_LINKINFO')
                    # remove virtual interface
                    if info is not None:
                        kind = info.get_attr('IFLA_INFO_KIND')
                        logger.info('del',
                                    extra={
                                        'iface': name,
                                        'style': IfStateLogging.STYLE_DEL
                                    })
                        if do_apply:
                            try:
                                ipr.link('set',
                                         index=link.get('index'),
                                         state='down')
                                ipr.link('del', index=link.get('index'))
                            except Exception as err:
                                if not isinstance(err, netlinkerror_classes):
                                    raise
                                logger.warning(
                                    'removing link {} failed: {}'.format(
                                        name, err.args[1]))
                    # shutdown physical interfaces
                    else:
                        if name in vrrp_ignore:
                            logger.warning('vrrp',
                                           extra={
                                               'iface': name,
                                               'style': IfStateLogging.STYLE_OK
                                           })
                        if link.get('state') == 'down':
                            logger.warning('orphan',
                                           extra={
                                               'iface': name,
                                               'style': IfStateLogging.STYLE_OK
                                           })
                        else:
                            logger.warning('orphan',
                                           extra={
                                               'iface': name,
                                               'style':
                                               IfStateLogging.STYLE_CHG
                                           })
                            if do_apply:
                                try:
                                    ipr.link('set',
                                             index=link.get('index'),
                                             state='down')
                                except Exception as err:
                                    if not isinstance(err,
                                                      netlinkerror_classes):
                                        raise
                                    logger.warning(
                                        'updating link {} failed: {}'.format(
                                            name, err.args[1]))
            if not retry:
                break

        if any(not x is None for x in self.tc.values()):
            logger.info("\nconfiguring interface traffic control...")

            for name, tc in self.tc.items():
                if name in vrrp_ignore:
                    logger.debug('skipped due to vrrp constraint',
                                 extra={'iface': name})
                elif tc is None:
                    logger.debug('skipped due to no tc settings',
                                 extra={'iface': name})
                else:
                    tc.apply(do_apply)

        if any(not x is None for x in self.addresses.values()):
            logger.info("\nconfiguring interface ip addresses...")
            # add empty objects for unhandled interfaces
            for link in ipr.get_links():
                name = link.get_attr('IFLA_IFNAME')
                # skip links on ignore list
                if not name in self.addresses and not any(
                        re.match(regex, name)
                        for regex in self.ignore.get('ifname', [])):
                    self.addresses[name] = Addresses(name, [])

            for name, addresses in self.addresses.items():
                if name in vrrp_ignore:
                    logger.debug('skipped due to vrrp constraint',
                                 extra={'iface': name})
                elif addresses is None:
                    logger.debug('skipped due to no address settings',
                                 extra={'iface': name})
                else:
                    addresses.apply(self.ipaddr_ignore,
                                    self.ignore.get('ipaddr_dynamic', True),
                                    do_apply)
        else:
            logger.info("\nno interface ip addressing to be applied")

        if any(not x is None for x in self.neighbours.values()):
            logger.info("\nconfiguring interface neighbours...")
            # add empty objects for unhandled interfaces
            for link in ipr.get_links():
                name = link.get_attr('IFLA_IFNAME')
                # skip links on ignore list
                if not name in self.neighbours and not any(
                        re.match(regex, name)
                        for regex in self.ignore.get('ifname', [])):
                    self.neighbours[name] = Neighbours(name, [])

            for name, neighbours in self.neighbours.items():
                if name in vrrp_ignore:
                    logger.debug('skipped due to vrrp constraint',
                                 extra={'iface': name})
                elif neighbours is None:
                    logger.debug('skipped due to no address settings',
                                 extra={'iface': name})
                else:
                    neighbours.apply(do_apply)
        else:
            logger.info("\nno interface neighbours to be applied")

        if not self.tables is None:
            self.tables.apply(self.ignore.get('routes', []), do_apply)

        if not self.rules is None:
            self.rules.apply(self.ignore.get('rules', []), do_apply)

        if len(self.wireguard):
            logger.info("\nconfiguring WireGuard...")
            for iface, wireguard in self.wireguard.items():
                if iface in vrrp_ignore:
                    logger.debug('skipped due to vrrp constraint',
                                 extra={'iface': name})
                    continue
                wireguard.apply(do_apply)