<reponame>pimvanpelt/vppcfg
#
# Copyright (c) 2022 <NAME>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#     http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
""" A vppcfg configuration module that validates taps """
import logging
from config import mac


def get_taps(yaml):
    """Return a list of all taps."""
    ret = []
    if "taps" in yaml:
        for ifname, _iface in yaml["taps"].items():
            ret.append(ifname)
    return ret


def get_by_name(yaml, ifname):
    """Return the tap by name, if it exists. Return None otherwise."""
    try:
        if ifname in yaml["taps"]:
            return ifname, yaml["taps"][ifname]
    except KeyError:
        pass
    return None, None


def is_tap(yaml, ifname):
    """Returns True if the interface name is an existing tap in the config.
    The TAP has to be explicitly named in the configuration, and notably
    a TAP belonging to a Linux Control Plane (LCP) will return False.
    """
    ifname, iface = get_by_name(yaml, ifname)
    return iface is not None


def is_host_name_unique(yaml, hostname):
    """Returns True if there is at most one occurence of the given ifname amonst all host-names of TAPs."""
    if not "taps" in yaml:
        return True
    host_names = []
    for _tap_ifname, tap_iface in yaml["taps"].items():
        host_names.append(tap_iface["host"]["name"])
    return host_names.count(hostname) < 2


def validate_taps(yaml):
    """Validate the semantics of all YAML 'taps' entries"""
    result = True
    msgs = []
    logger = logging.getLogger("vppcfg.config")
    logger.addHandler(logging.NullHandler())

    if not "taps" in yaml:
        return result, msgs

    for ifname, iface in yaml["taps"].items():
        logger.debug(f"tap {iface}")
        instance = int(ifname[3:])

        ## NOTE(pim): 1024 is not off-by-one, tap1024 is precisely the highest permissible id
        if instance > 1024:
            msgs.append(f"tap {ifname} has instance {int(instance)} which is too large")
            result = False

        if not is_host_name_unique(yaml, iface["host"]["name"]):
            msgs.append(
                f"tap {ifname} does not have a unique host name {iface['host']['name']}"
            )
            result = False

        if "rx-ring-size" in iface:
            ncount = iface["rx-ring-size"]
            if ncount & (ncount - 1) != 0:
                msgs.append(f"tap {ifname} rx-ring-size must be a power of two")
                result = False

        if "tx-ring-size" in iface:
            ncount = iface["tx-ring-size"]
            if ncount & (ncount - 1) != 0:
                msgs.append(f"tap {ifname} tx-ring-size must be a power of two")
                result = False

        if (
            "namespace-create" in iface["host"]
            and iface["host"]["namespace-create"]
            and not "namespace" in iface["host"]
        ):
            msgs.append(
                f"tap {ifname} namespace-create can only be set if namespace is set"
            )
            result = False

        if (
            "bridge-create" in iface["host"]
            and iface["host"]["bridge-create"]
            and not "bridge" in iface["host"]
        ):
            msgs.append(f"tap {ifname} bridge-create can only be set if bridge is set")
            result = False

        if "mac" in iface["host"] and mac.is_multicast(iface["host"]["mac"]):
            msgs.append(
                f"tap {ifname} host MAC address {iface['host']['mac']} cannot be multicast"
            )
            result = False

    return result, msgs

#
# Copyright (c) 2022 <NAME>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#     http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
""" A vppcfg configuration module that validates taps """
import logging
from config import mac


def get_taps(yaml):
    """Return a list of all taps."""
    ret = []
    if "taps" in yaml:
        for ifname, _iface in yaml["taps"].items():
            ret.append(ifname)
    return ret


def get_by_name(yaml, ifname):
    """Return the tap by name, if it exists. Return None otherwise."""
    try:
        if ifname in yaml["taps"]:
            return ifname, yaml["taps"][ifname]
    except KeyError:
        pass
    return None, None


def is_tap(yaml, ifname):
    """Returns True if the interface name is an existing tap in the config.
    The TAP has to be explicitly named in the configuration, and notably
    a TAP belonging to a Linux Control Plane (LCP) will return False.
    """
    ifname, iface = get_by_name(yaml, ifname)
    return iface is not None


def is_host_name_unique(yaml, hostname):
    """Returns True if there is at most one occurence of the given ifname amonst all host-names of TAPs."""
    if not "taps" in yaml:
        return True
    host_names = []
    for _tap_ifname, tap_iface in yaml["taps"].items():
        host_names.append(tap_iface["host"]["name"])
    return host_names.count(hostname) < 2


def validate_taps(yaml):
    """Validate the semantics of all YAML 'taps' entries"""
    result = True
    msgs = []
    logger = logging.getLogger("vppcfg.config")
    logger.addHandler(logging.NullHandler())

    if not "taps" in yaml:
        return result, msgs

    for ifname, iface in yaml["taps"].items():
        logger.debug(f"tap {iface}")
        instance = int(ifname[3:])

        ## NOTE(pim): 1024 is not off-by-one, tap1024 is precisely the highest permissible id
        if instance > 1024:
            msgs.append(f"tap {ifname} has instance {int(instance)} which is too large")
            result = False

        if not is_host_name_unique(yaml, iface["host"]["name"]):
            msgs.append(
                f"tap {ifname} does not have a unique host name {iface['host']['name']}"
            )
            result = False

        if "rx-ring-size" in iface:
            ncount = iface["rx-ring-size"]
            if ncount & (ncount - 1) != 0:
                msgs.append(f"tap {ifname} rx-ring-size must be a power of two")
                result = False

        if "tx-ring-size" in iface:
            ncount = iface["tx-ring-size"]
            if ncount & (ncount - 1) != 0:
                msgs.append(f"tap {ifname} tx-ring-size must be a power of two")
                result = False

        if (
            "namespace-create" in iface["host"]
            and iface["host"]["namespace-create"]
            and not "namespace" in iface["host"]
        ):
            msgs.append(
                f"tap {ifname} namespace-create can only be set if namespace is set"
            )
            result = False

        if (
            "bridge-create" in iface["host"]
            and iface["host"]["bridge-create"]
            and not "bridge" in iface["host"]
        ):
            msgs.append(f"tap {ifname} bridge-create can only be set if bridge is set")
            result = False

        if "mac" in iface["host"] and mac.is_multicast(iface["host"]["mac"]):
            msgs.append(
                f"tap {ifname} host MAC address {iface['host']['mac']} cannot be multicast"
            )
            result = False

    return result, msgs
# # Copyright (c) 2022 <NAME> # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: #     http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # A vppcfg configuration module that validates taps Return a list of all taps. Return the tap by name, if it exists. Return None otherwise. Returns True if the interface name is an existing tap in the config.     The TAP has to be explicitly named in the configuration, and notably     a TAP belonging to a Linux Control Plane (LCP) will return False. Returns True if there is at most one occurence of the given ifname amonst all host-names of TAPs. Validate the semantics of all YAML 'taps' entries ## NOTE(pim): 1024 is not off-by-one, tap1024 is precisely the highest permissible id