# # Copyright (c) 2022 Pim van Pelt # # 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