Example #1
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 #2
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 #3
0
    def apply_qtree(self, tc, qdisc, ipr_qdiscs, parent, excpts, do_apply, recreate=False):
        if qdisc is None:
            recreate = True

        if not recreate:
            logger.debug('  kind: {} => {}'.format(
                qdisc.get_attr("TCA_KIND"), tc["kind"]), extra={'iface': self.iface})
            if tc["kind"] != qdisc.get_attr("TCA_KIND"):
                recreate = True
            else:
                logger.debug('  handle: {} => {}'.format(
                    TC.int2handle(qdisc["handle"]), tc["handle"]), extra={'iface': self.iface})
                if TC.handle2int(tc["handle"]) != qdisc["handle"]:
                    recreate = True

        if recreate and do_apply:
            if qdisc is not None:
                try:
                    ipr.tc("del", index=self.idx, parent=qdisc["parent"])
                except:
                    pass

        opts = {
            "index": self.idx,
            "parent": TC.int2handle(parent),
        }
        for k, v in tc.items():
            if k != "children":
                opts[k] = v

        if do_apply:
            if recreate:
                try:
                    ipr.tc("add", **opts)
                except Exception as err:
                    if not isinstance(err, netlinkerror_classes):
                        raise
                    logger.warning('adding qdisc {} on {} failed: {}'.format(
                        opts.get("handle"), self.iface, err.args[1]))
                    excpts.add('add', err, **opts)
            else:
                # soft update for TCA_OPTIONS
                try:
                    ipr.tc("change", **opts)
                except Exception as err:
                    if not isinstance(err, netlinkerror_classes):
                        raise
                    logger.warning('updating qdisc {} on {} failed: {}'.format(
                        opts.get("handle"), self.iface, err.args[1]))
                    excpts.add('change', err, **opts)

        changes = recreate
        if "children" in tc:
            for i in range(len(tc["children"])):
                changes = changes or self.apply_qtree(tc["children"][i], self.get_qchild(
                    ipr_qdiscs, qdisc["handle"], i+1), ipr_qdiscs, TC.handle2int(tc["handle"]) | i + 1, do_apply, recreate)

        return changes
Example #4
0
 def add(self, op, excpt, **kwargs):
     self.excpts.append({
         'op': op,
         'excpt': excpt,
         'args': kwargs,
     })
     if not self.quiet:
         logger.warning('{} link {} failed: {}'.format(
             op, self.ifname, excpt.args[1]))
Example #5
0
    def get_ethtool_state(self, settings):
        ethtool = {}

        for setting in settings:
            ethtool[setting] = {}
            fn = self.get_ethtool_fn(setting)

            if not os.path.isfile(fn):
                logger.debug('no prior ethtool %s state available',
                             setting,
                             extra={'iface': self.settings['ifname']})
                continue

            try:
                with open(fn) as fh:
                    obj = yaml.load(fh, Loader=yaml.SafeLoader)
                    if type(obj) == dict:
                        ethtool[setting] = obj
            except Exception as err:
                logger.warning('parsing {} failed: {}'.format(fn, err))

        return ethtool
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_ingress(self, ingress, qdisc, excpts, do_apply):
        logger.debug('checking ingress qdisc', extra={'iface': self.iface})
        if not ingress:
            if qdisc:
                if do_apply:
                    opts = {
                        "index": self.idx,
                        "parent": TC.INGRESS_PARENT,
                    }
                    try:
                        ipr.tc("del", **opts)
                    except Exception as err:
                        if not isinstance(err, netlinkerror_classes):
                            raise
                        logger.warning('removing ingress qdisc on {} failed: {}'.format(
                            self.iface, err.args[1]))
                        excpts.add('del', err, **opts)
                return True
        else:
            if not qdisc:
                if do_apply:
                    opts = {
                        "index": self.idx,
                        "kind": "ingress",
                    }
                    try:
                        ipr.tc("add", **opts)
                    except Exception as err:
                        if not isinstance(err, netlinkerror_classes):
                            raise
                        logger.warning('adding ingress qdisc on {} failed: {}'.format(
                            self.iface, err.args[1]))
                        excpts.add('add', err, **opts)
                return True

        return False
Example #8
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 #9
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 #10
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)
Example #11
0
    def update(self, ifstates, soft_schema):
        # check config schema
        schema = json.loads(
            pkgutil.get_data("libifstate",
                             "../schema/ifstate.conf.schema.json"))
        try:
            validate(ifstates, schema, format_checker=FormatChecker())
        except ValidationError as ex:
            if len(ex.path) > 0:
                path = ["$"]
                for i, p in enumerate(ex.absolute_path):
                    if type(p) == int:
                        path.append("[{}]".format(p))
                    else:
                        path.append(".")
                        path.append(p)

                detail = "{}: {}".format("".join(path), ex.message)
            else:
                detail = ex.message
            if soft_schema:
                logger.error("Config validation failed for {}".format(detail))
            else:
                raise ParserValidationError(detail)

        # parse options
        if 'options' in ifstates:
            # parse global sysctl settings
            if 'sysctl' in ifstates['options']:
                for iface in ['all', 'default']:
                    if iface in ifstates['options']['sysctl']:
                        self.sysctl.add(iface,
                                        ifstates['options']['sysctl'][iface])

        # add interfaces from config
        for ifstate in ifstates['interfaces']:
            name = ifstate['name']
            if name in self.links:
                raise LinkDuplicate()
            if 'link' in ifstate:
                self.links[name] = Link(name, ifstate['link'],
                                        ifstate.get('ethtool'),
                                        ifstate.get('vrrp'))
            else:
                self.links[name] = None

            if 'addresses' in ifstate:
                self.addresses[name] = Addresses(name, ifstate['addresses'])
            else:
                self.addresses[name] = None

            if 'neighbours' in ifstate:
                self.neighbours[name] = Neighbours(name, ifstate['neighbours'])
            else:
                self.neighbours[name] = None

            if 'vrrp' in ifstate:
                ktype = ifstate['vrrp']['type']
                kname = ifstate['vrrp']['name']
                kstates = ifstate['vrrp']['states']
                if not kname in self.vrrp[ktype]:
                    self.vrrp[ktype][kname] = {}
                for kstate in kstates:
                    if not kstate in self.vrrp[ktype][kname]:
                        self.vrrp[ktype][kname][kstate] = []
                    self.vrrp[ktype][kname][kstate].append(name)
                self.vrrp['links'].append(name)

            if 'sysctl' in ifstate:
                self.sysctl.add(name, ifstate['sysctl'])

            if 'cshaper' in ifstate:
                profile_name = ifstate['cshaper'].get('profile', 'default')
                logger.debug('cshaper profile {} enabled'.format(profile_name),
                             extra={'iface': name})
                cshaper_profile = ifstates['cshaper'][profile_name]

                # ingress
                ifb_name = re.sub(cshaper_profile['ingress_ifname']['search'],
                                  cshaper_profile['ingress_ifname']['replace'],
                                  name)
                logger.debug('cshaper ifb name {}'.format(ifb_name),
                             extra={'iface': name})

                ifb_state = {
                    'name': ifb_name,
                    'link': {
                        'state': 'up',
                        'kind': 'ifb',
                    },
                    'tc': {
                        'qdisc': cshaper_profile['ingress_qdisc'],
                    }
                }
                ifb_state['tc']['qdisc']['bandwidth'] = ifstate['cshaper'].get(
                    'ingress', 'unlimited')

                ifstates['interfaces'].append(ifb_state)

                # egress
                if 'tc' in ifstate:
                    logger.warning('cshaper settings replaces tc settings',
                                   extra={'iface': name})

                ifstate['tc'] = {
                    'ingress':
                    True,
                    'qdisc':
                    cshaper_profile['egress_qdisc'],
                    'filter': [{
                        'kind':
                        'matchall',
                        'parent':
                        'ffff:',
                        'action': [{
                            'kind': 'mirred',
                            'direction': 'egress',
                            'action': 'redirect',
                            'dev': ifb_name,
                        }]
                    }]
                }

                ifstate['tc']['qdisc']['bandwidth'] = ifstate['cshaper'].get(
                    'egress', 'unlimited')

                del ifstate['cshaper']

            if 'tc' in ifstate:
                self.tc[name] = TC(name, ifstate['tc'])

            if 'wireguard' in ifstate:
                if not self.features['wireguard']:
                    raise FeatureMissingError("wireguard")

                self.wireguard[name] = WireGuard(name, ifstate['wireguard'])

        # add routing from config
        if 'routing' in ifstates:
            if 'routes' in ifstates['routing']:
                if self.tables is None:
                    self.tables = Tables()
                for route in ifstates['routing']['routes']:
                    self.tables.add(route)

            if 'rules' in ifstates['routing']:
                if self.rules is None:
                    self.rules = Rules()
                for rule in ifstates['routing']['rules']:
                    self.rules.add(rule)

        # add ignore list items
        self.ignore.update(ifstates['ignore'])
Example #12
0
    def apply_filter(self, tc, ipr_filters, excpts, do_apply):
        tc_filters = {}
        # assign prio numbers if missing
        for i in range(len(tc)):
            if not "prio" in tc[i]:
                tc[i]["prio"] = 0xc001 - len(tc) + i

            parent = TC.handle2int(tc[i].get("parent", 0))
            if not parent in tc_filters:
                tc_filters[parent] = {}

            tc_filters[parent][tc[i]["prio"]] = tc[i]

        changes = False
        # remove unreferenced filters
        removed = {}
        for ipr_filter in ipr_filters:
            prio = ipr_filter["info"] >> 16

            parent = TC.handle2int(ipr_filter.get("parent", 0))
            rm = not parent in tc_filters
            rm |= parent in tc_filters and \
                prio not in tc_filters[parent]
            if rm and prio not in removed.get(parent, []):
                changes = True
                if not parent in removed:
                    removed[parent] = []
                removed[parent].append(prio)
                if do_apply:
                    opts = {
                        "index": self.idx,
                        "info": ipr_filter["info"],
                        "parent": parent,
                    }
                    try:
                        ipr.del_filter_by_info(**opts)
                    except Exception as err:
                        if not isinstance(err, netlinkerror_classes):
                            raise
                        logger.warning('deleting filter #{} on {} failed: {}'.format(
                            prio, self.iface, err.args[1]))
                        excpts.add('del', err, **opts)

        if do_apply:
            for parent in tc_filters.keys():
                for tc_filter in tc_filters[parent].values():
                    tc_filter['index'] = self.idx
                    if "action" in tc_filter:
                        for action in tc_filter["action"]:
                            if action["kind"] == "mirred":
                                # get ifindex
                                action["ifindex"] = next(
                                    iter(ipr.link_lookup(ifname=action["dev"])), None)

                                if self.idx == None:
                                    logger.warning("filter #{} references unknown interface {}".format(
                                        tc_filter["prio"], action["dev"]), extra={'iface': self.iface})
                                    continue
                    if "parent" in tc_filter:
                        tc_filter["parent"] = TC.handle2int(tc_filter["parent"])
                    try:
                        try:
                            ipr.tc("replace-filter", **tc_filter)
                            # replace seems only to work if there is no filter
                            # => something has changed
                            changes = True
                        except Exception as err:
                            if not isinstance(err, netlinkerror_classes):
                                raise
                            # replace does not work, supress changes result
                            # for now
                            #changes = True
                            opts = {
                                "index": self.idx,
                                "info": tc_filter["prio"] << 16,
                                "parent": parent,
                            }
                            ipr.del_filter_by_info(**opts)
                            ipr.tc("add-filter", **tc_filter)

                    except Exception as err:
                        if not isinstance(err, netlinkerror_classes):
                            raise
                        logger.warning('replace filter #{} on {} failed: {}'.format(
                            tc_filter['prio'], self.iface, err.args[1]))
                        excpts.add('replace', err, **tc_filter)

        return changes
Example #13
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 #14
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 #15
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 #16
0
    def show(self, showall=False):
        if showall:
            defaults = deepcopy(Parser._default_ifstates)
        else:
            defaults = {}

        ipaddr_ignore = []
        for ip in Parser._default_ifstates['ignore']['ipaddr_builtin']:
            ipaddr_ignore.append(ip_network(ip))

        ifs_links = []
        for ipr_link in ipr.get_links():
            name = ipr_link.get_attr('IFLA_IFNAME')
            # skip links on ignore list
            if not any(
                    re.match(regex, name) for regex in
                    Parser._default_ifstates['ignore']['ifname_builtin']):
                ifs_link = {
                    'name': name,
                    'addresses': [],
                    'link': {
                        'state': ipr_link['state'],
                    },
                }

                for addr in ipr.get_addr(index=ipr_link['index']):
                    if addr['flags'] & IFA_F_PERMANENT == IFA_F_PERMANENT:
                        ip = ip_interface(
                            addr.get_attr('IFA_ADDRESS') + '/' +
                            str(addr['prefixlen']))
                        if not any(ip in net for net in ipaddr_ignore):
                            ifs_link['addresses'].append(ip.with_prefixlen)

                info = ipr_link.get_attr('IFLA_LINKINFO')
                if info is None:
                    kind = None
                else:
                    kind = info.get_attr('IFLA_INFO_KIND')
                if kind is not None:
                    ifs_link['link']['kind'] = kind

                    data = info.get_attr('IFLA_INFO_DATA')
                    # unsupported link type, fallback to raw encoding
                    if data is not None and type(data) != str:
                        for k, v in data['attrs']:
                            if k not in ['UNKNOWN', 'IFLA_VLAN_FLAGS']:
                                attr = ipr_link.nla2name(k)
                                if attr in Link.attr_value_maps:
                                    ifs_link['link'][
                                        attr] = Link.attr_value_maps[attr].get(
                                            v, v)
                                else:
                                    ifs_link['link'][attr] = v
                else:
                    ifs_link['link']['kind'] = 'physical'
                    addr = ipr_link.get_attr('IFLA_ADDRESS')
                    if not addr is None:
                        ifs_link['link']['address'] = addr
                    permaddr = ipr.get_permaddr(name)
                    if not permaddr is None:
                        if addr is None:
                            ifs_link['link']['addr'] = permaddr
                        elif addr != permaddr:
                            ifs_link['link']['permaddr'] = permaddr
                    businfo = ipr.get_businfo(name)
                    if not businfo is None:
                        ifs_link['link']['businfo'] = businfo

                for attr in [
                        'link', 'master', 'gre_link', 'ip6gre_link',
                        'vxlan_link', 'xfrm_link'
                ]:
                    ref = ipr_link.get_attr('IFLA_{}'.format(attr.upper()))
                    if ref is not None:
                        try:
                            ifs_link['link'][attr] = ipr.get_ifname_by_index(
                                ref)
                        except Exception as err:
                            if not isinstance(err, netlinkerror_classes):
                                raise
                            logger.warning('lookup {} failed: {}'.format(
                                attr, err.args[1]),
                                           extra={'iface': name})
                            ifs_link['link'][attr] = ref

                mtu = ipr_link.get_attr('IFLA_MTU')
                if not mtu is None and not mtu in [1500, 65536]:
                    ifs_link['link']['mtu'] = mtu

                ifs_links.append(ifs_link)

        routing = {
            'routes':
            Tables().show_routes(
                Parser._default_ifstates['ignore']['routes_builtin']),
            'rules':
            Rules().show_rules(
                Parser._default_ifstates['ignore']['rules_builtin']),
        }

        return {**defaults, **{'interfaces': ifs_links, 'routing': routing}}
Example #17
0
 def create(self, do_apply, oper="add"):
     logger.warning('Unable to create missing physical link: {}'.format(
         self.settings.get('ifname')))