diff --git a/README.md b/README.md
index e3024561eb5c0d84458ac301c8ddd912d5a5ca92..4620c9d1c6efc1f5ec8f583e3ff27eca33739e05 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[PACKAGE]: ../../raw/master/mkp/nvdct-0.9.8-20250205.mkp "nvdct-0.9.8-20250205.mkp"
+[PACKAGE]: ../../raw/master/mkp/nvdct-0.9.9-20250214.mkp "nvdct-0.9.9-20250214.mkp"
 # Network Visualization Data Creation Tool (NVDCT)
 
 This script creates the topology data file needed for the [Checkmk Exchange Network visualization](https://exchange.checkmk.com/p/network-visualization) plugin.\
diff --git a/mkp/nvdct-0.9.9-20250214.mkp b/mkp/nvdct-0.9.9-20250214.mkp
new file mode 100644
index 0000000000000000000000000000000000000000..979efdd9cd8f8c8682f257efb4380f9e70c2c7a9
Binary files /dev/null and b/mkp/nvdct-0.9.9-20250214.mkp differ
diff --git a/source/bin/nvdct/conf/nvdct.toml b/source/bin/nvdct/conf/nvdct.toml
index 524e395d38864e29bd7ef959f39cd45f7400feff..6698fe7b296944873216998e669fc9475615e8f8 100644
--- a/source/bin/nvdct/conf/nvdct.toml
+++ b/source/bin/nvdct/conf/nvdct.toml
@@ -168,6 +168,7 @@ FILTER_BY_SITE = [
 # l2_case = "OFF" | "LOWER" | "UPPER" | "IGNORE" | "AUTO"
 # l2_display_neighbours = false | true
 # l2_display_ports = false | true
+# l2_ignore_mismatch = ["DUPLEX", "SPEED", "VLAN"]
 # l2_prefix = ""
 # l2_remove_domain = "OFF" | "ON" | "AUTO"
 # l2_skip_external = false | true
diff --git a/source/bin/nvdct/lib/args.py b/source/bin/nvdct/lib/args.py
index 3f9a81f629ee74ce192dc5669815b72bf3d4efb6..8f2fa78ebbe3ea4b0527e40f9ca6339760d09c29 100755
--- a/source/bin/nvdct/lib/args.py
+++ b/source/bin/nvdct/lib/args.py
@@ -24,6 +24,7 @@
 # --l2-case
 # --l2-display-ports
 # --l2-display-neighbours
+# --l2-ignore-mismatch
 # --l2-prefix
 # --l2-remove-domain
 # --l2-skip-external
@@ -55,16 +56,17 @@ from pathlib import Path
 from lib.constants import (
     Backends,
     CONFIG_FILE,
-    Case,
     CliLong,
     CliShort,
     ExitCodes,
     IncludeExclude,
+    L2Case,
+    L2IgnoreMismatch,
+    L2RemoveDomain,
     Layers,
     LogLevels,
     MinVersions,
     NVDCT_VERSION,
-    RemoveDomain,
     SCRIPT,
     TIME_FORMAT_ARGPARSER,
     TomlSections,
@@ -95,11 +97,11 @@ def parse_arguments() -> arg_Namespace:
                f' {ExitCodes.AUTOMATION_SECRET_NOT_FOUND} - Automation secret not found\n'
                f' {ExitCodes.NO_LAYER_CONFIGURED} - No layer to work on\n'
                '\nUsage:\n'
-               f'{SCRIPT} -u ~/local/bin/nvdct/conf/my_{CONFIG_FILE} \n\n'
+               f'{SCRIPT} -c ~/local/bin/nvdct/conf/my_{CONFIG_FILE} \n\n'
     )
     parser.add_argument(
         CliShort.BACKEND, CliLong.BACKEND,
-        choices=[Backends.LIVESTATUS, Backends.MULTISITE, Backends.RESTAPI],
+        choices=Backends.list(),
         # default='MULTISITE',
         help='Backend used to retrieve the topology data\n'
              f' - {Backends.LIVESTATUS} : fetches data via local Livestatus (local site only)\n'
@@ -119,13 +121,7 @@ def parse_arguments() -> arg_Namespace:
     parser.add_argument(
         CliShort.LAYERS, CliLong.LAYERS,
         nargs='+',
-        choices=[
-            Layers.CDP,
-            Layers.LLDP,
-            Layers.L3V4,
-            Layers.STATIC,
-        ],
-        # default=['CDP'],
+        choices=Layers.list(),
         help=(
             f' - {Layers.CDP}    : needs inv_cdp_cache package at least in version {MinVersions.CDP}\n'
             f' - {Layers.LLDP}   : needs inv_lldp_cache package at least in version {MinVersions.LLDP}\n'
@@ -166,14 +162,14 @@ def parse_arguments() -> arg_Namespace:
     )
     parser.add_argument(
         CliLong.FILTER_CUSTOMERS,
-        choices=[IncludeExclude.INCLUDE, IncludeExclude.EXCLUDE],
+        choices=IncludeExclude.list(),
         # default='INCLUDE',
         help=f'{IncludeExclude.INCLUDE}/{IncludeExclude.EXCLUDE} customer list "[{TomlSections.FILTER_BY_CUSTOMER}]" from TOML file.'
              f'NOTE: {Backends.MULTISITE} backend only.',
     )
     parser.add_argument(
         CliLong.FILTER_SITES,
-        choices=[IncludeExclude.EXCLUDE, IncludeExclude.EXCLUDE],
+        choices=IncludeExclude.list(),
         # default='INCLUDE',
         help=f'{IncludeExclude.INCLUDE}/{IncludeExclude.EXCLUDE} site list "[{TomlSections.FILTER_BY_SITE}]" from TOML file.'
     )
@@ -185,13 +181,13 @@ def parse_arguments() -> arg_Namespace:
     )
     parser.add_argument(
         CliLong.L2_CASE,
-        choices=[Case.INSENSITIVE, Case.LOWER, Case.UPPER, Case.AUTO, Case.OFF],
-        help='Change L2 neighbour name case before matching to Checkmk host.\n'
-             f'- {Case.OFF}         : Do not change the case of the neighbour name.'
-             f'- {Case.INSENSITIVE} : search for a matching host by ignoring the case of neighbour name and host name\n'
-             f'- {Case.LOWER}       : change to all lower case\n'
-             f'- {Case.UPPER}       : change to all upper case, without the domain part of the neighbour name\n'
-             f'- {Case.AUTO}        : try all the above variants\n'
+        choices=L2Case.list(),
+        help='Change L2 neighbour name case before matching to Checkmk host\n'
+             f'- {L2Case.OFF}         : Do not change the case of the neighbour name\n'
+             f'- {L2Case.INSENSITIVE} : search for a matching host by ignoring the case of neighbour name and host name\n'
+             f'- {L2Case.LOWER}       : change to all lower case\n'
+             f'- {L2Case.UPPER}       : change to all upper case, without the domain part of the neighbour name\n'
+             f'- {L2Case.AUTO}        : try all the above variants\n'
              f'Default is let the case of the neighbour name untouched ("OFF").\n'
              f'Takes place after "{CliLong.L2_REMOVE_DOMAIN}" and before "{CliLong.L2_PREFIX}"',
     )
@@ -203,6 +199,16 @@ def parse_arguments() -> arg_Namespace:
         CliLong.L2_DISPLAY_NEIGHBOURS, action='store_const', const=True,  # default=False,
         help='Use L2 neighbour name as display name in L2 topologies',
     )
+    parser.add_argument(
+        CliLong.L2_IGNORE_MISMATCH,
+        nargs='+',
+        choices=L2IgnoreMismatch.list(),
+        help=(
+            f' - {L2IgnoreMismatch.DUPLEX} : Ignore duplex mismatch in layer 2 topologies\n'
+            f' - {L2IgnoreMismatch.SPEED}  : Ignore speed mismatch in layer 2 topologies\n'
+            f' - {L2IgnoreMismatch.VLAN}   : Ignore native vlan mismatch in layer 2 topologies\n'
+        )
+    )
     parser.add_argument(
         CliLong.L2_PREFIX, type=str,
         help=f'Prepends each L2 neighbour name with the prefix before matching to a Checkmk host name.\n'
@@ -210,12 +216,12 @@ def parse_arguments() -> arg_Namespace:
     )
     parser.add_argument(
         CliLong.L2_REMOVE_DOMAIN,
-        choices=[RemoveDomain.OFF, RemoveDomain.ON, RemoveDomain.AUTO],
+        choices=L2RemoveDomain.list(),
         help=f'Handle the the domain name part of a neighbour name before matching it to a Checkmk host.\n'
-             f'- {RemoveDomain.OFF} : dont touch the neighbour name, keep host name and domain part\n'
-             f'- {RemoveDomain.ON}  : will remove the domain part from the neighbour name, keep only the host name part\n'
-             f'- {RemoveDomain.AUTO} : try all of the above variants\n'
-             f'Default: "{RemoveDomain.OFF}". Takes place after "{CliLong.L2_REMOVE_DOMAIN}" and before "{CliLong.L2_PREFIX}"',
+             f'- {L2RemoveDomain.OFF} : dont touch the neighbour name, keep host name and domain part\n'
+             f'- {L2RemoveDomain.ON}  : will remove the domain part from the neighbour name, keep only the host name part\n'
+             f'- {L2RemoveDomain.AUTO} : try all of the above variants\n'
+             f'Default: "{L2RemoveDomain.OFF}". Takes place after "{CliLong.L2_REMOVE_DOMAIN}" and before "{CliLong.L2_PREFIX}"',
     )
     parser.add_argument(
         CliLong.L2_SKIP_EXTERNAL, action='store_const', const=True,  # default=False,
@@ -260,15 +266,7 @@ def parse_arguments() -> arg_Namespace:
     parser.add_argument(
         CliLong.LOG_LEVEL,
         # nargs='+',
-        choices=[
-            LogLevels.CRITICAL,
-            LogLevels.FATAL,
-            LogLevels.ERROR,
-            LogLevels.WARNING,
-            LogLevels.INFO,
-            LogLevels.DEBUG,
-            LogLevels.OFF
-        ],
+        choices=LogLevels.list(),
         # default='WARNING',
         help=f'Sets the log level. The default is "{LogLevels.WARNING}"'
     )
@@ -295,6 +293,6 @@ def parse_arguments() -> arg_Namespace:
     )
     parser.add_argument(
         CliLong.UPDATE_CONFIG, action='store_const', const=True,  # default=False,
-        help='Adjusts old options in TOML file.',
+        help='Adjusts options in config file.',
     )
     return parser.parse_args()
diff --git a/source/bin/nvdct/lib/backends.py b/source/bin/nvdct/lib/backends.py
index 8fce63c9be89a7c6c98fdcaf29c773ba3c6351ee..35860ed20cbdfa94d4ad27120782a61a9e6b747c 100755
--- a/source/bin/nvdct/lib/backends.py
+++ b/source/bin/nvdct/lib/backends.py
@@ -31,16 +31,16 @@ from lib.constants import (
     Backends,
     CACHE_INTERFACES_DATA,
     CacheItems,
-    Case,
     ExitCodes,
     HostFilter,
     IncludeExclude,
     InvPaths,
+    L2Case,
     L2InvColumns,
+    L2RemoveDomain,
     LiveStatusOperator,
     MIN_CMK_VERSION_POST,
     OMD_ROOT,
-    RemoveDomain,
 )
 from lib.utils import (
     LOGGER,
@@ -62,7 +62,7 @@ class HostCache:
         LOGGER.info(f'{backend} init HOST_CACHE')
 
         self.cache: Dict = {}
-        self.neighbour_to_host: MutableMapping[str, str] = {}
+        self.neighbour_to_host: MutableMapping[str, str | None] = {}
         self._inventory_pre_fetch_list: List[str] = [InvPaths.INTERFACES]
 
         self.backend: str = str(backend)
@@ -71,7 +71,7 @@ class HostCache:
         self.l2_neighbour_replace_regex: List[Tuple[str, str]] = []
         self.pre_fetch: bool = bool(pre_fetch)
         self.prefix: str = ''
-        self.remove_domain: str = RemoveDomain.OFF
+        self.remove_domain: str = L2RemoveDomain.OFF
         self.filter_include: MutableSequence[str] = []
         self.filter_exclude: MutableSequence[str] = []
         self.no_case_host_map: MutableMapping[str, str] = {}
@@ -94,9 +94,9 @@ class HostCache:
 
     def init_filter_lists(
             self,
-            filter_by_folder: Mapping[str, Sequence[str]],
-            filter_by_host_label: Mapping[str, Sequence[str]],
-            filter_by_host_tag: Mapping[str, Sequence[str]],
+            filter_by_folder: Mapping[str, set[str]],
+            filter_by_host_label: Mapping[str, set[str]],
+            filter_by_host_tag: Mapping[str, set[str]],
     ):
         for folder in filter_by_folder[IncludeExclude.INCLUDE]:
             self.filter_include += self.query_hosts_by_filter(HostFilter.FOLDER, folder, LiveStatusOperator.SUPERSET)
@@ -243,7 +243,7 @@ class HostCache:
         Returns: None, the data is directly writen to self.cache
         """
 
-        inventory_of_hosts: Mapping[str, Mapping | None] = self.get_inventory_data(hosts=hosts)
+        inventory_of_hosts: Mapping[str, Dict | None] = self.get_inventory_data(hosts=hosts)
         if inventory_of_hosts:
             for host, inventory in inventory_of_hosts.items():
                 if host not in self.cache:
@@ -343,30 +343,30 @@ class HostCache:
 
         host_no_domain: str = host.split('.')[0]
         match self.remove_domain:
-            case RemoveDomain.ON:
+            case L2RemoveDomain.ON:
                 LOGGER.debug(f'{self.backend} Remove domain: {host} -> {host_no_domain}')
                 host = host_no_domain
                 possible_hosts.add(host_no_domain)
-            case RemoveDomain.AUTO:
+            case L2RemoveDomain.AUTO:
                 possible_hosts.add(host)
                 possible_hosts.add(host_no_domain)
-            case RemoveDomain.OFF | _:
+            case L2RemoveDomain.OFF | _:
                 possible_hosts.add(host)
 
         match self.case:
-            case Case.UPPER:
+            case L2Case.UPPER:
                 if not '.' in host:  # use UPPER only for names without domain
                     LOGGER.debug(f'{self.backend} Change neighbour to upper case: {host} -> {host.upper()}')
                     possible_hosts.add(host.upper)
-            case Case.LOWER:
+            case L2Case.LOWER:
                 LOGGER.debug(f'{self.backend} Change neighbour to lower case: {host} -> {host.lower()}')
                 possible_hosts.add(host.lower())
-            case Case.AUTO:
+            case L2Case.AUTO:
                 possible_hosts.add(host)
                 possible_hosts.add(host.lower())
                 possible_hosts.add(host_no_domain.lower())
                 possible_hosts.add(host_no_domain.upper())
-            case Case.OFF | _:
+            case L2Case.OFF | _:
                 possible_hosts.add(host)
 
         possible_hosts = [f'{self.prefix}{host}' for host in possible_hosts]
@@ -381,7 +381,7 @@ class HostCache:
                 LOGGER.debug(f'{self.backend} Matched neighbour to host: |{neighbour}| -> |{entry}|')
                 return entry
 
-        if self.case in [Case.AUTO, Case.INSENSITIVE]:
+        if self.case in [L2Case.AUTO, L2Case.INSENSITIVE]:
             if not self.no_case_host_map:
                 self.no_case_host_map = {host.lower():host for host in self.cache}
             for entry in possible_hosts:
diff --git a/source/bin/nvdct/lib/constants.py b/source/bin/nvdct/lib/constants.py
index 579eb0d75050c4dd8b280c62138deecfb5e4e401..2cac55c304fbf5a352847dfe99a59345b3781002 100755
--- a/source/bin/nvdct/lib/constants.py
+++ b/source/bin/nvdct/lib/constants.py
@@ -13,7 +13,7 @@ from os import environ
 from typing import Final
 
 #
-NVDCT_VERSION: Final[str] = '0.9.8-20250107'
+NVDCT_VERSION: Final[str] = '0.9.9-20250207'
 #
 OMD_ROOT: Final[str] = environ["OMD_ROOT"]
 #
@@ -21,19 +21,25 @@ API_PORT_DEFAULT: Final[int] = 5001
 CACHE_INTERFACES_DATA: Final[str] = 'interface_data'
 CMK_SITE_CONF: Final[str] = f'{OMD_ROOT}/etc/omd/site.conf'
 CONFIG_FILE: Final[str] = 'nvdct.toml'
+CONFIG_PATH_DEFAULT: Final[str] = f'{OMD_ROOT}/local/bin/nvdct/conf'
 DATAPATH: Final[str] = f'{OMD_ROOT}/var/check_mk/topology/data'
 LOGGER: Logger = getLogger('root)')
-LOG_FILE_DEFAULT: Final[str] = f'{OMD_ROOT}/var/log/nvdct.log'
+LOG_PATH_DEFAULT: Final[str] =  f'{OMD_ROOT}/var/log'
+LOG_FILE_DEFAULT: Final[str] = f'{LOG_PATH_DEFAULT}/nvdct.log'
 MIN_CMK_VERSION_POST: Final[str] = '2.3.0p23'
 SCRIPT: Final[str] = '~/local/bin/nvdct/nvdct.py'
-TIME_FORMAT_ARGPARSER: Final[str] = '%%Y-%%m-%%dT%%H:%%M:%%S.%%m'
 TIME_FORMAT_DEFAULT: Final[str] = '%Y-%m-%dT%H:%M:%S.%m'
 
+TIME_FORMAT_ARGPARSER: Final[str] = TIME_FORMAT_DEFAULT.replace('%', '%%')
+
 
 class EnumValue(Enum):
     def __get__(self, instance, owner):
         return self.value
 
+    @classmethod
+    def list(cls):
+        return list(map(lambda c: c.value, cls))
 
 @unique
 class ExitCodes(EnumValue):
@@ -72,7 +78,7 @@ class Backends(EnumValue):
 
 
 @unique
-class Case(EnumValue):
+class L2Case(EnumValue):
     AUTO: Final[str] = 'AUTO'
     INSENSITIVE: Final[str] = 'INSENSITIVE'
     LOWER: Final[str] = 'LOWER'
@@ -81,11 +87,16 @@ class Case(EnumValue):
 
 
 @unique
-class RemoveDomain(EnumValue):
+class L2RemoveDomain(EnumValue):
     ON: Final[str] = 'ON'
     OFF: Final[str] = 'OFF'
     AUTO: Final[str] = 'AUTO'
 
+@unique
+class L2IgnoreMismatch(EnumValue):
+    DUPLEX: Final[str] = 'DUPLEX'
+    SPEED: Final[str] = 'SPEED'
+    VLAN: Final[str] = 'VLAN'
 
 @unique
 class CacheItems(EnumValue):
@@ -107,6 +118,7 @@ class CliLong(EnumValue):
     L2_CASE: Final[str] = '--l2-case'
     L2_DISPLAY_NEIGHBOURS: Final[str] = '--l2-display-neighbours'
     L2_DISPLAY_PORTS: Final[str] = '--l2-display-ports'
+    L2_IGNORE_MISMATCH: Final[str] = '--l2-ignore-mismatch'
     L2_PREFIX: Final[str] = '--l2-prefix'
     L2_REMOVE_DOMAIN: Final[str] = '--l2-remove-domain'
     L2_SKIP_EXTERNAL: Final[str] = '--l2-skip-external'
@@ -285,6 +297,7 @@ class TomlSettings(EnumValue):
     L2_CASE: Final[str] = cli_long_to_toml(CliLong.L2_CASE)
     L2_DISPLAY_NEIGHBOURS: Final[str] = cli_long_to_toml(CliLong.L2_DISPLAY_NEIGHBOURS)
     L2_DISPLAY_PORTS: Final[str] = cli_long_to_toml(CliLong.L2_DISPLAY_PORTS)
+    L2_IGNORE_MISMATCH: Final[str] = cli_long_to_toml(CliLong.L2_IGNORE_MISMATCH)
     L2_PREFIX: Final[str] = cli_long_to_toml(CliLong.L2_PREFIX)
     L2_REMOVE_DOMAIN: Final[str] = cli_long_to_toml(CliLong.L2_REMOVE_DOMAIN)
     L2_SKIP_EXTERNAL: Final[str] = cli_long_to_toml(CliLong.L2_SKIP_EXTERNAL)
diff --git a/source/bin/nvdct/lib/settings.py b/source/bin/nvdct/lib/settings.py
index e069f0ccddcaefac6174632fffcf6c9abef030fe..79576921727996d781dd1bc91fc16d801784ec4d 100755
--- a/source/bin/nvdct/lib/settings.py
+++ b/source/bin/nvdct/lib/settings.py
@@ -15,7 +15,6 @@ from collections.abc import Mapping
 from ipaddress import AddressValueError, NetmaskValueError, ip_address, ip_network
 from logging import CRITICAL, DEBUG, ERROR, FATAL, INFO, WARNING
 from pathlib import Path
-from sys import exit as sys_exit
 from time import strftime
 from typing import Dict, List, NamedTuple, Set, Tuple
 
@@ -23,16 +22,18 @@ from lib.constants import (
     API_PORT_DEFAULT,
     Backends,
     CONFIG_FILE,
-    Case,
+    CONFIG_PATH_DEFAULT,
     EmblemNames,
     EmblemValues,
-    ExitCodes,
     IncludeExclude,
+    L2Case,
+    L2IgnoreMismatch,
+    L2RemoveDomain,
+    Layers,
     LOGGER,
     LOG_FILE_DEFAULT,
+    LOG_PATH_DEFAULT,
     LogLevels,
-    OMD_ROOT,
-    RemoveDomain,
     TIME_FORMAT_DEFAULT,
     TomlSections,
     TomlSettings,
@@ -86,7 +87,7 @@ class Settings:
             TomlSettings.API_PORT: None,
             TomlSettings.BACKEND: Backends.MULTISITE,
             TomlSettings.CHECK_CONFIG: False,
-            TomlSettings.CONFIG: f'{OMD_ROOT}/local/bin/nvdct/conf/{CONFIG_FILE}',
+            TomlSettings.CONFIG: f'{CONFIG_PATH_DEFAULT}/{CONFIG_FILE}',
             TomlSettings.DEFAULT: False,
             TomlSettings.DONT_COMPARE: False,
             TomlSettings.FILTER_CUSTOMERS: None,
@@ -94,6 +95,7 @@ class Settings:
             TomlSettings.KEEP_MAX_TOPOLOGIES: False,
             TomlSettings.L2_CASE: None,
             TomlSettings.L2_DISPLAY_PORTS: False,
+            TomlSettings.L2_IGNORE_MISMATCH: [],
             TomlSettings.L2_DISPLAY_NEIGHBOURS: False,
             TomlSettings.L2_PREFIX: None,
             TomlSettings.L2_REMOVE_DOMAIN: None,
@@ -132,14 +134,6 @@ class Settings:
         self.__settings.update(self.__user_data.get(TomlSections.SETTINGS, {}))
         self.__settings.update(self.__args)
 
-        if self.layers:
-            layers = list(set(self.layers))
-            if len(layers) != len(self.layers):
-                # logger not initialized here
-                # LOGGER.fatal('-l/--layers options must be unique. Don\'t use any layer more than once.')
-                print('-l/--layers options must be unique. Don\'t use any layer more than once.')
-                sys_exit(ExitCodes.BAD_OPTION_LIST)
-
         self.__api_port: int | None = None
 
         # init user data with defaults
@@ -150,6 +144,7 @@ class Settings:
         self.__filter_by_host_tag: Dict[str, Set[str]] | None = None
         self.__filter_by_site: List[str] | None = None
         self.__l2_drop_neighbours: List[str] | None = None
+        self.__l2_ignore_mismatch: List[str] | None = None
         self.__l2_neighbour_replace_regex: List[Tuple[str, str]] | None = None
         self.__l2_neighbour_to_host_map: Dict[str, str] | None = None
         self.__l2_seed_devices: List[str] | None = None
@@ -158,6 +153,7 @@ class Settings:
         self.__l3_replace: Dict[str, str] | None = None
         self.__l3_summarize: List[ip_network] | None = None
         self.__l3v4_ignore_wildcard: List[Wildcard] | None = None
+        self.__layers: List[str] | None = None
         self.__map_speed_to_thickness: List[Thickness] | None = None
         self.__protected_topologies: List[str] | None = None
         self.__static_connections: List[StaticConnection] | None = None
@@ -179,11 +175,7 @@ class Settings:
 
     @property  # -b --backend
     def backend(self) -> str:
-        if str(self.__settings[TomlSettings.BACKEND]) in [
-            Backends.LIVESTATUS,
-            Backends.MULTISITE,
-            Backends.RESTAPI
-        ]:
+        if str(self.__settings[TomlSettings.BACKEND]) in Backends.list():
             return str(self.__settings[TomlSettings.BACKEND])
         else:  # fallback to defaukt -> exit ??
             LOGGER.error(
@@ -210,7 +202,7 @@ class Settings:
 
     @property  # --filter-customers
     def filter_customers(self) -> str | None:
-        if self.__settings[TomlSettings.FILTER_CUSTOMERS] in [IncludeExclude.INCLUDE, IncludeExclude.EXCLUDE]:
+        if self.__settings[TomlSettings.FILTER_CUSTOMERS] in IncludeExclude.list():
             return self.__settings[TomlSettings.FILTER_CUSTOMERS]
         elif self.__settings[TomlSettings.FILTER_CUSTOMERS] is not None:
             LOGGER.error(
@@ -221,7 +213,7 @@ class Settings:
 
     @property  # --filter-sites
     def filter_sites(self) -> str | None:
-        if self.__settings[TomlSettings.FILTER_SITES] in [IncludeExclude.INCLUDE, IncludeExclude.EXCLUDE]:
+        if self.__settings[TomlSettings.FILTER_SITES] in IncludeExclude.list():
             return self.__settings[TomlSettings.FILTER_SITES]
         elif self.__settings[TomlSettings.FILTER_SITES] is not None:
             LOGGER.error(
@@ -232,12 +224,12 @@ class Settings:
 
     @property  # --l2-case
     def l2_case(self) -> str | None:
-        if self.__settings[TomlSettings.L2_CASE] in [Case.LOWER, Case.UPPER, Case.INSENSITIVE, Case.AUTO, Case.OFF]:
+        if self.__settings[TomlSettings.L2_CASE] in L2Case.list():
             return self.__settings[TomlSettings.L2_CASE]
         elif self.__settings[TomlSettings.L2_CASE] is not None:
             LOGGER.error(
                     f'Unknown case setting {self.__settings[TomlSettings.L2_CASE]}. '
-                    f'Accepted are {Case.LOWER}|{Case.UPPER}|{Case.INSENSITIVE}|{Case.AUTO}|{Case.OFF}. Falling back to "OFF" (no change).'
+                    f'Accepted are {L2Case.LOWER}|{L2Case.UPPER}|{L2Case.INSENSITIVE}|{L2Case.AUTO}|{L2Case.OFF}. Falling back to "OFF" (no change).'
                 )
         return None
 
@@ -249,6 +241,15 @@ class Settings:
     def l2_display_neighbours(self) -> bool:
         return bool(self.__settings[TomlSettings.L2_DISPLAY_NEIGHBOURS])
 
+    @property  # --l2-ignore-mismatch
+    def l2_ignore_mismatch(self) -> List[str]:
+        if self.__l2_ignore_mismatch is None:
+            self.__l2_ignore_mismatch = list(set(self.__settings[TomlSettings.L2_IGNORE_MISMATCH]))
+            self.__l2_ignore_mismatch = [entry for entry in self.__l2_ignore_mismatch if entry in L2IgnoreMismatch.list()]
+            if len(self.__layers) != len(self.__settings[TomlSettings.LAYERS]):
+                LOGGER.error(f'Wrong/duplicate l2_mismatch_ignore options ignored, {self.__settings[TomlSettings.L2_IGNORE_MISMATCH]}')
+        return self.__l2_ignore_mismatch
+
     @property  # --l2-prefix
     def l2_prefix(self) -> str:
         if self.__settings[TomlSettings.L2_PREFIX] is not None:
@@ -257,11 +258,11 @@ class Settings:
 
     @property  # --l2-remove-domain
     def l2_remove_domain(self) -> str:
-        if self.__settings[TomlSettings.L2_REMOVE_DOMAIN] in [RemoveDomain.ON, RemoveDomain.OFF, RemoveDomain.AUTO]:
+        if self.__settings[TomlSettings.L2_REMOVE_DOMAIN] in L2RemoveDomain.list():
             return self.__settings[TomlSettings.L2_REMOVE_DOMAIN]
         else:
-            self.__settings[TomlSettings.L2_REMOVE_DOMAIN] = RemoveDomain.OFF
-            return RemoveDomain.OFF
+            self.__settings[TomlSettings.L2_REMOVE_DOMAIN] = L2RemoveDomain.OFF
+            return L2RemoveDomain.OFF
 
     @property  # --l2-skip-external
     def l2_skip_external(self) -> bool:
@@ -301,12 +302,17 @@ class Settings:
 
     @property  # --layers
     def layers(self) -> List[str]:
-        return self.__settings[TomlSettings.LAYERS]
+        if self.__layers is None:
+            self.__layers = list(set(self.__settings[TomlSettings.LAYERS]))
+            self.__layers = [entry for entry in self.__layers if entry in Layers.list()]
+            if len(self.__layers) != len(self.__settings[TomlSettings.LAYERS]):
+                LOGGER.error(f'Wrong/duplicate layer(s) ignored, {self.__settings[TomlSettings.LAYERS]}.')
+        return self.__layers
 
     @property  # --log-file
     def log_file(self) -> str:
         raw_log_file = str(Path(str(self.__settings[TomlSettings.LOG_FILE])).expanduser())
-        if not raw_log_file.startswith(f'{OMD_ROOT}/var/log/'):
+        if not raw_log_file.startswith(f'{LOG_PATH_DEFAULT}/'):
             # logger not ready yet
             print(f'\nInvalid log file {raw_log_file}. Falling back to {LOG_FILE_DEFAULT}')
             return LOG_FILE_DEFAULT
@@ -405,13 +411,13 @@ class Settings:
         return self.__filter_by_site
 
     @staticmethod
-    def parse_key_value_section(section: str, data: Mapping[str, str]) -> Dict[str, set[str]]:
+    def parse_key_value_section(section: str, data: Mapping[str, str]) -> Mapping[str, set[str]]:
         parsed = {
             IncludeExclude.INCLUDE: set(),
             IncludeExclude.EXCLUDE: set()
         }
         for key_value, mode in data.items():
-            if mode not in [IncludeExclude.INCLUDE, IncludeExclude.EXCLUDE]:
+            if mode not in IncludeExclude.list():
                 LOGGER.error(
                     f'Invalid mode in {section} found: {key_value}={mode} -> line ignored'
                 )
@@ -438,7 +444,7 @@ class Settings:
         return parsed
 
     @property
-    def filter_by_folder(self) -> Dict[str, set[str]]:
+    def filter_by_folder(self) -> Mapping[str, set[str]]:
         if self.__filter_by_folder is None:
             self.__filter_by_folder = self.parse_key_value_section(
                 section=TomlSections.FILTER_BY_FOLDER,
diff --git a/source/bin/nvdct/lib/topologies.py b/source/bin/nvdct/lib/topologies.py
index 7a4634f156a401fd898ecec2cb210fb06a0a5529..69a4b4d5a077acbe59900fdbeebf54fbfba96be5 100755
--- a/source/bin/nvdct/lib/topologies.py
+++ b/source/bin/nvdct/lib/topologies.py
@@ -29,6 +29,7 @@ from lib.constants import (
     HostLabels,
     IPVersion,
     InvPaths,
+    L2IgnoreMismatch,
     L2InvColumns,
     L3InvColumns,
     LOGGER,
@@ -382,6 +383,7 @@ class NvConnections:
             self,
             nv_objects: NvObjects,
             speed_map: Sequence[Thickness],
+            ignore_mismatch: Sequence[str],
     ):
         for connection in self.nv_connections:
             warning = False
@@ -411,7 +413,7 @@ class NvConnections:
                 # left_thickness = map_speed_to_thickness(left_speed, speed_map)
                 metadata['line_config']['thickness'] = right_thickness
 
-                if right_speed != left_speed:
+                if right_speed != left_speed and not L2IgnoreMismatch.SPEED in ignore_mismatch:
                     warning = True
                     metadata = add_tooltip_html(
                         metadata, 'Speed', left, left_speed_str, right, right_speed_str
@@ -428,7 +430,7 @@ class NvConnections:
             # for duplex/native vlan it might be a good idea to change left/right
             # value as they are reported by CDP as neighbour states
             if left_duplex and right_duplex:
-                if left_duplex != right_duplex:
+                if left_duplex != right_duplex and not L2IgnoreMismatch.DUPLEX in ignore_mismatch:
                     warning = True
 
                     metadata = add_tooltip_html(
@@ -439,7 +441,7 @@ class NvConnections:
                         f'Connection duplex mismatch: {left} ({left_duplex})'
                         f'<->{right} ({right_duplex})'
                     )
-            if left_native_vlan and right_native_vlan:
+            if left_native_vlan and right_native_vlan and not L2IgnoreMismatch.VLAN in ignore_mismatch:
                 if left_native_vlan != '0' and right_native_vlan != '0':  # ignore VLAN 0 (Native VLAN on routed ports)
                     if left_native_vlan != right_native_vlan:
                         warning = True
@@ -710,7 +712,7 @@ class TopologyL2(Topology):
         self.hosts_to_go: MutableSet[str] = set(seed_devices)
         self.label: str = label
         self.neighbour_replace_regex: List[Tuple[str, str]] = neighbour_replace_regex
-        self.neighbour_to_host: MutableMapping[str, str] = {}
+        self.neighbour_to_host: MutableMapping[str, str | None] = {}
         self.path_in_inventory: str = path_in_inventory
         self.raw_neighbour_to_neighbour: Dict[str, str] = {}
         self.skip_external: bool = skip_external
@@ -910,7 +912,7 @@ class TopologyL3(Topology):
         super().__init__(
             emblems=emblems,
             host_cache=host_cache,
-            topology=f'[L3 IPv{version}]'
+            topology=f'[L3 IPv{version}]',
         )
         self.diplay_devices = display_devices
         self.ignore_hosts: Sequence[str] = ignore_hosts
diff --git a/source/bin/nvdct/lib/update_config.py b/source/bin/nvdct/lib/update_config.py
new file mode 100755
index 0000000000000000000000000000000000000000..b1f52fb4f33924560d091043526d5a514470f5d3
--- /dev/null
+++ b/source/bin/nvdct/lib/update_config.py
@@ -0,0 +1,402 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# License: GNU General Public License v2
+# Author: thl-cmk[at]outlook[dot]com
+# URL   : https://thl-cmk.hopto.org
+# Date  : 202-01-03
+# File  : nvdct/lib/adjust_toml.py
+
+
+from pathlib import Path
+from re import findall as re_findall, sub as re_sub
+from sys import exit as sys_exit
+
+from lib.constants import (
+    API_PORT_DEFAULT,
+    Backends,
+    EmblemNames,
+    EmblemValues,
+    ExitCodes,
+    IncludeExclude,
+    L2Case,
+    L2IgnoreMismatch,
+    L2RemoveDomain,
+    Layers,
+    LogLevels,
+    TomlSections,
+    TomlSettings,
+)
+
+
+def update_config(toml_file: str):
+    fix_options = {
+        'DROP_HOSTS': TomlSections.L2_DROP_NEIGHBOURS,
+        'HOST_MAP': TomlSections.L2_NEIGHBOUR_TO_HOST_MAP,
+        'L2_DROP_HOSTS': TomlSections.L2_DROP_NEIGHBOURS,
+        'L2_HOST_MAP': TomlSections.L2_NEIGHBOUR_TO_HOST_MAP,
+        'L3V4_IGNORE_HOSTS': TomlSections.L3_IGNORE_HOSTS,
+        'L3V4_IGNORE_IP': TomlSections.L3_IGNORE_IP,
+        'L3V4_IRNORE_WILDCARD': TomlSections.L3V4_IGNORE_WILDCARD,
+        'L3V4_REPLACE': TomlSections.L3_REPLACE_NETWORKS,
+        'L3V3_REPLACE': TomlSections.L3_REPLACE_NETWORKS,
+        'L3_REPLACE': TomlSections.L3_REPLACE_NETWORKS,
+        'L3V4_SUMMARIZE': TomlSections.L3_SUMMARIZE,
+        'SEED_DEVICES': TomlSections.L2_SEED_DEVICES,
+        'SITES': TomlSections.FILTER_BY_SITE,
+        'CUSTOMERS': TomlSections.FILTER_BY_CUSTOMER,
+        'icon_missinc': EmblemValues.ICON_ALERT_UNREACHABLE,
+        'icon_missing': EmblemValues.ICON_ALERT_UNREACHABLE,
+        'l3v4_replace': EmblemNames.L3_REPLACE,
+        'l3v4_summarize': EmblemNames.L3_SUMMARIZE,
+        'keep_domain = true': f'{TomlSettings.L2_REMOVE_DOMAIN} = "{L2RemoveDomain.OFF}"',
+        'keep_domain = false': f'{TomlSettings.L2_REMOVE_DOMAIN} = "{L2RemoveDomain.ON}"',
+        'case': TomlSettings.L2_CASE,
+        'prefix': TomlSettings.L2_PREFIX,
+        'remove_domain': TomlSettings.L2_REMOVE_DOMAIN,
+        'keep': TomlSettings.KEEP_MAX_TOPOLOGIES,
+        'min_age': TomlSettings.MIN_TOPOLOGY_AGE,
+        'display_l2_neighbours': TomlSettings.L2_DISPLAY_NEIGHBOURS,
+        'include_l3_hosts': TomlSettings.L3_INCLUDE_HOSTS,
+        'include_l3_loopback': TomlSettings.L3_INCLUDE_LOOPBACK,
+        'skip_l3_cidr_0': TomlSettings.L3_SKIP_CIDR_0,
+        'skip_l3_cidr_32_128': TomlSettings.L3_SKIP_CIDR_32_128,
+        'skip_l3_if': TomlSettings.L3_SKIP_IF,
+        'skip_l3_ip': TomlSettings.L3_SKIP_IP,
+        'skip_l3_public': TomlSettings.L3_SKIP_PUBLIC,
+    }
+
+    fix_params = {
+        f'# {TomlSettings.L2_CASE} = "{L2Case.LOWER}" | "{L2Case.UPPER}"\n': f'# {TomlSettings.L2_CASE} = "{L2Case.LOWER}" | "{L2Case.UPPER}" | {L2Case.INSENSITIVE} | "{L2Case.AUTO}" | "{L2Case.OFF}"\n',
+        f'# {TomlSettings.L2_CASE} = "{L2Case.LOWER}\n"' : f'# {TomlSettings.L2_CASE} = "{L2Case.LOWER}" | "{L2Case.UPPER}" | {L2Case.INSENSITIVE} | "{L2Case.AUTO}" | "{L2Case.OFF}"\n',
+        f'# {TomlSettings.L2_CASE} = "{L2Case.UPPER}\n"': f'# {TomlSettings.L2_CASE} = "{L2Case.LOWER}" | "{L2Case.UPPER}" | {L2Case.INSENSITIVE} | "{L2Case.AUTO}" | "{L2Case.OFF}\n',
+        f'# {TomlSettings.L2_REMOVE_DOMAIN} = false | true\n': f'# {TomlSettings.L2_REMOVE_DOMAIN} = "{L2RemoveDomain.OFF}" | "{L2RemoveDomain.ON}" | "{L2RemoveDomain.AUTO}"\n',
+        f'# {TomlSettings.L2_REMOVE_DOMAIN} = false\n': f'# {TomlSettings.L2_REMOVE_DOMAIN} = "{L2RemoveDomain.OFF}" | "{L2RemoveDomain.ON}" | "{L2RemoveDomain.AUTO}"\n',
+        f'# {TomlSettings.L2_REMOVE_DOMAIN} = true\n': f'# {TomlSettings.L2_REMOVE_DOMAIN} = "{L2RemoveDomain.OFF}" | "{L2RemoveDomain.ON}" | "{L2RemoveDomain.AUTO}"\n',
+        f'{TomlSettings.L2_REMOVE_DOMAIN} = false\n': f'{TomlSettings.L2_REMOVE_DOMAIN} = "{L2RemoveDomain.OFF}"\n',
+        f'{TomlSettings.L2_REMOVE_DOMAIN} = true\n': f'{TomlSettings.L2_REMOVE_DOMAIN} = "{L2RemoveDomain.ON}"\n',
+    }
+
+    old_options = {
+        'CUSTOM_LAYERS': f'Can be removed (no longer supported).',
+        'FILESYSTEM': f'Use {Backends.MULTISITE} instead.',
+        'debug': f'Use "{TomlSettings.LOG_LEVEL} = {LogLevels.DEBUG}" instead.',
+        'lowercase': f'Use "{TomlSettings.L2_CASE} = {L2Case.LOWER}" instead.',
+        'uppercase': f'Use "{TomlSettings.L2_CASE} = {L2Case.UPPER}" instead.',
+        'CUSTOM': f'Is no loger supported. remove it from layers please.'
+    }
+    # sorted in reverse
+    missing_settings = {
+        TomlSettings.TIME_FORMAT: '"%Y-%m-%dT%H:%M:%S.%m"',
+        TomlSettings.QUIET: 'false | true',
+        TomlSettings.PRE_FETCH: 'false | true',
+        TomlSettings.OUTPUT_DIRECTORY: '"nvdct"  # remove to get date formated directory',
+        TomlSettings.MIN_TOPOLOGY_AGE: '1',
+        TomlSettings.LOG_TO_STDOUT: 'false | true',
+        TomlSettings.LOG_LEVEL: f'"{LogLevels.WARNING}" | "{LogLevels.DEBUG}" | "{LogLevels.INFO}" | "{LogLevels.ERROR}" | "{LogLevels.FATAL}" | {LogLevels.CRITICAL}" | "{LogLevels.OFF}"',
+        TomlSettings.LOG_FILE: '"~/var/log/nvdct.log"',
+        TomlSettings.LAYERS: f'["{Layers.CDP}", "{Layers.LLDP}", "{Layers.L3V4}", "{Layers.STATIC}"]',
+        TomlSettings.L3_SKIP_PUBLIC: 'false | true',
+        TomlSettings.L3_SKIP_IP: 'false | true',
+        TomlSettings.L3_SKIP_IF: 'false | true',
+        TomlSettings.L3_SKIP_CIDR_32_128: 'false | true',
+        TomlSettings.L3_SKIP_CIDR_0: 'false | true',
+        TomlSettings.L3_INCLUDE_LOOPBACK: 'false | true  # most likely dropped from inventory (SNMP) before',
+        TomlSettings.L3_INCLUDE_HOSTS: 'false | true',
+        TomlSettings.L3_DISPLAY_DEVICES: 'false | true',
+        TomlSettings.L2_SKIP_EXTERNAL: 'false | true',
+        TomlSettings.L2_REMOVE_DOMAIN: f'"{L2RemoveDomain.OFF}" | "{L2RemoveDomain.ON}" | "{L2RemoveDomain.AUTO}"',
+        TomlSettings.L2_PREFIX: '""',
+        TomlSettings.L2_IGNORE_MISMATCH: f'["{L2IgnoreMismatch.DUPLEX}", "{L2IgnoreMismatch.SPEED}", "{L2IgnoreMismatch.VLAN}"]',
+        TomlSettings.L2_DISPLAY_NEIGHBOURS: 'false | true',
+        TomlSettings.L2_DISPLAY_PORTS: 'false | true',
+        TomlSettings.L2_CASE: f'"{L2Case.OFF}" | "{L2Case.LOWER}" | "{L2Case.UPPER}" | {L2Case.INSENSITIVE} | "{L2Case.AUTO}"',
+        TomlSettings.KEEP_MAX_TOPOLOGIES: '10',
+        TomlSettings.FILTER_SITES: f'"{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}"',
+        TomlSettings.FILTER_CUSTOMERS: f'"{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}"',
+        TomlSettings.DONT_COMPARE: 'false | true',
+        TomlSettings.DEFAULT: 'false | true',
+        TomlSettings.BACKEND: f'"{Backends.MULTISITE}" | "{Backends.RESTAPI}" | "{Backends.LIVESTATUS}"',
+        TomlSettings.API_PORT: API_PORT_DEFAULT,
+    }
+
+    l2_neighbour_to_host_map = r'''
+# map inventory CDP/LLDP neighbour name to Checkmk host name
+# [0-9-a-zA-Z\.\_\-]{1,253} -> host
+    
+# "inventory_neighbour1" = "cmk_host1"
+# "inventory_neighbour2" = "cmk_host2"
+# "inventory_neighbour3" = "cmk_host3"
+    '''
+
+    l2_neighbour_replace_regex = r'''
+# modify CDP/LLDP neighbour name with regex before mapping to CMK host names
+
+# "regex string to replace" = "string to replace with"
+# "^(([0-9a-fA-F]){2}[:.-]?){5}([0-9a-fA-F]){2}$" = ""
+# "\\([0-9a-zA-Z]+\\)$" = ""
+# "^Meraki.*\\s-\\s" = ""    
+    '''
+
+    l3_replace_networks = r'''
+# replace _network objects_ in L§ topologies (takes place after summarize)
+# [0-9-a-zA-Z\.\_\-]{1,253} -> host
+
+# "10.193.172.0/24" = "MPLS"
+# "10.194.8.0/23" = "MPLS"
+# "10.194.12.0/24" = "MPLS"
+# "10.194.115.0/24" = "MPLS"
+# "fc00::/7" = "Unique-local"    
+    '''
+
+    emblems = f'''
+# can use misc icons from CMK or upload your own in the misc category
+# for built-in icons use "icon_" as l2_prefix to the name from CMK
+# max size 80x80px
+# emblems will only be used for non CMK objects
+
+# "{EmblemNames.HOST_NODE}" = "{EmblemValues.ICON_ALERT_UNREACHABLE}"
+# "{EmblemNames.IP_ADDRESS}" = "{EmblemValues.IP_ADDRESS_80}"
+# "{EmblemNames.IP_NETWORK}" = "{EmblemValues.IP_NETWORK_80}"
+# "{EmblemNames.L3_REPLACE}" = "{EmblemValues.ICON_PLUGINS_CLOUD}"
+# "{EmblemNames.L3_SUMMARIZE}" = "{EmblemValues.ICON_AGGREGATION}"
+# "{EmblemNames.SERVICE_NODE}" = "{EmblemValues.ICON_ALERT_UNREACHABLE}"    
+    '''
+
+    map_speed_to_thickness = r'''
+# must be sorted from slower to faster speed
+# use only one/no entry to have all connections with the same thickness
+# "bits per second" = thickness
+
+# "2000000" = 1  # 2 mbit
+# "5000000" = 2  # 5 mbit
+# "1e7" = 3      # 10 mbit
+# "51e7" = 4     # 51 mbit
+"1e8" = 1  # 100 mbit
+"1e9" = 3  # 1 gbit
+"1e10" = 5 # 10 gbit    
+    '''
+
+    filter_by_folder = f'''
+# "/folder1/subfolder1" = "{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}"
+# "/folder2/subfolder2" = "{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}"    
+'''
+
+    filter_host_by_label = f'''
+# "hostlabel1:value" = "{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}"
+# "hostlabel2:value" = "{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}"
+'''
+
+    filter_host_by_tag = f'''
+# "host_tag1:value" = "{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}"
+# "host_tag2:value" = "{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}"
+    '''
+
+    missing_tables = {
+        TomlSections.L2_NEIGHBOUR_TO_HOST_MAP: l2_neighbour_to_host_map,
+        TomlSections.L2_NEIGHBOUR_REPLACE_REGEX: l2_neighbour_replace_regex,
+        TomlSections.L3_REPLACE_NETWORKS: l3_replace_networks,
+        TomlSections.EMBLEMS: emblems,
+        TomlSections.MAP_SPEED_TO_THICKNESS: map_speed_to_thickness,
+        TomlSections.FILTER_BY_FOLDER: filter_by_folder,
+        TomlSections.FILTER_BY_HOST_LABEL: filter_host_by_label,
+        TomlSections.FILTER_BY_HOST_TAG: filter_host_by_tag,
+    }
+
+    l2_seed_devices = r''' = [
+    # list of CDP/LLDP seed devices (if empty, all CDP/LLDP devices will be used)
+    # [0-9-a-zA-Z\.\_\-]{1,253} -> host
+
+    # "CORE01",
+    # "LOCATION01",
+    # "LOCATION02",
+]
+    '''
+
+    l2_drop_neighbours = r''' = [
+    # drop CDP/LLDP neighbours names
+
+    # "not advertised",
+    # "a nother invalid name",
+]
+    '''
+
+    l3_ignore_hosts = r''' = [
+    # hosts will be ignored in L3 topologies
+    # [0-9-a-zA-Z\.\_\-]{1,253} -> host
+    
+    # "host1",
+    # "host2",
+]
+    '''
+
+    l3_ignore_ip = r''' = [
+    # drop IP address that matches ip/network
+
+    # "192.168.100.231",
+    # "192.168.100.0/16",
+    # "192.168.150.0/255.255.255.0",
+    # "fd00::1"
+    # "fd00::/8"
+]
+    '''
+
+    l3v4_ignore_wildcard = r''' = [
+    # ignore IPs by wildcard
+    # if comparing an ip address:
+    # each 0 bit in the wildcard has to be exactly as in the pattern
+    # each 1 bit in the wildcard will be ignored
+
+    # [ pattern    ,  wildcard ]
+    # ["172.17.0.1", "0.0.255.0"],  # ignore all IPs ending with 1 from 172.17.0.0/16
+    # ["172.17.128.0", "0.0.127.3"],  # ignore all IPs ending with 0-3 from 172.17.128.0/17
+    # ["172.17.128.3", "0.0.127.0"],  # ignore all IPs ending with 3 from 172.17.128.0/17
+]
+'''
+
+    l3_summarize = r''' = [
+    # IP _networks_ to summarize
+
+    # "10.193.172.0/24",
+    # "10.194.8.0/23",
+    # "10.194.12.0/24",
+    # "10.194.115.0/255.255.255.0",
+    # "fd00::/8"
+]
+    '''
+
+    protected_topologies = r''' = [
+    # topologies will not be deleted by "--keep_max_topologies"
+
+    # "2023-10-17T14:08:05.10",
+    # "your_important_topology"
+]
+    '''
+
+    static_connections = r''' = [
+    # user defined static connections
+    # [0-9-a-zA-Z\.\_\-]{1,253} -> host
+
+    # valid entry formats
+    # ["left_host", "left_service", "right_service", "right_host"],
+    # connection: "left_host"<->"left_service"<->"right_service"<->"right_host"
+    # ["left_host", "", "right_service", "right_host"],
+    # connection: "left_host"<->"right_service"<->"right_host"
+    # ["left_host", "left_service", "", "right_host"],
+    # connection: "left_host"<->"left_service"<->"right_host"
+    # ["left_host", "", "", "right_host"],
+    # connection: "left_host"<->"right_host"
+]
+    '''
+
+    filter_by_customer = r''' = [
+    # list customers to include/exclude, use with option --filter-costumers INCLUDE/EXCLUDE
+    # [0-9-a-zA-Z\.\_\-]{1,16} -> customer
+    
+    # "customer1",
+    # "customer2",
+    # "customer3"
+]
+    '''
+
+    filter_by_site = r''' = [
+    # list site to include/exclude, use with option --filter-sites INCLUDE/EXCLUDE
+    # [0-9-a-zA-Z\.\_\-]{1,16} -> site
+    
+    # "site1",
+    # "site2",
+    # "site3",
+]
+    '''
+
+    missing_arrays = {
+        TomlSections.FILTER_BY_SITE: filter_by_site,
+        TomlSections.FILTER_BY_CUSTOMER: filter_by_customer,
+        TomlSections.STATIC_CONNECTIONS: static_connections,
+        TomlSections.PROTECTED_TOPOLOGIES: protected_topologies,
+        TomlSections.L3_SUMMARIZE: l3_summarize,
+        TomlSections.L3V4_IGNORE_WILDCARD: l3v4_ignore_wildcard,
+        TomlSections.L3_IGNORE_IP: l3_ignore_ip,
+        TomlSections.L3_IGNORE_HOSTS: l3_ignore_hosts,
+        TomlSections.L2_DROP_NEIGHBOURS: l2_drop_neighbours,
+        TomlSections.L2_SEED_DEVICES: l2_seed_devices,
+    }
+
+    changed: bool = False
+    org_file = Path(toml_file)
+    if not org_file.exists():
+        print(f'Config file {org_file.name} not found!')
+        sys_exit(ExitCodes.FILE_NOT_FOUND)
+
+    print(f'Checking file.: {org_file.name}')
+    org_content: str = org_file.read_text()
+    content: str = org_content
+    for old, new in fix_options.items():
+        re_pattern = f'\\b{old}\\b'
+        count = len(re_findall(re_pattern, content))
+        if count > 0:
+            changed = True
+            content = re_sub(re_pattern, new, content)
+            print(f'Found value...: "{old}" {count} times, replaced by "{new}"')
+
+    for old, new in fix_params.items():
+        if old in content:
+            changed = True
+            content = content.replace(old, new)
+            new_line = '\n'
+            print(f'Found value...: "{old.replace(new_line, "")}", replaced by "{new.replace(new_line, "")}"')
+
+    for missing_setting, value in missing_settings.items():
+        re_pattern = f'\\b{missing_setting}\\b'
+        count = len(re_findall(re_pattern, content))
+        if count == 0:
+            changed = True
+            content = re_sub(
+                f'\\[{TomlSections.SETTINGS}\\]\n',
+                f'[{TomlSections.SETTINGS}]\n# {missing_setting} = {value}\n',
+                content
+            )
+            print(f'Added option..: "# {missing_setting} = {value}"')
+
+    for table, value in missing_tables.items():
+        re_pattern = f'\\[{table}\\]\n'
+        count = len(re_findall(re_pattern, content))
+        if count == 0:
+            changed = True
+            content = re_sub(
+                f'\\[{TomlSections.SETTINGS}\\]\n',
+                f'[{table}]{value}\n[{TomlSections.SETTINGS}]\n',
+                content
+            )
+            print(f'Added section.: "[{table}]"')
+
+    for array, value in missing_arrays.items():
+        re_pattern = f'\\b{array}\\b'
+        count = len(re_findall(re_pattern, content))
+        if count == 0:
+            changed = True
+            content = content.replace(']\n\n#', f']\n\n#\n{array}{value}\n#', 1)
+            print(f'Added section.: "{array} = []"')
+            pass
+
+    for old, new in old_options.items():
+        re_pattern = f'\\b{old}\\b'
+        count = len(re_findall(re_pattern, org_content))
+        if count > 0:
+            print(f'Obsolete......: "{old}", {new}')
+
+    if changed:
+        backup_file = Path(f'{toml_file}.backup')
+        if not backup_file.exists():
+            org_file.rename(backup_file)
+            print(f'Renamed TOML..: {backup_file.name}')
+            new_file = Path(toml_file)
+            new_file.open('w').write(content)
+            print(f'Written fixed.: {new_file.name}')
+        else:
+            print(
+                f'Can not create backup file {backup_file.name}, file exists. Aborting!\n'
+                f'Nothing has changed.'
+            )
+    else:
+        print('Finished......: Nothing found to fix.')
diff --git a/source/bin/nvdct/lib/utils.py b/source/bin/nvdct/lib/utils.py
index 4eb62eeee370b7a302cf9b3812d6ec1f55a9ea69..98878f4ce5e833bd828737b2443398f4bb7644bb 100755
--- a/source/bin/nvdct/lib/utils.py
+++ b/source/bin/nvdct/lib/utils.py
@@ -237,7 +237,6 @@ def is_valid_output_directory(directory: str) -> bool:
     re_host_pattern = r'^[0-9a-z-A-Z\.\-\_\:]{1,30}$'
     if re_match(re_host_pattern, directory):
         return True
-        return True
     else:
         LOGGER.error(f'Invalid output directory name found: {directory}')
         return False
diff --git a/source/bin/nvdct/nvdct.py b/source/bin/nvdct/nvdct.py
index 48d47168211fa332708f76952c2b8c6ba03f47c3..2e59506abed001472a37e813158d7fe1e1c059d3 100755
--- a/source/bin/nvdct/nvdct.py
+++ b/source/bin/nvdct/nvdct.py
@@ -221,6 +221,7 @@
 #             fixed REST API query for interface services
 # 2025-01-24: added option --l2-display-ports, --l3-display-devices
 # 2025-02-05: added option "OFF" to --l2-case
+# 2025-02-07: added option --l2-ignore-mismatch
 #
 # creating topology data json from inventory data
 #
@@ -541,6 +542,7 @@ def main():
         topology.nv_connections.add_meta_data_to_connections(
             nv_objects=topology.nv_objects,
             speed_map=settings.map_speed_to_thickness,
+            ignore_mismatch=settings.l2_ignore_mismatch,
         )
 
         topology.save(
diff --git a/source/packages/nvdct b/source/packages/nvdct
index d277e55d94d95f7fc2f230b44f2e30ff6993ff98..5c37104728f9ef918cc9c7a1a7dc2a1f712da759 100644
--- a/source/packages/nvdct
+++ b/source/packages/nvdct
@@ -40,14 +40,15 @@
                    'nvdct/lib/__init__.py',
                    'nvdct/conf/nvdct.toml',
                    'nvdct/lib/topologies.py',
-                   'nvdct/lib/constants.py'],
+                   'nvdct/lib/constants.py',
+                   'nvdct/lib/update_config.py'],
            'web': ['htdocs/images/icons/cloud_80.png',
                    'htdocs/images/icons/ip-address_80.png',
                    'htdocs/images/icons/ip-network_80.png',
                    'htdocs/images/icons/location_80.png']},
  'name': 'nvdct',
  'title': 'Network Visualization Data Creation Tool (NVDCT)',
- 'version': '0.9.8-20250205',
+ 'version': '0.9.9-20250214',
  'version.min_required': '2.3.0b1',
  'version.packaged': 'cmk-mkp-tool 0.2.0',
  'version.usable_until': '2.4.0p1'}