diff --git a/gui/metrics/ospfv3.py b/gui/metrics/ospfv3.py
new file mode 100644
index 0000000000000000000000000000000000000000..bbb0e8e4ca43a89d3dd029e2d17b6273c6a3e068
--- /dev/null
+++ b/gui/metrics/ospfv3.py
@@ -0,0 +1,218 @@
+#!/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  : 2019-11-02
+#
+# OSPFv3 plugin metrics
+#
+# 2023-06-03: moved gui files to ~/local/lib/chek_mk/gui/plugins/...
+#
+
+from cmk.gui.i18n import _
+
+from cmk.gui.plugins.metrics.utils import (
+    metric_info,
+    graph_info,
+    perfometer_info
+)
+
+
+#####################################################################################################################
+#
+# define metrics for perfdata
+#
+#####################################################################################################################
+
+metric_info['ospfv3_general_lsacount'] = {
+    'title': _('LSA count'),
+    'unit': 'count',
+    'color': '26/a',
+}
+metric_info['ospfv3_general_rxnewlsas'] = {
+    'title': _('receive new LSA count'),
+    'unit': 'count',
+    'color': '16/a',
+}
+metric_info['ospfv3_general_extlsacount'] = {
+    'title': _('Etxternal LSA count'),
+    'unit': 'count',
+    'color': '16/a',
+}
+
+metric_info['ospfv3_events'] = {
+    'title': _('Events'),
+    'unit': 'count',
+    'color': '26/a',
+}
+metric_info['ospfv3_lsretransqlen'] = {
+    'title': _('Queue length'),
+    'unit': 'count',
+    'color': '16/a',
+}
+
+metric_info['ospfv3_interface_events'] = {
+    'title': _('Events'),
+    'unit': 'count',
+    'color': '26/a',
+}
+metric_info['ospfv3_interface_linkscopelsacount'] = {
+    'title': _('Link LSA count'),
+    'unit': 'count',
+    'color': '16/a',
+}
+
+metric_info['ospfv3_area_spfruns'] = {
+    'title': _('SPF runs'),
+    'unit': 'count',
+    'color': '26/a',
+}
+metric_info['ospfv3_area_bdrrtcount'] = {
+    'title': _('# of ABRs'),
+    'unit': 'count',
+    'color': '11/a',
+}
+metric_info['ospfv3_area_asbdrrtrcount'] = {
+    'title': _('# of ASBRs'),
+    'unit': 'count',
+    'color': '22/a',
+}
+metric_info['ospfv3_area_scopelsacount'] = {
+    'title': _('Area LSA count'),
+    'unit': 'count',
+    'color': '23/a',
+}
+metric_info['ospfv3_area_nssatranslatorevents'] = {
+    'title': _('NSSA translator events'),
+    'unit': 'count',
+    'color': '11/a',
+}
+
+######################################################################################################################
+#
+# how to graph perdata
+#
+######################################################################################################################
+
+
+graph_info['ospfv3_general'] = {
+    'title': _('LSA count'),
+    'metrics': [
+        ('ospfv3_general_lsacount', 'line'),
+        ('ospfv3_general_rxnewlsas', 'line'),
+        ('ospfv3_general_extlsacount', 'line'),
+    ],
+}
+
+graph_info['ospfv3_events'] = {
+    'title': _('OSPFv3 Events'),
+    'metrics': [
+        ('ospfv3_events', 'area'),
+    ],
+}
+graph_info['ospfv3_lsretransqlen'] = {
+    'title': _('Link state retransmission queue length'),
+    'metrics': [
+        ('ospfv3_lsretransqlen', 'area'),
+    ],
+}
+graph_info['ospfv3_interface'] = {
+    'title': _('Link scope LSA count'),
+    'metrics': [
+        ('ospfv3_interface_linkscopelsacount', 'area'),
+    ],
+}
+graph_info['ospfv3_area_spfruns'] = {
+    'title': _('SPF runs'),
+    'metrics': [
+        ('ospfv3_area_spfruns', 'area'),
+    ],
+}
+graph_info['ospfv3_area_border'] = {
+    'title': _('Border router count'),
+    'metrics': [
+        ('ospfv3_area_bdrrtcount', 'area'),
+        ('ospfv3_area_asbdrrtrcount', '-area'),
+    ],
+}
+graph_info['ospfv3_area_scopelsacount'] = {
+    'title': _('Area scope LSA count'),
+    'metrics': [
+        ('ospfv3_area_scopelsacount', 'area'),
+    ],
+}
+graph_info['ospfv3_area_nssatranslatorevents'] = {
+    'title': _('NSSA translator events'),
+    'metrics': [
+        ('ospfv3_area_nssatranslatorevents', 'area'),
+    ],
+}
+
+######################################################################################################################
+#
+# define perf-o-meter
+#
+######################################################################################################################
+
+perfometer_info.append(('stacked', [
+    {
+        'type': 'linear',
+        'segments': ['ospfv3_general_lsacount',
+                     ],
+        'total': 100,
+    },
+    {
+        'type': 'linear',
+        'segments': ['ospfv3_general_extlsacount',
+                     ],
+        'total': 100,
+    }
+]))
+
+perfometer_info.append(('stacked', [
+    {
+        'type': 'linear',
+        'segments': ['ospfv3_events',
+                     ],
+        'total': 100,
+    },
+    {
+        'type': 'linear',
+        'segments': ['ospfv3_lsretransqlen',
+                     ],
+        'total': 100,
+    }
+]))
+
+perfometer_info.append(('stacked', [
+    {
+        'type': 'linear',
+        'segments': ['ospfv3_interface_events',
+                     ],
+        'total': 1000,
+    },
+    {
+        'type': 'linear',
+        'segments': ['ospfv3_interface_linkscopelsacount',
+                     ],
+        'total': 1000,
+    }
+]))
+
+perfometer_info.append(('stacked', [
+    {
+        'type': 'linear',
+        'segments': ['ospfv3_area_spfruns',
+                     ],
+        'total': 1000,
+    },
+    {
+        'type': 'linear',
+        'segments': ['ospfv3_area_scopelsacount',
+                     ],
+        'total': 1000,
+    }
+]))
diff --git a/gui/wato/ospfv3.py b/gui/wato/ospfv3.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce548ce7ae57c0f6384f435b7e21508c30d29452
--- /dev/null
+++ b/gui/wato/ospfv3.py
@@ -0,0 +1,373 @@
+#!/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  : 2021-09-28
+#
+# wato plugin for ospfv3 check
+#
+# 2022-11-30: fixed CheckParameterRulespecWithoutItem (from CheckParameterRulespecWithItem)
+# 2023-06-03: moved gui files to ~/local/lib/chek_mk/gui/plugins/...
+#
+from cmk.gui.i18n import _
+from cmk.gui.valuespec import (
+    Dictionary,
+    TextAscii,
+    ListOf,
+    Tuple,
+    TextUnicode,
+    MonitoringState,
+    FixedValue,
+)
+
+from cmk.gui.plugins.wato.utils import (
+    CheckParameterRulespecWithItem,
+    CheckParameterRulespecWithoutItem,
+    rulespec_registry,
+    RulespecGroupCheckParametersNetworking,
+    RulespecGroupCheckParametersDiscovery,
+    HostRulespec,
+)
+
+neighbor_states = ('neighbor_states',
+                   Dictionary(
+                       title=_('State to report for OSPF neighbor state'),
+                       help=_('Map each OSPF state to a CheckMK monitoring state'),
+                       elements=[
+                           ('1',
+                            MonitoringState(
+                                title=_('1 - down'),
+                                help=_(
+                                    'This is the first OSPF neighbor state. It means that no information (hellos) has '
+                                    'been received from this neighbor, but hello packets can still be sent to the '
+                                    'neighbor in this state. During the fully adjacent neighbor state, if a router '
+                                    'doesn\'t receive hello packet from a neighbor within the RouterDeadInterval time '
+                                    '(RouterDeadInterval = 4*HelloInterval by default) or if the manually configured '
+                                    'neighbor is being removed from the configuration, then the neighbor state changes '
+                                    'from Full to Down. Default monitoring state is "CRIT"'),
+                                default_value=2,
+                            )),
+                           ('2',
+                            MonitoringState(
+                                title=_('2 - attempt'),
+                                help=_(
+                                    'This state is only valid for manually configured neighbors in an NBMA '
+                                    'environment. In Attempt state, the router sends unicast hello packets every poll '
+                                    'interval to the neighbor, from which hellos have not been received within the '
+                                    'dead interval. Default monitoring state is "WARN"'),
+                                default_value=1,
+                            )),
+                           ('3',
+                            MonitoringState(
+                                title=_('3 - init'),
+                                help=_(
+                                    'This state specifies that the router has received a hello packet from its '
+                                    'neighbor, but the receiving router\'s ID was not included in the hello packet. '
+                                    'When a router receives a hello packet from a neighbor, it should list the '
+                                    'sender\'s router ID in its hello packet as an acknowledgment that it received a '
+                                    'valid hello packet. Default monitoring state is "WARN"'),
+                                default_value=1,
+                            )),
+                           ('4',
+                            MonitoringState(
+                                title=_('4 - twoWay'),
+                                help=_(
+                                    'This state designates that bi-directional communication has been established '
+                                    'between two routers. Bi-directional means that each router has seen the other\'s '
+                                    'hello packet. This state is attained when the router receiving the hello packet '
+                                    'sees its own Router ID within the received hello packet\'s neighbor field. At '
+                                    'this state, a router decides whether to become adjacent with this neighbor. On '
+                                    'broadcast media and non-broadcast multiaccess networks, a router becomes full '
+                                    'only with the designated router (DR) and the backup designated router (BDR); it '
+                                    'stays in the 2-way state with all other neighbors. On Point-to-point and '
+                                    'Point-to-multipoint networks, a router becomes full with all connected routers. '
+                                    'At the end of this stage, the DR and BDR for broadcast and non-broadcast '
+                                    'multiacess networks are elected. For more information on the DR election process, '
+                                    'refer to DR Election. Note: Receiving a Database Descriptor (DBD) packet from a '
+                                    'neighbor in the init state will also a cause a transition to 2-way state. Default '
+                                    'monitoring state is "OK"'),
+                                default_value=0,
+                            )),
+                           ('5',
+                            MonitoringState(
+                                title=_('5 - exchangeStart'),
+                                help=_('Once the DR and BDR are elected, the actual process of exchanging link state '
+                                       'information can start between the routers and their DR and BDR. In this state, '
+                                       'the routers and their DR and BDR establish a master-slave relationship and '
+                                       'choose the initial sequence number for adjacency formation. The router with '
+                                       'the higher router ID becomes the master and starts the exchange, and as such, '
+                                       'is the only router that can increment the sequence number. Note that one would '
+                                       'logically conclude that the DR/BDR with the highest router ID will become the '
+                                       'master during this process of master-slave relation. Remember that the DR/BDR '
+                                       'election might be purely by virtue of a higher priority configured on the '
+                                       'router instead of highest router ID. Thus, it is possible that a DR plays the '
+                                       'role of slave. And also note that master/slave election is on a per-neighbor '
+                                       'basis. Default monitoring state is "WARN"'),
+                                default_value=1,
+                            )),
+                           ('6',
+                            MonitoringState(
+                                title=_('6 - exchange'),
+                                help=_(
+                                    'In the exchange state, OSPF routers exchange database descriptor (DBD) packets. '
+                                    'Database descriptors contain link-state advertisement (LSA) headers only and '
+                                    'describe the contents of the entire link-state database. Each DBD packet has a '
+                                    'sequence number which can be incremented only by master which is explicitly '
+                                    'acknowledged by slave. Routers also send link-state request packets and '
+                                    'link-state update packets (which contain the entire LSA) in this state. The '
+                                    'contents of the DBD received are compared to the information contained in the '
+                                    'routers link-state database to check if new or more current link-state '
+                                    'information is available with the neighbor. Default monitoring state is "WARN"'),
+                                default_value=1,
+                            )),
+                           ('7',
+                            MonitoringState(
+                                title=_('7 - loading'),
+                                help=_(
+                                    'In this state, the actual exchange of link state information occurs. Based on the '
+                                    'information provided by the DBDs, routers send link-state request packets. The '
+                                    'neighbor then provides the requested link-state information in link-state update '
+                                    'packets. During the adjacency, if a router receives an outdated or missing LSA, '
+                                    'it requests that LSA by sending a link-state request packet. All link-state '
+                                    'update packets are acknowledged. Default monitoring state is "WARN"'),
+                                default_value=1,
+                            )),
+                           ('8',
+                            MonitoringState(
+                                title=_('8 - full'),
+                                help=_('In this state, routers are fully adjacent with each other. All the router and '
+                                       'network LSAs are exchanged and the routers databases are fully synchronized. '
+                                       'Full is the normal state for an OSPF router. If a router is stuck in another '
+                                       'state, it\'s an indication that there are problems in forming adjacencies. The '
+                                       'only exception to this is the 2-way state, which is normal in a broadcast '
+                                       'network. Routers achieve the full state with their DR and BDR only. Neighbors '
+                                       'always see each other as 2-way. Default monitoring state is "OK"'),
+                                default_value=0,
+                            )),
+                       ]))
+
+
+# #####################################################
+#
+# OSPFv3 General
+#
+# #####################################################
+
+
+def _parameter_valuespec_ospfv3_general():
+    return Dictionary(
+        elements=[
+            ('state_admin_disabled',
+             MonitoringState(
+                 title=_('State to report if OSPFv3 is admin disabled'),
+                 help=_('Monitoring state if the OSPFv3 process is admin disabled. Default state is "WARNING"'),
+                 default_value=1,
+             )),
+        ],
+    )
+
+
+rulespec_registry.register(
+    CheckParameterRulespecWithoutItem(
+        check_group_name='ospfv3_general',
+        group=RulespecGroupCheckParametersNetworking,
+        match_type='dict',
+        parameter_valuespec=_parameter_valuespec_ospfv3_general,
+        title=lambda: _('OSPFv3 general'),
+    ))
+
+
+def _valuespec_discovery_ospfv3_general():
+    return Dictionary(
+            title=_('OSPFv3 general'),
+            elements=[
+                ('admin_disabled',
+                 FixedValue(
+                     True,
+                     title=_('discover admin disabled OSPFv3 process'),
+                     totext=_('discover admin disabled OSPFv3 process'),
+                     default_value=False,
+                 )),
+            ],
+        )
+
+
+rulespec_registry.register(
+    HostRulespec(
+        group=RulespecGroupCheckParametersDiscovery,
+        match_type='dict',
+        name='discovery_ospfv3_general',
+        valuespec=_valuespec_discovery_ospfv3_general,
+    ))
+
+
+# #####################################################
+#
+# OSPFv3 Interface
+#
+# #####################################################
+
+
+def _parameter_valuespec_ospfv3_interface():
+    return Dictionary(
+        elements=[
+            ('state_admin_disabled',
+             MonitoringState(
+                 title=_('State to report if OSPFv3 is admin disabled'),
+                 help=_('Monitoring state if OSPFv3 is admin disabled. Default state is "WARNING"'),
+                 default_value=1,
+             )),
+            ('state_if_down',
+             MonitoringState(
+                 title=_('State to report if OSPFv3 is down'),
+                 help=_('Monitoring state if OSPFv3 is down. Default state is "CRITICAL"'),
+                 default_value=2,
+             )),
+            ('state_if_wait_stby',
+             MonitoringState(
+                 title=_('State to report if OSPFv3 is in waiting or standby'),
+                 help=_('Monitoring state if OSPFv3 is waiting/standby. Default state is "WARNING"'),
+                 default_value=1,
+             )),
+        ],
+    )
+
+
+rulespec_registry.register(
+    CheckParameterRulespecWithItem(
+        check_group_name='ospfv3_interface',
+        group=RulespecGroupCheckParametersNetworking,
+        item_spec=lambda: TextAscii(title=_('OSPFv3 interface'), ),
+        match_type='dict',
+        parameter_valuespec=_parameter_valuespec_ospfv3_interface,
+        title=lambda: _('OSPFv3 interface'),
+    ))
+
+
+# #####################################################
+#
+# OSPFv3 neighbor
+#
+# #####################################################
+
+
+def _parameter_valuespec_ospfv3_neighbor():
+    return Dictionary(
+        elements=[
+            ('state_not_found',
+             MonitoringState(
+                 title=_('State to report if neighbor not found'),
+                 help=_('Default monitoring state if the neighbor is not found in the SNMP data. '
+                        'Default state is "UNKNOWN"'),
+                 default_value=3,
+             )),
+            neighbor_states,
+            ('peer_list',
+             ListOf(
+                 Tuple(
+                     title=_('Neighbors'),
+                     elements=[
+                         TextUnicode(
+                             title=_('OSPFv3 Neighbor'),
+                             help=_(
+                                 'The configured value must match a OSPFv3 Neighbor item reported by the monitored '
+                                 'device. For example: "FE80::192:168:10:148 on Vlan1"'),
+                             allow_empty=False,
+                         ),
+                         TextUnicode(
+                             title=_('OSPFv3 Neighbor Alias'),
+                             help=_('You can configure an individual alias here for the OSPF Neighbor matching '
+                                    'the text configured in the "OSPFv3 Neighbor" field. The alias will '
+                                    'be shown in the check info (i.e. [your alias])'),
+                             allow_empty=False,
+                         ),
+                         MonitoringState(
+                             default_value=2,
+                             title=_('State if not found'),
+                             help=_('You can configure an individual state if the OSPFv3 Neighbor matching the text '
+                                    'configured in the "OSPFv3 Neighbor" field is not found. '
+                                    'Default state is "CRITICAL".')
+                         )]),
+                 add_label=_('Add OSPFv3 Neighbor'),
+                 movable=False,
+                 title=_('Neighbor specific configuration'),
+             )),
+        ],
+    )
+
+
+rulespec_registry.register(
+    CheckParameterRulespecWithItem(
+        check_group_name='ospfv3_neighbor',
+        group=RulespecGroupCheckParametersNetworking,
+        item_spec=lambda: TextAscii(title=_('OSPFv3 neighbor'), ),
+        match_type='dict',
+        parameter_valuespec=_parameter_valuespec_ospfv3_neighbor,
+        title=lambda: _('OSPFv3 neighbor'),
+    ))
+
+
+# #####################################################
+#
+# OSPFv3 virtual link
+#
+# #####################################################
+
+
+def _parameter_valuespec_ospfv3_virtuallink():
+    return Dictionary(
+        elements=[
+            ('state_not_found',
+             MonitoringState(
+                 title=_('State to report if virtual link not found'),
+                 help=_('Default monitoring state if the virtual link is not found in the SNMP data. '
+                        'Default monitoring state is "UNKNOWN"'),
+                 default_value=3,
+             )),
+            neighbor_states,
+            ('peer_list',
+             ListOf(
+                 Tuple(
+                     title=_('Virtual Link'),
+                     elements=[
+                         TextUnicode(
+                             title=_('OSPFv3 Virtual-Link'),
+                             help=_(
+                                 'The configured value must match a OSPFv3 Virtual-Link item reported by the monitored '
+                                 'device. For example: "2003::192:168:0:1"'),
+                             allow_empty=False,
+                         ),
+                         TextUnicode(
+                             title=_('OSPFv3 Virtual-Link Alias'),
+                             help=_('You can configure an individual alias here for the OSPFv3 Virtual-Link matching '
+                                    'the text configured in the "OSPFv3 Virtual-Link" field. The alias will '
+                                    'be shown in the check info (i.e. [your alias])'),
+                             allow_empty=False,
+                         ),
+                         MonitoringState(
+                             default_value=2,
+                             title=_('State if not found'),
+                             help=_('You can configure an individual state if the OSPFv3 Virtual-Link matching the '
+                                    'text configured in the "OSPFv3 Virtual-Link" field is not found. '
+                                    'Default state is "CRITICAL".')
+                         )]),
+                 add_label=_('Add Virtual-Link'),
+                 movable=False,
+                 title=_('Virtual-Link specific configuration'),
+             )),
+        ],
+    )
+
+
+rulespec_registry.register(
+    CheckParameterRulespecWithItem(
+        check_group_name='ospfv3_virtuallink',
+        group=RulespecGroupCheckParametersNetworking,
+        item_spec=lambda: TextAscii(title=_('OSPFv3 virtual link'), ),
+        match_type='dict',
+        parameter_valuespec=_parameter_valuespec_ospfv3_virtuallink,
+        title=lambda: _('OSPFv3 virtual link'),
+    ))
diff --git a/ospfv3-0.4.0-20230603.mkp b/ospfv3-0.4.0-20230603.mkp
new file mode 100644
index 0000000000000000000000000000000000000000..1e5393c905409894a73a8314c175e8e8be576335
Binary files /dev/null and b/ospfv3-0.4.0-20230603.mkp differ
diff --git a/ospfv3.mkp b/ospfv3.mkp
index acf2054658790d0c83cfe9acd35a5e675828fdf6..1e5393c905409894a73a8314c175e8e8be576335 100644
Binary files a/ospfv3.mkp and b/ospfv3.mkp differ
diff --git a/packages/ospfv3 b/packages/ospfv3
index 59c4c4e9e2de74ac9d670dbba3f96217eda8ec5e..8301b3111dcd1de9cea6d43e2dff38a3d92e7116 100644
--- a/packages/ospfv3
+++ b/packages/ospfv3
@@ -14,11 +14,10 @@
                            'ospfv3_neighbor.py',
                            'ospfv3_virtuallink.py',
                            'utils/ospfv3.py'],
-           'web': ['plugins/metrics/ospfv3.py', 'plugins/wato/ospfv3.py']},
+           'gui': ['metrics/ospfv3.py', 'wato/ospfv3.py']},
  'name': 'ospfv3',
- 'num_files': 8,
  'title': 'Collection of OSPFv3 checks',
- 'version': '20221130.v0.3a',
- 'version.min_required': '2.0.0',
- 'version.packaged': '2021.09.20',
+ 'version': '0.4.0-20230603',
+ 'version.min_required': '2.1.0b1',
+ 'version.packaged': '2.1.0p21',
  'version.usable_until': None}
\ No newline at end of file