From ec1f73e7b1ff8b396b39978e7945b49271870d27 Mon Sep 17 00:00:00 2001
From: "th.l" <thl-cmk@outlook.com>
Date: Sat, 4 Dec 2021 13:40:55 +0100
Subject: [PATCH] update project

---
 active_check_traceroute.mkp               | Bin 0 -> 7345 bytes
 checks/check_traceroute                   |  69 +++++
 lib/nagios/plugins/check_traceroute       | 297 ++++++++++++++++++++++
 packages/active_check_traceroute          |  37 +++
 web/plugins/metrics/traceroute.py         |  32 +++
 web/plugins/wato/active_checks_routing.py | 179 +++++++++++++
 6 files changed, 614 insertions(+)
 create mode 100644 active_check_traceroute.mkp
 create mode 100644 checks/check_traceroute
 create mode 100755 lib/nagios/plugins/check_traceroute
 create mode 100644 packages/active_check_traceroute
 create mode 100644 web/plugins/metrics/traceroute.py
 create mode 100644 web/plugins/wato/active_checks_routing.py

diff --git a/active_check_traceroute.mkp b/active_check_traceroute.mkp
new file mode 100644
index 0000000000000000000000000000000000000000..1fa711dfb8738d7caf5ba8e42dfd15a0da11910e
GIT binary patch
literal 7345
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

literal 0
HcmV?d00001

diff --git a/checks/check_traceroute b/checks/check_traceroute
new file mode 100644
index 0000000..a0eec4b
--- /dev/null
+++ b/checks/check_traceroute
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2
+# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
+# conditions defined in the file COPYING, which is part of this source code package.
+
+
+def check_traceroute_arguments(params):
+    args = []
+
+    if not params['dns']:
+        args.append('-n')
+    method = params['method']
+    if method == 'icmp':
+        args.append('-I')
+    elif method == 'tcp':
+        args.append('-T')
+    # else: None -> default method
+
+    for router, state in params['routers']:
+        args += ['-%s' % state, router]
+
+    family = params.get('address_family')
+    if family is None:
+        family = 'ipv6' if is_ipv6_primary(host_name()) else 'ipv4'
+
+    if family == 'ipv6':
+        args.append('-6')
+    else:
+        args.append('-4')
+
+    # additional options
+    if 'port' in params:
+        args.append(f'-p {params["port"]}')
+
+    if 'queries' in params:
+        args.append(f'-q {params["queries"]}')
+
+    if 'max_ttl' in params:
+        args.append(f'-m {params["max_ttl"]}')
+
+    # needs root
+    if 'source_interface' in params:
+        args.append(f'-i {params["source_interface"]}')
+    # needs root
+    if 'source_address' in params:
+        args.append(f'-s {params["source_address"]}')
+
+    if 'destination_address' in params:
+        args.append(params['destination_address'])
+    else:
+        args.append('$HOSTADDRESS$')
+
+    return args
+
+
+def _check_description(params):
+    if 'description' in params:
+        return f'Routing {params["description"]}'
+
+    return 'Routing'
+
+
+active_check_info['traceroute'] = {
+    'command_line': 'check_traceroute $ARG1$',
+    'argument_function': check_traceroute_arguments,
+    'service_description': _check_description,
+    'has_perfdata': True,
+}
diff --git a/lib/nagios/plugins/check_traceroute b/lib/nagios/plugins/check_traceroute
new file mode 100755
index 0000000..dfa2290
--- /dev/null
+++ b/lib/nagios/plugins/check_traceroute
@@ -0,0 +1,297 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2
+# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
+# conditions defined in the file COPYING, which is part of this source code package.
+
+# This check does a traceroute to the specified target host
+# (usually $HOSTADDRESS$ itself) and checks which route(s) are
+# being taken. That way you can check if your preferred or
+# some alternative route in in place.
+# traceroute is expected to be in the search path and installed
+# with SUID root bit.
+
+# Example output from traceroute -n
+# traceroute to www.google.de (173.194.44.55), 30 hops max, 60 byte packets
+#  1  10.10.11.4  0.419 ms  0.444 ms  0.518 ms
+#  2  33.117.16.28  14.359 ms  14.371 ms  14.434 ms
+#  3  112.18.7.119  14.750 ms  14.765 ms  19.530 ms
+#  4  184.50.190.61  17.844 ms  17.865 ms  17.862 ms
+#  5  67.249.94.88  24.285 ms  78.527 ms  26.834 ms
+#  6  209.85.240.99  27.910 ms  27.420 ms  27.442 ms
+#  7  173.194.44.55  26.583 ms  20.410 ms  23.142 ms
+
+# Output without -n option:
+# traceroute to www.google.de (173.194.44.56), 30 hops max, 60 byte packets
+#  1  fritz.box (10.10.11.4)  0.570 ms  0.606 ms  0.677 ms
+#  2  foo-bar.x-online.net (33.117.16.28)  14.566 ms  14.580 ms  14.658 ms
+#  3  xu-2-3-0.rt-inxs-1.x-online.net (112.13.6.109)  18.214 ms  18.228 ms  18.221 ms
+#  4  * * *
+#  5  66.249.94.88 (66.249.94.88)  24.481 ms  24.498 ms  24.271 ms
+#  6  209.85.240.99 (209.85.240.99)  27.628 ms  21.605 ms  21.943 ms
+#  7  muc03s08-in-f24.1e100.net (173.194.44.56)  21.277 ms  22.236 ms  22.192 ms
+
+# Example output for IPv6
+# traceroute to ipv6.google.com (2404:6800:4004:80e::200e), 30 hops max, 80 byte packets
+#  1  2001:2e8:665:0:2:2:0:1 (2001:2e8:665:0:2:2:0:1)  0.082 ms  0.046 ms  0.044 ms
+#  2  2001:2e8:22:204::2 (2001:2e8:22:204::2)  0.893 ms  0.881 ms  0.961 ms
+#  3  * 2001:4860:0:1::1abd (2001:4860:0:1::1abd)  225.189 ms *
+#  4  2001:4860:0:1003::1 (2001:4860:0:1003::1)  3.052 ms  2.820 ms 2001:4860:0:1002::1 (2001:4860:0:1002::1)  1.501 ms
+#  5  nrt13s48-in-x0e.1e100.net (2404:6800:4004:80e::200e)  1.910 ms  1.828 ms  1.753 ms
+
+# It is also possible that for one hop several different answers appear:
+# 11 xe-0-0-1-0.co2-96c-1b.ntwk.msn.net (204.152.141.11)  174.185 ms xe-10-0-2-0.co1-96c-1a.ntwk.msn.net (207.46.40.94)  174.279 ms xe-0-0-1-0.co2-96c-1b.ntwk.msn.net (204.152.141.11)  174.444 ms
+
+# if DNS fails then it looks like this:
+#  5  66.249.94.88 (66.249.94.88)  24.481 ms  24.498 ms  24.271 ms
+#  6  209.85.240.99 (209.85.240.99)  27.628 ms  21.605 ms  21.943 ms
+
+import ast
+import getopt
+import ipaddress
+import os
+import subprocess
+import sys
+
+
+class MissingValueError(Exception):
+    pass
+
+
+class ProtocolVersionError(Exception):
+    pass
+
+
+class ExecutionError(Exception):
+    pass
+
+
+def parse_exception(exc):
+    exc = str(exc)
+    if exc[0] == "{":
+        exc = "%d - %s" % list(ast.literal_eval(exc).values())[0]
+    return str(exc)
+
+
+def output_check_result(s, perfdata):
+    if perfdata:
+        perfdata_output_entries = ["%s=%s" % (p[0], ";".join(map(str, p[1:]))) for p in perfdata]
+        s += " | %s" % " ".join(perfdata_output_entries)
+    sys.stdout.write("%s\n" % s)
+
+
+def option_to_state(c):
+    return {"w": 1, "c": 2}[c.lower()]
+
+
+def _execute_traceroute(target, nodns, method, address_family, queries, max_ttl, port, source_addr, source_int):
+    cmd = ["traceroute"]
+    if nodns:
+        cmd.append("-n")
+    if method:
+        cmd.append(method)
+    if address_family:
+        cmd.append(address_family)
+    if port and method != "-I":
+        cmd.append(f"-p {port}")
+    if queries:
+        cmd.append(f"-q {queries}")
+    if max_ttl:
+        cmd.append(f"-m {max_ttl}")
+    if source_int:
+        cmd.append(f"-i {source_int}")
+
+    if source_addr:
+        cmd.append(f"-s {source_addr}")
+
+    cmd.append(target)
+
+    if (source_int is not None) or (source_addr is not None):
+        cmd = ' '.join(cmd)
+        shell = True
+    else:
+        shell = False
+
+    p = subprocess.Popen(args=cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf8", shell=shell)
+    sto, ste = p.communicate()
+    if p.returncode:
+        raise ExecutionError("UNKNOWN - " + ste.replace("\n", " "))
+    return sto
+
+
+def check_traceroute(lines, routes):
+    # find all visited routers
+    routers = set([])
+    hops = len(lines[1:])
+    for line in lines[1:]:
+        parts = line.strip().split()
+        for part in parts:
+            try:
+                part = part.lstrip("(").rstrip(",").rstrip(")")
+                ipaddress.ip_interface(part)
+                routers.add(part)
+            except ValueError:
+                pass
+
+    state = 0
+    bad_routers = []
+    missing_routers = []
+    for option, route in routes:
+        s = option_to_state(option)
+        if option.islower() and route in routers:
+            state = max(state, s)
+            bad_routers.append("%s(%s)" % (route, "!" * s))
+        elif option.isupper() and route not in routers:
+            state = max(state, s)
+            missing_routers.append("%s(%s)" % (route, "!" * s))
+
+    info_text = "%d hops, missing routers: %s, bad routers: %s" % (
+        hops,
+        missing_routers and ", ".join(missing_routers) or "none",
+        bad_routers and ", ".join(bad_routers) or "none",
+    )
+    perfdata = [("hops", hops)]
+    return state, info_text, perfdata
+
+
+def validate_ip_version(address_arg, ip_version_arg):
+    # ipv6 address can have an appended interface index/name: 'fe80::%{interface}'
+    try:
+        ip_address_version = ipaddress.ip_interface(address_arg.split("%")[0]).ip.version
+    except ValueError:
+        # May be a host or DNS name, don't execute the validation in this case.
+        # check_traceroute will perform the name resolution for us.
+        return
+
+    if not ip_address_version == ip_version_arg:
+        raise ProtocolVersionError(
+            'IP protocol version "%s" not the same as the IP address version "%s".'
+            % (ip_version_arg, ip_address_version)
+        )
+
+
+def usage():
+    sys.stdout.write(
+        """check_traceroute -{c|w|C|W} ROUTE  [-{o|c|w|O|C|W} ROUTE...] TARGET
+
+Check by which routes TARGET is being reached. Each possible route is being
+prefixed with a state option:
+
+ -w Make outcome WARN if that route is present
+ -W Make outcome WARN if that route is missing
+ -c Make outcome CRIT if that route is present
+ -C Make outcome CRIT if that route is missing
+
+Other options:
+
+ -h, --help     show this help and exit
+ --debug        show Python exceptions verbosely
+ -n             disable reverse DNS lookups
+ -I             Use ICMP ECHO for probes
+ -T             Use TCP SYN for probes
+ -4             Use IPv4
+ -6             Use IPv6
+ -p port        Set the destination port to use
+ -m max_ttl     Set the max number of hops (max TTL to be reached). Default is 30
+ -s src_addr    Use source address
+ -i device      Specify a network interface to operate with
+ -q nqueries    Set the number of probes per each hop. Default is 3
+
+"""
+    )
+
+
+def main(args=None):
+    if args is None:
+        args = sys.argv[1:]
+
+    os.unsetenv("LANG")
+
+    opt_verbose = 0
+    opt_debug = False
+    opt_nodns = False
+    opt_method = None
+    opt_address_family = None
+    opt_port = None
+    opt_source_int = None
+    opt_source_addr = None
+    opt_queries = None
+    opt_max_ttl = None
+
+    short_options = "hw:W:c:C:i:s:p:q:m:nTI46"
+    long_options = [
+        "verbose",
+        "help",
+        "debug",
+    ]
+
+    route_params = []
+
+    try:
+        opts, args = getopt.getopt(args, short_options, long_options)
+
+        if len(args) < 1:
+            usage()
+            raise MissingValueError("Please specify the target destination.")
+
+        target_address = args[0]
+
+        # first parse modifers
+        for o, a in opts:
+            if o in ["-v", "--verbose"]:
+                opt_verbose += 1
+            elif o in ["-d", "--debug"]:
+                opt_debug = True
+            elif o in ["-w", "-W", "-c", "-C"]:
+                route_params.append((o[1], a))
+            elif o == "-n":
+                opt_nodns = True
+            elif o in ["-T", "-I"]:
+                opt_method = o
+            elif o in ["-4", "-6"]:
+                if opt_address_family:
+                    raise ProtocolVersionError("Cannot use both IPv4 and IPv6")
+                validate_ip_version(target_address, int(o.lstrip("-")))
+                opt_address_family = o
+            elif o in ["-s"]:
+                opt_source_addr = a
+            elif o in ["-i"]:
+                opt_source_int = a
+            elif o in ["-p"]:
+                opt_port = a
+            elif o in ["-q"]:
+                opt_queries = a
+            elif o in ["-m"]:
+                opt_max_ttl = a
+
+        # now handle action options
+        for o, a in opts:
+            if o in ["-h", "--help"]:
+                usage()
+                sys.exit(0)
+
+        sto = _execute_traceroute(target_address, opt_nodns, opt_method, opt_address_family, opt_queries,
+                                  opt_max_ttl, opt_port, opt_source_addr, opt_source_int)
+        status, output, perfdata = check_traceroute(sto.split("\n"), route_params)
+        info_text = output.strip() + "\n%s" % sto
+        return status, info_text, perfdata
+
+    except ExecutionError as e:
+        return 3, str(e), None
+
+    except MissingValueError as e:
+        return 3, str(e), None
+
+    except ProtocolVersionError as e:
+        return 3, str(e), None
+
+    except Exception as e:
+        if opt_debug:
+            raise
+        return 2, "Unhandled exception: %s" % parse_exception(e), None
+
+
+if __name__ == "__main__":
+    exitcode, info, perf = main()
+    output_check_result(info, perf)
+    sys.exit(exitcode)
diff --git a/packages/active_check_traceroute b/packages/active_check_traceroute
new file mode 100644
index 0000000..9d4a911
--- /dev/null
+++ b/packages/active_check_traceroute
@@ -0,0 +1,37 @@
+{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)',
+ 'description': 'extended traceroute check plugin\n'
+                '\n'
+                'adds the following options:\n'
+                '- service description suffix\n'
+                '- alternate destination address\n'
+                '- destination port for UDP/TCP path trace\n'
+                '- max hops\n'
+                '- queries per hop\n'
+                '- source address \n'
+                '- source interface (needs root permissions).\n'
+                '\n'
+                'Source address and source interface uses the "shell=True" '
+                'option in in\n'
+                'the "subprocess.Popen" command. This is highly insecure, so '
+                'be careful.\n'
+                '\n'
+                'Note: the original option TCP path trace also needs root '
+                'permissions.\n'
+                '\n'
+                'To give "traceroute" root permissions you need to set the '
+                'SUID bit for\n'
+                'your traceroute program, like "sudo chmod u+s '
+                '/usr/bin/traceroute.db" \n'
+                'on Ubuntu 20.04.3 LTS.\n',
+ 'download_url': 'https://thl-cmk.hopto.org',
+ 'files': {'checks': ['check_traceroute'],
+           'lib': ['nagios/plugins/check_traceroute'],
+           'web': ['plugins/wato/active_checks_routing.py',
+                   'plugins/metrics/traceroute.py']},
+ 'name': 'active_check_traceroute',
+ 'num_files': 4,
+ 'title': 'Active Check Traceroute',
+ 'version': '20212104.v.0.0.1a',
+ 'version.min_required': '2.0.0',
+ 'version.packaged': '2021.09.20',
+ 'version.usable_until': None}
\ No newline at end of file
diff --git a/web/plugins/metrics/traceroute.py b/web/plugins/metrics/traceroute.py
new file mode 100644
index 0000000..9ee094f
--- /dev/null
+++ b/web/plugins/metrics/traceroute.py
@@ -0,0 +1,32 @@
+#!/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-12-04
+#
+#
+#
+
+from cmk.gui.i18n import _
+
+from cmk.gui.plugins.metrics import (
+    metric_info,
+    graph_info,
+    perfometer_info
+
+)
+
+metric_info['hops'] = {
+    'title': _('Number of hops'),
+    'unit': 'count',
+    'color': '41/a',
+}
+
+perfometer_info.append({
+    'type': 'linear',
+    'segments': ['hops'],
+    'total': 30,
+})
diff --git a/web/plugins/wato/active_checks_routing.py b/web/plugins/wato/active_checks_routing.py
new file mode 100644
index 0000000..474e425
--- /dev/null
+++ b/web/plugins/wato/active_checks_routing.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env python3
+# -*- encoding: utf-8; py-indent-offset: 4 -*-
+
+#
+# (c) 2013 Heinlein Support GmbH
+#          Robert Sander <r.sander@heinlein-support.de>
+#
+
+# This 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 in version 2.  This file is distributed
+# in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
+# out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
+# PARTICULAR PURPOSE. See the  GNU General Public License for more de-
+# ails.  You should have  received  a copy of the  GNU  General Public
+# License along with GNU Make; see the file  COPYING.  If  not,  write
+# to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
+# Boston, MA 02110-1301 USA.
+
+from cmk.gui.i18n import _
+from cmk.gui.valuespec import (
+    Dictionary,
+    ListOf,
+    Tuple,
+    Transform,
+    Checkbox,
+    DropdownChoice,
+    TextInput,
+    Integer,
+    TextAscii,
+)
+
+from cmk.gui.plugins.wato import (
+    rulespec_registry,
+    HostRulespec,
+)
+
+from cmk.gui.plugins.wato.active_checks import (
+    RulespecGroupActiveChecks
+)
+
+
+def _ip_address_family_element():
+    return (
+        'address_family',
+        DropdownChoice(
+            title=_('IP address family'),
+            choices=[
+                (None, _('Primary address family')),
+                ('ipv4', _('Enforce IPv4')),
+                ('ipv6', _('Enforce IPv6')),
+            ],
+            default_value=None,
+        ),
+    )
+
+
+def _transform_add_address_family(v):
+    v.setdefault('address_family', None)
+    return v
+
+
+def _valuespec_active_checks_traceroute():
+    return Transform(
+        Dictionary(
+            title=_('Check current routing'),
+            help=_(
+                'This active check uses <tt>traceroute</tt> in order to determine the current '
+                'routing from the monitoring host to the target host. You can specify any number '
+                'of missing or expected routes in order to detect e.g. an (unintended) failover '
+                'to a secondary route.'
+            ),
+            elements=[
+                ('description',
+                 TextAscii(
+                     title=_('Service Description suffix'),
+                     help=_('Must be unique for every host. The service description starts always with \"Routing\".'),
+                     size=30,
+                 )),
+                ('dns',
+                 Checkbox(
+                     title=_('Name resolution'),
+                     label=_('Use DNS to convert IP addresses into hostnames'),
+                     help=_(
+                         'If you use this option, then <tt>traceroute</tt> is <b>not</b> being '
+                         'called with the option <tt>-n</tt>. That means that all IP addresses '
+                         'are tried to be converted into names. This usually adds additional '
+                         'execution time. Also DNS resolution might fail for some addresses.'
+                     ),
+                 ),),
+                _ip_address_family_element(),
+                ('routers',
+                 ListOf(
+                     Tuple(
+                         elements=[
+                             TextInput(
+                                 title=_('Router (FQDN, IP-Address)'),
+                                 allow_empty=False,
+                             ),
+                             DropdownChoice(
+                                 title=_('How'),
+                                 choices=[
+                                     ('W', _('WARN - if this router is not being used')),
+                                     ('C', _('CRIT - if this router is not being used')),
+                                     ('w', _('WARN - if this router is being used')),
+                                     ('c', _('CRIT - if this router is being used')),
+                                 ],
+                             ),
+                         ]
+                     ),
+                     title=_('Router that must or must not be used'),
+                     add_label=_('Add Condition'),
+                 ),),
+                ('method',
+                 DropdownChoice(
+                     title=_('Method of probing'),
+                     choices=[
+                         (None, _('UDP (default behaviour of traceroute)')),
+                         ('icmp', _('ICMP Echo Request')),
+                         ('tcp', _('TCP SYN, needs root permissions')),
+                     ],
+                     default_value='icmp',
+                 ),),
+                ('port',
+                 Integer(
+                     title=_('Port'),
+                     help=_('Set the destination port to use. It is either initial udp port value for \"default\" '
+                            'method (incremented by each probe, default is 33434), or initial seq for \"icmp\" '
+                            '(incremented as well, default from 1), or some constant destination port for other '
+                            'methods (with default of 80 for \"tcp\", 53 for \"udp\", etc.)'),
+                     maxvalue=65535,
+                 ),),
+                ('destination_address',
+                 TextAscii(
+                     title=_('Alternate Destination'),
+                     help=_('Path trace to alternate destination instead of \"HOSTADDRESS\".'),
+                 ),),
+                ('queries',
+                 Integer(
+                     title=_('Number of queries'),
+                     help=_('Set the number of probes per each hop. Default is 3.'),
+                     default_value=3,
+                     minvalue=1,
+                     maxvalue=10,
+                 ),),
+                ('max_ttl',
+                 Integer(
+                     title=_('Max hops'),
+                     help=_('Set the max number of hops (max TTL to be reached). Default is 30'),
+                     default_value=30,
+                     minvalue=1,
+                     maxvalue=255,
+                 ),),
+                ('source_interface',
+                 TextAscii(
+                     title=_('Source interface'),
+                     help=_('Specify a network interface to operate with. Needs root permissions.'),
+                 ),),
+                ('source_address',
+                 TextAscii(
+                     title=_('Source address'),
+                     help=_('Use source source address for outgoing packets'),
+                 ),),
+            ],
+            optional_keys=['description', 'max_ttl', 'queries', 'destination_address', 'source_address',
+                           'source_interface', 'port'],
+        ),
+        forth=_transform_add_address_family,
+    )
+
+
+rulespec_registry.register(
+    HostRulespec(
+        group=RulespecGroupActiveChecks,
+        match_type='all',
+        name='active_checks:traceroute',
+        valuespec=_valuespec_active_checks_traceroute,
+    )
+)
-- 
GitLab