diff --git a/agent_based/ospf_neighbor.py b/agent_based/ospf_neighbor.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b1662a6496cb99a7cfbbe2f21ffd602a135ba84
--- /dev/null
+++ b/agent_based/ospf_neighbor.py
@@ -0,0 +1,272 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+#
+###############################################################################
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+###############################################################################
+
+###############################################################################
+# $Id: ospf_neighbor 288 2012-07-10 11:06:38Z twollner $
+# Descr: OSPF Neighbor State check_mk check
+# $Author: twollner $
+# $Date: 2012-07-10 13:06:38 +0200 (Tue, 10 Jul 2012) $
+# $Rev: 288 $
+###############################################################################
+# Author: Thomas Wollner (tw@wollner-net.de)
+###############################################################################
+#
+# changes by: thl-cmk[at]outlook[dot]com
+# url       : https://thl-cmk.hopto.org
+#
+#  2018-06-15: changed item from neighbor id to neighbor ip
+#              added events as perfdata (incl. metrics file)
+#              moved part of the output to long output
+#              a little code cleanup to better match coding guide lines
+#  2019-11-03: moved 'events' from infotext to longoutput
+#  2020-07-26: added parse section, alias, wato for alias and state
+#  2021-09-15: rewritten for CMK 2.0
+#
+###############################################################################
+
+# Example Agent Output:
+# OSPF-MIB
+
+# 1.3.6.1.2.1.14.10.1.1.172.20.2.214.0 = IpAddress: 172.20.2.214
+# 1.3.6.1.2.1.14.10.1.2.172.20.2.214.0 = INTEGER: 0
+# 1.3.6.1.2.1.14.10.1.3.172.20.2.214.0 = IpAddress: 192.168.1.2
+# 1.3.6.1.2.1.14.10.1.4.172.20.2.214.0 = INTEGER: 2
+# 1.3.6.1.2.1.14.10.1.5.172.20.2.214.0 = INTEGER: 1
+# 1.3.6.1.2.1.14.10.1.6.172.20.2.214.0 = INTEGER: 8
+# 1.3.6.1.2.1.14.10.1.7.172.20.2.214.0 = Counter32: 6
+# 1.3.6.1.2.1.14.10.1.8.172.20.2.214.0 = Gauge32: 0
+# 1.3.6.1.2.1.14.10.1.9.172.20.2.214.0 = INTEGER: 1
+# 1.3.6.1.2.1.14.10.1.10.172.20.2.214.0 = INTEGER: 1
+# 1.3.6.1.2.1.14.10.1.11.172.20.2.214.0 = INTEGER: 2
+#
+# sample parsed
+# {
+#  '172.17.108.52': {'helperage': '', 'prio': '1', 'permanence': 'dynamic', 'helperstatus': '', 'options': '2',
+#                    'state': '8', 'hellosup': 'false', 'helperexitreason': '', 'events': 6, 'rtrid': '10.250.128.130'},
+#  '172.17.108.60': {'helperage': '', 'prio': '1', 'permanence': 'dynamic', 'helperstatus': '', 'options': '2',
+#                    'state': '8', 'hellosup': 'false', 'helperexitreason': '', 'events': 6, 'rtrid': '10.253.128.101'},
+#  '172.17.108.58': {'helperage': '', 'prio': '1', 'permanence': 'dynamic', 'helperstatus': '', 'options': '2',
+#                    'state': '8', 'hellosup': 'false', 'helperexitreason': '', 'events': 12, 'rtrid': '172.17.0.2'},
+#  '172.17.108.49': {'helperage': '', 'prio': '1', 'permanence': 'dynamic', 'helperstatus': '', 'options': '2',
+#                    'state': '8', 'hellosup': 'false', 'helperexitreason': '', 'events': 9, 'rtrid': '172.17.0.2'}
+# }
+#
+
+from dataclasses import dataclass
+from typing import Dict
+
+from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+    register,
+    Service,
+    Result,
+    State,
+    SNMPTree,
+    exists,
+    Metric,
+)
+from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
+    DiscoveryResult,
+    CheckResult,
+    StringTable,
+)
+
+
+@dataclass
+class OspfNeighbor:
+    rtrid: str
+    options: str
+    prio: str
+    state: str
+    events: int
+    permanence: str
+    hellosup: str
+    helperstatus: str
+    helperage: str
+    helperexitreason: str
+
+
+def parse_ospf_neighbor(string_table: StringTable) -> Dict[str, OspfNeighbor]:
+    def ospf_nbr_hellosuppressed(st: str) -> str:
+        names = {'1': 'true',
+                 '2': 'false'}
+        return names.get(st, st)
+
+    def ospf_nbr_permanence(st: str) -> str:
+        names = {'1': 'dynamic',
+                 '2': 'permanent'}
+        return names.get(st, st)
+
+    def ospf_nbr_helperstatus(st: str) -> str:
+        names = {'1': 'notHelping',
+                 '2': 'helping'}
+        return names.get(st, st)
+
+    def ospf_nbr_helperexitreason(st: str) -> str:
+        names = {'1': 'none',
+                 '2': 'inProgress',
+                 '3': 'completed',
+                 '4': 'timedOut',
+                 '5': 'topologyChanged'}
+        return names.get(st, st)
+
+    def ospf_nbr_options(st: str) -> str:
+        """
+        A bit mask corresponding to the neighbor's options field.
+        Bit 0, if set, indicates that the system will operate on Type of Service metrics other than TOS 0.
+               If zero, the neighbor will ignore all metrics except the TOS 0 metric.
+        Bit 1, if set, indicates that the associated area accepts and operates on external information;
+               if zero, it is a stub area.
+        Bit 2, if set, indicates that the system is capable of routing IP multicast datagrams, that is that it
+               implements the multicast extensions to OSPF.
+        Bit 3, if set, indicates that the associated area is an NSSA. These areas are capable of carrying type-7
+               external advertisements, which are translated into type-5 external advertisements at NSSA borders.
+        """
+        try:
+            st = ord(st)
+        except TypeError:
+            return 'unknown'
+
+        options = []
+        for key, value in [
+            (1, 'non TOS 0 service metrics accepted'),
+            (2, 'not a stub area'),
+            (4, 'IP multicast routing capable'),
+            (8, 'is NSSA'),
+        ]:
+            if st & key == key:
+                options.append(value)
+
+        options = ', '.join(options)
+        if options == '':
+            return 'unknown'
+        else:
+            return options
+
+    parsed = {}
+    for ip, rtrid, options, prio, state, events, permanence, hellosup, helperstatus, helperage, \
+        helperexitreason in string_table:
+        parsed[ip] = OspfNeighbor(
+            rtrid=rtrid,
+            options=ospf_nbr_options(options),
+            prio=prio,
+            state=state,
+            events=int(events),
+            permanence=ospf_nbr_permanence(str(permanence)),
+            hellosup=ospf_nbr_hellosuppressed(hellosup),
+            helperstatus=ospf_nbr_helperstatus(helperstatus),
+            helperage=helperage,
+            helperexitreason=ospf_nbr_helperexitreason(helperexitreason),
+        )
+    return parsed
+
+
+def discovery_ospf_neighbor(section: Dict[str, OspfNeighbor]) -> DiscoveryResult:
+    for neighbor in section.keys():
+        yield Service(item=neighbor)
+
+
+def check_ospf_neighbor(item, params, section: Dict[str, OspfNeighbor]) -> CheckResult:
+    def ospf_nbr_state(st):
+        names = {'1': 'down',
+                 '2': 'attempt',
+                 '3': 'init',
+                 '4': 'twoWay',
+                 '5': 'exchangeStart',
+                 '6': 'exchange',
+                 '7': 'loading',
+                 '8': 'full'}
+        return names.get(st, 'unknown: %s' % st)
+
+    # default checkmk states for ospfNbrState
+    neighborstate = {
+        '1': 2,  # down
+        '2': 1,  # attempt
+        '3': 1,  # init
+        '4': 0,  # twoWay
+        '5': 1,  # exchangeStart
+        '6': 1,  # exchange
+        '7': 1,  # loading
+        '8': 0,  # full
+    }
+
+    notFoundState = 2
+
+    for neighbour, neighbourAlias, neighbourNotFoundState in params.get('peer_list', []):
+        if item == neighbour:
+            yield Result(state=State.OK, summary=f'[{neighbourAlias}]')
+            notFoundState = neighbourNotFoundState
+
+    try:
+        neighbor = section[item]
+    except KeyError:
+        yield Result(state=State(notFoundState), notice='Item not found in SNMP data')
+        return 
+
+    yield Result(state=State.OK, summary=f'Neighbor ID: {neighbor.rtrid}')
+
+    neighborstate.update(params.get('neighborstate', neighborstate))  # update neighborstatus with params
+
+    yield Result(state=State(neighborstate.get(neighbor.state, 3)), summary=f'Status {ospf_nbr_state(neighbor.state)}')
+
+    yield Metric(value=neighbor.events, name='ospf_neighbor_ospf_events')
+
+    for text, value in [
+        ('options', neighbor.options),
+        ('priority', neighbor.prio),
+        ('permanence', neighbor.permanence),
+        ('hello suppressed', neighbor.hellosup),
+        ('helper status', neighbor.helperstatus),
+        ('helper age', neighbor.helperage),
+        ('helper exit reason', neighbor.helperexitreason),
+    ]:
+        if value != '':
+            yield Result(state=State.OK, notice=f'Neighbor {text}: {value}')
+
+
+register.snmp_section(
+    name='ospf_neighbor',
+    parse_function=parse_ospf_neighbor,
+    fetch=SNMPTree(
+        base='.1.3.6.1.2.1.14.10.1',  # OSPF-MIB::ospfNbrEntry
+        oids=[
+            '1',  # 'ospfNbrIpAddr'
+            '3',  # 'ospfNbrRtrId'
+            '4',  # 'ospfNbrOptions'
+            '5',  # 'ospfNbrPriority'
+            '6',  # 'ospfNbrState
+            '7',  # 'ospfNbrEvents'
+            '10',  # 'ospfNbrPermanence'
+            '11',  # 'ospfNbrHelloSuppressed'
+            '12',  # 'ospfNbrRestartHelperStatus'
+            '13',  # 'ospfNbrRestartHelperAge'
+            '14',  # 'ospfNbrRestartHelperExitReason'
+        ]
+    ),
+    detect=exists('.1.3.6.1.2.1.14.10.1.1.*')
+)
+
+register.check_plugin(
+    name='ospf_neighbor',
+    service_name='OSPF neighbor %s',
+    discovery_function=discovery_ospf_neighbor,
+    check_function=check_ospf_neighbor,
+    check_default_parameters={
+    },
+    check_ruleset_name='ospf_neighbor',
+)
diff --git a/ospf_neighbor.mkp b/ospf_neighbor.mkp
index 6c4dc70afda94f7814e4fc23f3cc38a047992696..f53c528d4152ff900f6478b5c36304833055d6d2 100644
Binary files a/ospf_neighbor.mkp and b/ospf_neighbor.mkp differ
diff --git a/packages/ospf_neighbor b/packages/ospf_neighbor
index 8b478bdf6d1fbc63c585715d3a5c60d5c8ca2aa9..b05a2c8dd0f7c126c1b5f676b4d53f241a1fb566 100644
--- a/packages/ospf_neighbor
+++ b/packages/ospf_neighbor
@@ -1,13 +1,23 @@
-{'author': u'Thomas Wollner',
- 'description': u'OSPF Neighborship State Check\nchanges by thl-cmk[at]outlook[dot]com]\n2018-06-15: changed item from neighbor id to neighbor address\n            added events as perfdata (incl. metrics file)\n            moved part of the output to long output\n2020-07-26: added parse section, alias, wato for alias and state\n',
- 'download_url': 'http://exchange.check-mk.org/',
- 'files': {'checkman': ['ospf_neighbor'],
-           'checks': ['ospf_neighbor'],
+{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)',
+ 'description': 'based on OSPF Neighbor State Check by Thomas Wollner\n'
+                '\n'
+                'changes by thl-cmk[at]outlook[dot]com]\n'
+                '2018-06-15: changed item from neighbor id to neighbor '
+                'address\n'
+                '            added events as perfdata (incl. metrics file)\n'
+                '            moved part of the output to long output\n'
+                '2020-07-26: added parse section, alias, wato for alias and '
+                'state\n'
+                '2021-09-15: rewritten for CMK 2.0\n',
+ 'download_url': 'https://thl-cmk.hopto.org',
+ 'files': {'agent_based': ['ospf_neighbor.py'],
+           'checkman': ['ospf_neighbor'],
            'web': ['plugins/metrics/ospf_neighbor.py',
                    'plugins/wato/ospf_neighbor.py']},
  'name': 'ospf_neighbor',
  'num_files': 4,
- 'title': u'OSPF Neighbor State Check',
- 'version': '20200726.v1.3',
- 'version.min_required': '1.2.8b8',
- 'version.packaged': '1.4.0p38'}
\ No newline at end of file
+ 'title': 'OSPF Neighbor State Check',
+ 'version': '20210915.v1.4',
+ 'version.min_required': '2.0.0',
+ 'version.packaged': '2021.07.14',
+ 'version.usable_until': None}
\ No newline at end of file
diff --git a/web/plugins/metrics/ospf_neighbor.py b/web/plugins/metrics/ospf_neighbor.py
index b157f66bf19fcd0eb148f649e33935705d35c397..5ed4d45e87fdabc7cac868a9224380f8e7286a67 100644
--- a/web/plugins/metrics/ospf_neighbor.py
+++ b/web/plugins/metrics/ospf_neighbor.py
@@ -1,18 +1,18 @@
-#!/usr/bin/python
-# -*- encoding: utf-8; py-indent-offset: 4 -*-
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
 #
 # OSPF neighbor metrics plugin
 #
 # Author: Th.L.
 # Date  : 2018-06-15
 #
+from cmk.gui.i18n import _
 
-#####################################################################################################################
-#
-# define units for OSPF neighbor perfdata
-#
-#####################################################################################################################
-
+from cmk.gui.plugins.metrics import (
+    metric_info,
+    graph_info,
+    perfometer_info
+)
 
 #####################################################################################################################
 #
@@ -26,29 +26,18 @@ metric_info['ospf_neighbor_ospf_events'] = {
     'color': '26/a',
 }
 
-
-######################################################################################################################
-#
-# map OSPF neighbor perfdata to metric
-#
-######################################################################################################################
-
-check_metrics['check_mk-ospf_neighbor'] = {
-    'ospf_events': {'name': 'ospf_neighbor_ospf_events', },
-}
-
 ######################################################################################################################
 #
 # how to graph perdata for OSPF neighbor
 #
 ######################################################################################################################
 
-graph_info.append({
+graph_info['ospf_neighbor_ospf_events'] = {
     'title': _('OSPF neighbor events'),
     'metrics': [
         ('ospf_neighbor_ospf_events', 'area'),
     ],
-})
+}
 
 ######################################################################################################################
 #
@@ -57,7 +46,7 @@ graph_info.append({
 ######################################################################################################################
 
 perfometer_info.append({
-        'type': 'linear',
-        'segments': ['ospf_neighbor_ospf_events'],
-        'total': 100,
-    })
+    'type': 'linear',
+    'segments': ['ospf_neighbor_ospf_events'],
+    'total': 100,
+})
diff --git a/web/plugins/wato/ospf_neighbor.py b/web/plugins/wato/ospf_neighbor.py
index 898e6c3d605da2b531c9311dc4f9538300b24a24..7b4122fb8bdcfb0a28a9a3af3fb759ba4882fdc9 100644
--- a/web/plugins/wato/ospf_neighbor.py
+++ b/web/plugins/wato/ospf_neighbor.py
@@ -1,5 +1,5 @@
-#!/usr/bin/python
-# -*- encoding: utf-8; py-indent-offset: 4 -*-
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
 #
 # License: GNU General Public License v2
 #
@@ -10,98 +10,113 @@
 # wato plugin for ospf_neighbor check
 #
 #
+from cmk.gui.i18n import _
+from cmk.gui.valuespec import (
+    Dictionary,
+    TextAscii,
+    ListOf,
+    Tuple,
+    TextUnicode,
+    MonitoringState,
+    Transform,
+)
 
-register_check_parameters(
-    subgroup_networking,
-    'ospf_neighbor',
-    _('OSPF neighbor'),
-    Dictionary(
-        elements=[
-            ('neighborstate',
-             Dictionary(
-                 title=_('State to report for OSPF neighbor state'),
-                 elements=[
-                     ('1',
-                      MonitoringState(
-                          title=_('1 - down'),
-                          default_value=2,
-                      ),
-                      ),
-                     ('2',
-                      MonitoringState(
-                          title=_('2 - attempt'),
-                          default_value=1,
-                      ),
-                      ),
-                     ('3',
-                      MonitoringState(
-                          title=_('3 - init'),
-                          default_value=1,
-                      ),
-                      ),
-                     ('4',
-                      MonitoringState(
-                          title=_('4 - twoWay'),
-                          default_value=0,
-                      ),
-                      ),
-                     ('5',
-                      MonitoringState(
-                          title=_('5 - exchangeStart'),
-                          default_value=1,
-                      ),
-                      ),
-                     ('6',
-                      MonitoringState(
-                          title=_('6 - exchange'),
-                          default_value=1,
-                      ),
-                      ),
-                     ('7',
-                      MonitoringState(
-                          title=_('7 - loading'),
-                          default_value=1,
-                      ),
-                      ),
-                     ('8',
-                      MonitoringState(
-                          title=_('8 - full'),
-                          default_value=0,
-                      ),
-                      ),
-                 ]
-             )
-             ),
-            ('peer_list',
-             ListOf(
-                 Tuple(
-                     title=('OSPF Neighbors'),
+from cmk.gui.plugins.wato import (
+    CheckParameterRulespecWithItem,
+    rulespec_registry,
+    RulespecGroupCheckParametersNetworking,
+)
+
+
+def _parameter_valuespec_ospf_neighbor():
+    return Transform(
+        Dictionary(
+            elements=[
+                ('neighborstate',
+                 Dictionary(
+                     title=_('State to report for OSPF neighbor state'),
                      elements=[
-                         TextUnicode(
-                             title=_('OSPF Neighbor IP address'),
-                             help=_('The configured value must match a OSPF Neighbor item reported by the monitored '
-                                    'device. For example: "10.10.10.10"'),
-                             allow_empty=False,
-                         ),
-                         TextUnicode(
-                             title=_('OSPF Neighbor Alias'),
-                             help=_('You can configure an individual alias here for the OSPF Neighbor matching '
-                                    'the text configured in the "OSPF Neighbor IP address" field. The alias will '
-                                    'be shown in the infotext'),
-                             allow_empty=False,
-                         ),
-                         MonitoringState(
-                             default_value=2,
-                             title=_('State if not found'),
-                             help=_('You can configure an individual state if the OSPF Neighbor matching the text '
-                                    'configured in the "OSPF Neighbor IP address" field is not found')
-                         )]),
-                 add_label=_('Add OSPF Neighbor'),
-                 movable=False,
-                 title=_('OSPF Neighbor specific configuration'),
-             )),
-        ],
-    ),
-    TextAscii(title=_('OSPF Neighbor IP address')),
-    match_type='dict',
-)
\ No newline at end of file
+                         ('1',
+                          MonitoringState(
+                              title=_('1 - down'),
+                              default_value=2,
+                          )),
+                         ('2',
+                          MonitoringState(
+                              title=_('2 - attempt'),
+                              default_value=1,
+                          )),
+                         ('3',
+                          MonitoringState(
+                              title=_('3 - init'),
+                              default_value=1,
+                          )),
+                         ('4',
+                          MonitoringState(
+                              title=_('4 - twoWay'),
+                              default_value=0,
+                          )),
+                         ('5',
+                          MonitoringState(
+                              title=_('5 - exchangeStart'),
+                              default_value=1,
+                          )),
+                         ('6',
+                          MonitoringState(
+                              title=_('6 - exchange'),
+                              default_value=1,
+                          )),
+                         ('7',
+                          MonitoringState(
+                              title=_('7 - loading'),
+                              default_value=1,
+                          )),
+                         ('8',
+                          MonitoringState(
+                              title=_('8 - full'),
+                              default_value=0,
+                          )),
+                     ])
+                 ),
+                ('peer_list',
+                 ListOf(
+                     Tuple(
+                         title=('OSPF Neighbors'),
+                         elements=[
+                             TextUnicode(
+                                 title=_('OSPF Neighbor IP address'),
+                                 help=_(
+                                     'The configured value must match a OSPF Neighbor item reported by the monitored '
+                                     'device. For example: "10.10.10.10"'),
+                                 allow_empty=False,
+                             ),
+                             TextUnicode(
+                                 title=_('OSPF Neighbor Alias'),
+                                 help=_('You can configure an individual alias here for the OSPF Neighbor matching '
+                                        'the text configured in the "OSPF Neighbor IP address" field. The alias will '
+                                        'be shown in the infotext'),
+                                 allow_empty=False,
+                             ),
+                             MonitoringState(
+                                 default_value=2,
+                                 title=_('State if not found'),
+                                 help=_('You can configure an individual state if the OSPF Neighbor matching the text '
+                                        'configured in the "OSPF Neighbor IP address" field is not found')
+                             )]),
+                     add_label=_('Add OSPF Neighbor'),
+                     movable=False,
+                     title=_('OSPF Neighbor specific configuration'),
+                 )),
+            ],
+        ))
+
+
+rulespec_registry.register(
+    CheckParameterRulespecWithItem(
+        check_group_name='ospf_neighbor',
+        group=RulespecGroupCheckParametersNetworking,
+        item_spec=lambda: TextAscii(title=_('OSPF Neighbor IP address'), ),
+        match_type='dict',
+        parameter_valuespec=_parameter_valuespec_ospf_neighbor,
+        title=lambda: _('OSPF neighbor'),
+    ))