From aeaa238199c63b4512997ce728f28f56147f33ca Mon Sep 17 00:00:00 2001 From: "th.l" <thl-cmk@outlook.com> Date: Mon, 9 Dec 2024 14:19:05 +0100 Subject: [PATCH] added support for IPv6, changed hostlabel to nvdct/l3v4_topology, rewritten for check API 2.0 --- README.md | 2 +- mkp/inv_ip_address-0.0.5-20241209.mkp | Bin 0 -> 3610 bytes source/agent_based/inv_ipv4_addresses.py | 164 ----------- .../agent_based/inv_ip_addresses.py | 275 ++++++++++++++++++ source/packages/inv_ip_address | 19 ++ source/packages/inv_ipv4_addresses | 12 - ..._ipv4_addresses.py => inv_ip_addresses.py} | 6 +- 7 files changed, 298 insertions(+), 180 deletions(-) create mode 100644 mkp/inv_ip_address-0.0.5-20241209.mkp delete mode 100644 source/agent_based/inv_ipv4_addresses.py create mode 100644 source/cmk_addons_plugins/inv_ip_address/agent_based/inv_ip_addresses.py create mode 100644 source/packages/inv_ip_address delete mode 100644 source/packages/inv_ipv4_addresses rename source/web/plugins/views/{inv_ipv4_addresses.py => inv_ip_addresses.py} (92%) diff --git a/README.md b/README.md index 1f83346..ee96488 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[PACKAGE]: ../../raw/master/mkp/inv_ipv4_addresses-0.0.4-20240407.mkp "inv_ipv4_addresses-0.0.4-20240407.mkp" +[PACKAGE]: ../../raw/master/mkp/inv_ip_address-0.0.5-20241209.mkp "inv_ip_address-0.0.5-20241209.mkp" # Inventory of IP addresses The plugin adds the IP-addresses information to the inventory for devices monitored via SNMP. The plugin supports IPv4 and IPv6. diff --git a/mkp/inv_ip_address-0.0.5-20241209.mkp b/mkp/inv_ip_address-0.0.5-20241209.mkp new file mode 100644 index 0000000000000000000000000000000000000000..833326e6f28e095e4581d685cac51e08697256e2 GIT binary patch literal 3610 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 literal 0 HcmV?d00001 diff --git a/source/agent_based/inv_ipv4_addresses.py b/source/agent_based/inv_ipv4_addresses.py deleted file mode 100644 index 8a90ed5..0000000 --- a/source/agent_based/inv_ipv4_addresses.py +++ /dev/null @@ -1,164 +0,0 @@ -#!/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 : 2023-12-27 -# File : inv_ipv4_addresses.py -# -# inventory of IPv4 address information -# -# 2024-04-07: fixed missing/wrong netmask (ThX bitwiz@forum.checkmk.com) -# improved validation if SNMP input data -# drop this host ip address (0.0.0.0) - -from dataclasses import dataclass -from typing import List -from ipaddress import AddressValueError, IPv4Address, IPv4Network, NetmaskValueError - -from cmk.base.plugins.agent_based.agent_based_api.v1 import ( - OIDEnd, -) -from cmk.base.plugins.agent_based.agent_based_api.v1 import ( - HostLabel, - SNMPTree, - TableRow, - exists, - register, -) -from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( - HostLabelGenerator, - InventoryResult, - StringTable, -) - - -@dataclass(frozen=True) -class Ipv4Info: - address: str - broadcast: str - cidr: int - if_index: int | None - if_name: str - max_re_assemble: int | None - netmask: str - network: str - - -def parse_inv_ipv4_addresses(string_table: List[StringTable]) -> List[Ipv4Info] | None: - - try: - ipv4_info, if_info = string_table - except ValueError: - return - - try: - interface_by_index = {if_index: if_name for if_index, if_name in if_info} - except ValueError: - interface_by_index = {} - - ipv4_infos = [] - - for entry in ipv4_info: - try: - ipv4_address, if_index, ipv4_netmask, ipv4_bcast, max_re_assemble = entry - except ValueError: - continue - - try: - ipv4_address = IPv4Address(ipv4_address) - except AddressValueError: - continue - - if ipv4_address.is_loopback: # Drop localhost - continue - - if ipv4_address.exploded == '0.0.0.0': # drop this host address - continue - - try: - ipv4 = IPv4Network(address=f'{ipv4_address.exploded}/{ipv4_netmask}', strict=False) - except (AddressValueError, NetmaskValueError): - continue - - ipv4_infos.append( - Ipv4Info( - address=str(ipv4_address.exploded), - broadcast=str(ipv4.broadcast_address), - cidr=int(ipv4.prefixlen), - if_index=int(if_index) if if_index.isdigit() else None, - if_name=str(interface_by_index.get(if_index, if_index)), - max_re_assemble=int(max_re_assemble) if max_re_assemble.isdigit() else None, - netmask=str(ipv4.netmask), - network=str(ipv4.network_address), - ) - ) - - return ipv4_infos - - -def host_label_inv_ipv4_addresses(section: List[Ipv4Info]) -> HostLabelGenerator: - _non_host_ips = 0 - for ipv4 in section: - if ipv4.cidr != 32 and not ipv4.address.startswith('127.'): - _non_host_ips += 1 - if _non_host_ips > 1: - yield HostLabel(name="nvdct/routing_capable", value="yes") - - -def inventory_ipv4_addresses(section: List[Ipv4Info]) -> InventoryResult: - path = ['networking', 'addresses'] - - for ipv4_info in section: - key_columns = { - 'address': ipv4_info.address, - 'device': ipv4_info.if_name, - } - inventory_columns = { - 'broadcast': ipv4_info.broadcast, - 'cidr': ipv4_info.cidr, - # 'if_index': ipv4_info.if_index, - # 'max_re_assemble': ipv4_info.max_re_assemble, - 'netmask': ipv4_info.netmask, - 'network': ipv4_info.network, - 'type': 'ipv4', - } - yield TableRow( - path=path, - key_columns=key_columns, - inventory_columns=inventory_columns - ) - - -register.snmp_section( - name='inv_ipv4_addresses', - parse_function=parse_inv_ipv4_addresses, - host_label_function=host_label_inv_ipv4_addresses, - fetch=[ - SNMPTree( - base='.1.3.6.1.2.1.4.20.1', # IP-MIB::ipAddrEntry - oids=[ - '1', # ipAdEntAddr - '2', # ipAdEntIfIndex - '3', # ipAdEntNetMask - '4', # ipAdEntBcastAddr - '5', # ipAdEntReasmMaxSize - - ] - ), - SNMPTree( - base='.1.3.6.1.2.1.31.1.1.1', # - oids=[ - OIDEnd(), # ifIndex - '1', # ifName - ]), - ], - detect=exists('.1.3.6.1.2.1.4.20.1.1.*'), # -) - -register.inventory_plugin( - name='inv_ipv4_addresses', - inventory_function=inventory_ipv4_addresses, -) diff --git a/source/cmk_addons_plugins/inv_ip_address/agent_based/inv_ip_addresses.py b/source/cmk_addons_plugins/inv_ip_address/agent_based/inv_ip_addresses.py new file mode 100644 index 0000000..92b6d60 --- /dev/null +++ b/source/cmk_addons_plugins/inv_ip_address/agent_based/inv_ip_addresses.py @@ -0,0 +1,275 @@ +#!/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 : 2023-12-27 +# File : inv_ip_addresses.py +# +# inventory of IPv4 address information +# +# 2024-04-07: fixed missing/wrong netmask (ThX bitwiz@forum.checkmk.com) +# improved validation of SNMP input data +# drop this host ip address (0.0.0.0) +# 2024-12-02: incompatible: changed host label to nvdct/l3v4_host:yes and nvdct/l3v4_routing:yes +# 2024-12-03: added IP-MIB::ipAddressTable for IPv6 support +# incompatible: renamed to inv_ip_address -> remove inv_ipv4_address.mkp before updating +# 2024-12-05 changed to use ip_interface +# 2024-12-06: incompatible: changed hostlabel to nvdct/l3v4_topology:host and nvdct/l3v4_topology:router +# 2024-12-09: rewritten for CMK checkAPI 2.0 + +from collections.abc import Mapping, MutableSequence, Sequence +from ipaddress import AddressValueError, NetmaskValueError, ip_interface +from re import match as re_match +from typing import List + +from cmk.agent_based.v2 import ( + HostLabel, + HostLabelGenerator, + InventoryPlugin, + InventoryResult, + OIDBytes, + OIDEnd, + SNMPSection, + SNMPTree, + StringByteTable, + TableRow, + exists, +) + + +__ip_info_34_ios = [ + [ + '1.4.10.10.10.230', # OID end -> type: ipv4, length: 4, ipv4 address + [], # ip address -> empty + '3', # interface index + '.1.3.6.1.2.1.4.32.1.5.3.1.4.10.10.10.228.30' # prefix -> last number (30) + ], + [ + '2.16.42.0.28.160.16.0.1.53.0.0.0.0.0.0.0.2', # OID end -> type: ipv6, length: 16, ipv6 address + [], + '3', + '.1.3.6.1.2.1.4.32.1.5.3.2.16.42.0.28.160.16.0.1.53.0.0.0.0.0.0.0.0.64' + ], + [ + '4.20.254.128.0.0.0.0.0.0.114.219.152.255.254.159.41.2.18.0.0.8', + # OID end -> type: ipv6z, length: 20, ipv6 address with interface identifier (18.0.0.8) + [], + '3', + '.0.0' + ], +] +__ip_info_34_ibm = [ + [ + '1.15.48.49.48.46.49.52.48.46.49.54.48.46.48.49.55', + # OID end -> type: ipv4, length: 15, ipv4 address ('010.140.160.017') + [], + '805306370', + '.0.0' + ], + [ + '2.39.48.48.48.48.58.48.48.48.48.58.48.48.48.48.58.48.48.48.48.58.48.48.48.48.58.48.48.48.48.58.48.48.48.48.58.48.48.48.49', + # OID end -> type: ipv6, length: 39, ipv6 address ('0000:0000:0000:0000:0000:0000:0000:0001') + [], + '805306371', + '.0.0' + ], +] +__ip_info_34_firepower = [ + [ + '1.10.1.1.2', # OID end -> type: ipv4, , ipv4 address ('10.1.1.2') + [10, 1, 1, 2], # ip address in dec bytes + '18', + '.1.3.6.1.2.1.4.32.1.5.18.1.10.1.1.0.24' + ], + [ + '2.253.0.0.0.0.0.0.1.0.0.0.0.0.0.0.1', + # OID end -> type: ipv6, ipv6 address ('253.0.0.0.0.0.0.1.0.0.0.0.0.0.0.1') + [253, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], # ip address in dec bytes + '4', + '.1.3.6.1.2.1.4.32.1.5.4.2.253.0.0.0.0.0.0.1.0.0.0.0.0.0.0.0.64' + ], +] +__ip_info_34_fortinet = [ + [ + '1.10.118.132.1.76', # OID end -> type: ipv4, ipv4 address (10.118.132.1), interface index (76) + [], + '76', + '.0.0.0' # prefix -> missing + ], + [ + '2.10762.22982.8208.4113.0.0.0.282.40', + # OID end -> type: ipv6, ipv6 address (10762.22982.8208.4113.0.0.0.282), interface index (76) + [], + '40', + '.0.0.0' # prefix -> missing + ], +] + +Section = Sequence[Mapping[str, ip_interface]] | None + + +def parse_inv_ip_addresses(string_table: List[StringByteTable]) -> Section: + try: + ip_info_20, ip_info_34, if_info = string_table + except ValueError: + return + + try: + interface_by_index = {if_index: if_name for if_index, if_name in if_info} + except ValueError: + interface_by_index = {} + + ip_infos: MutableSequence[Mapping[str, ip_interface]] = [] + + for entry in ip_info_34: + try: + oid_end, dec_ip_address, if_index, ip_prefix = entry + except ValueError: + continue + + if (prefix := ip_prefix.split('.')[-1]) == '0': # drop entries without prefix (0) -> fortinet + continue + + if not (raw_ip := re_match(r'(\d+)\.(\d+)\.([\d|\.]+)', oid_end)): + continue + + raw_type, raw_length, raw_address = raw_ip.groups() + + if dec_ip_address: + raw_address = '.'.join(str(x) for x in dec_ip_address) + raw_length = str(len(dec_ip_address)) + + match raw_type: + case '1': # IPv4 address + if raw_length == '15': + raw_address = ''.join([chr(int(x)) for x in raw_address.split('.')]) + raw_address = '.'.join([str(int(x)) for x in raw_address.split('.')]) + case '2': # IPv6 address + match raw_length: + case '16': + raw_address = [f'{int(x):02x}' for x in raw_address.split('.')] + raw_address = ':'.join( + [''.join([raw_address[i], raw_address[i + 1]]) for i in range(0, len(raw_address), 2)] + ) + case '39': + raw_address = ''.join([chr(int(x)) for x in raw_address.split('.')]) + case _: + continue + + try: + interface = ip_interface(f'{raw_address}/{prefix}') + except (AddressValueError, NetmaskValueError): + continue + + if interface.ip.is_loopback: # Drop localhost + continue + + if interface.ip.exploded == '0.0.0.0': # drop this host address + continue + + ip_infos.append({(str(interface_by_index.get(if_index, if_index))): interface}) + + for entry in ip_info_20: + try: + raw_address, if_index, raw_netmask = entry + except ValueError: + continue + + try: + interface = ip_interface(f'{raw_address}/{raw_netmask}') + except (AddressValueError, NetmaskValueError): + continue + + if interface.ip.is_loopback: # Drop localhost + continue + + if interface.ip.exploded == '0.0.0.0': # drop this host address + continue + + ip_infos.append({str(interface_by_index.get(if_index, if_index)): interface}) + + return ip_infos + + +def host_label_inv_ip_addresses(section: Section) -> HostLabelGenerator: + """ + Host label function + Labels: + nvdct/l3v4_topology: + This label is set to "host" for all devices with one IPv4 address except form 127.0.0.0/8 and to + "router" for all devices with more than one IPv4 address except form 127.0.0.0/8 + """ + non_host_ips = 0 + for entry in section: + ip_data = entry.values() + if ip_data.version == 4 and not ip_data.ip.is_loopback: + non_host_ips += 1 + if non_host_ips == 1: + yield HostLabel(name="nvdct/l3v4_topology", value="host") + if non_host_ips > 1: + yield HostLabel(name="nvdct/l3v4_topology", value="router") + return + + +def inventory_ip_addresses(section: Section) -> InventoryResult: + address_type = { + 4: 'ipv4', + 6: 'ipv6', + } + for entry in section: + for if_name, ip_data in entry.items(): + yield TableRow( + path=['networking', 'addresses'], + key_columns={ + 'address': str(ip_data.ip.compressed), + 'device': if_name, + }, + inventory_columns={ + 'broadcast': str(ip_data.network.broadcast_address), + 'cidr': ip_data.network.prefixlen, + 'netmask': str(ip_data.network.netmask), + 'network': str(ip_data.network.network_address), + 'type': address_type.get(ip_data.version).lower(), + } + ) + + +snmp_section_inv_ip_address = SNMPSection( + name='inv_ip_addresses', + parse_function=parse_inv_ip_addresses, + host_label_function=host_label_inv_ip_addresses, + fetch=[ + SNMPTree( + base='.1.3.6.1.2.1.4.20.1', # IP-MIB::ipAddrEntry + oids=[ + '1', # ipAdEntAddr + '2', # ipAdEntIfIndex + '3', # ipAdEntNetMask + ] + ), + SNMPTree( + base='.1.3.6.1.2.1.4.34.1', # IP-MIB::ipAddrEntry + oids=[ + OIDEnd(), # type.length.ip-address + OIDBytes('2'), # ipAddressAddr + '3', # ipAddressIfIndex + '5', # ipAddressPrefix + ] + ), + SNMPTree( + base='.1.3.6.1.2.1.31.1.1.1', # + oids=[ + OIDEnd(), # ifIndex + '1', # ifName + ]), + ], + detect=exists('.1.3.6.1.2.1.4.20.1.1.*'), # +) + +inventory_plugin_inv_ip_address = InventoryPlugin( + name='inv_ip_addresses', + inventory_function=inventory_ip_addresses, +) diff --git a/source/packages/inv_ip_address b/source/packages/inv_ip_address new file mode 100644 index 0000000..e96434d --- /dev/null +++ b/source/packages/inv_ip_address @@ -0,0 +1,19 @@ +{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)', + 'description': 'Adds the IP addresses of a device monitored via SNMP to the ' + 'inventory\n' + '\n' + 'Adds host labels:\n' + ' - nvdct/l3v4_host: This label is set to "yes" for all ' + 'devices with one IPv4 address except form 127.0.0.0/8\n' + ' - nvdct/l3v4_routing: This label is set to "yes" for all ' + 'devices with more than one IPv4 address except form ' + '127.0.0.0/8\n', + 'download_url': 'https://thl-cmk.hopto.org', + 'files': {'cmk_addons_plugins': ['inv_ip_address/agent_based/inv_ip_addresses.py'], + 'web': ['plugins/views/inv_ip_addresses.py']}, + 'name': 'inv_ip_address', + 'title': 'Inventory of IP addresses', + 'version': '0.0.5-20241209', + 'version.min_required': '2.3.0b1', + 'version.packaged': 'cmk-mkp-tool 0.2.0', + 'version.usable_until': '2.4.0b1'} diff --git a/source/packages/inv_ipv4_addresses b/source/packages/inv_ipv4_addresses deleted file mode 100644 index c6d34da..0000000 --- a/source/packages/inv_ipv4_addresses +++ /dev/null @@ -1,12 +0,0 @@ -{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)', - 'description': 'Adds the IPv4 addresses of a device monitored via SNMP to the ' - 'inventory\n', - 'download_url': 'https://thl-cmk.hopto.org', - 'files': {'agent_based': ['inv_ipv4_addresses.py'], - 'web': ['plugins/views/inv_ipv4_addresses.py']}, - 'name': 'inv_ipv4_addresses', - 'title': 'Inventory of IPv4 addresses', - 'version': '0.0.4-20240407', - 'version.min_required': '2.2.0b1', - 'version.packaged': 'cmk-mkp-tool 0.2.0', - 'version.usable_until': '2.4.0b1'} diff --git a/source/web/plugins/views/inv_ipv4_addresses.py b/source/web/plugins/views/inv_ip_addresses.py similarity index 92% rename from source/web/plugins/views/inv_ipv4_addresses.py rename to source/web/plugins/views/inv_ip_addresses.py index b6fbf88..81afc04 100644 --- a/source/web/plugins/views/inv_ipv4_addresses.py +++ b/source/web/plugins/views/inv_ip_addresses.py @@ -19,16 +19,16 @@ inventory_displayhints.update({ 'address', 'device', 'type', - 'cidr', - 'netmask', 'network', + 'netmask', + 'cidr', 'broadcast', ], 'view': 'invipaddresses_of_host', }, '.networking.addresses:*.address': {'title': _l('Address')}, '.networking.addresses:*.broadcast': {'title': _l('Broadcast')}, - '.networking.addresses:*.cidr': {'title': _l('CIDR'), }, # 'filter': FilterInvtableIDRange}, + '.networking.addresses:*.cidr': {'title': _l('Prefix'), }, # 'filter': FilterInvtableIDRange}, '.networking.addresses:*.device': {'title': _l('Device')}, '.networking.addresses:*.netmask': {'title': _l('Netmask')}, '.networking.addresses:*.network': {'title': _l('Network')}, -- GitLab