diff --git a/check_bandwidth.py b/check_bandwidth.py index 9f9b492..b946a50 100755 --- a/check_bandwidth.py +++ b/check_bandwidth.py @@ -1,6 +1,8 @@ #!/usr/bin/env python3 import argparse +import re import sys +import time import traceback import psutil @@ -13,10 +15,49 @@ parser.add_argument('--bandwidth', type=float, required=True, help='Bandwidth sp parser.add_argument('--critical', type=int, default=75, help='Critical if percent of bandwidth usage is greater than or equal to this.') parser.add_argument('--warn', type=int, default=50, help='Warning if percent of bandwidth usage is greater than or equal to this.') parser.add_argument('--max', type=int, default=None, help='Set the max value the bandwidth can be. Useful for graphs and whatever.') -parser.add_argument('--ignore', default=None, help='Interface names to ignore.') +parser.add_argument('--ignore', nargs='*', default=[], help='Interface names to ignore, separated by a space.') +parser.add_argument('--ignore-re', default=None, help='Regex matching interface names to ignore.') args = parser.parse_args() -ignore_interfaces = args.ignore.split(',') if args.ignore else [] +if args.ignore_re: + ignore_re = re.compile(args.ignore_re) +else: + ignore_re = None + + +def get_network_traffic(interface): + net_io = psutil.net_io_counters(pernic=True) + if interface in net_io: + return net_io[interface] + else: + raise ValueError(f"Interface '{interface}' not found") + + +def calculate_network_traffic(interface, interval=1): + initial_traffic = get_network_traffic(interface) + start_time = time.perf_counter() + + # Should be more accurate that time.sleep() + while True: + current_time = time.perf_counter() + elapsed_time = current_time - start_time + if elapsed_time >= interval: + break + + final_traffic = get_network_traffic(interface) + + sent_bytes = final_traffic.bytes_sent - initial_traffic.bytes_sent + recv_bytes = final_traffic.bytes_recv - initial_traffic.bytes_recv + + sent_speed = sent_bytes / elapsed_time + recv_speed = recv_bytes / elapsed_time + + # Convert bytes per second to megabits per second + sent_speed_mbps = sent_speed * 8 / (1024 * 1024) + recv_speed_mbps = recv_speed * 8 / (1024 * 1024) + + return sent_speed_mbps, recv_speed_mbps + def main(): data = [] @@ -28,23 +69,11 @@ def main(): # Calculate bandwidth utilization for each interface for interface, stats in net_io_counters.items(): - if interface in ignore_interfaces: + if interface in args.ignore or (ignore_re and ignore_re.search(interface)): continue - # Get the number of bytes sent and received - bytes_sent = stats.bytes_sent - bytes_recv = stats.bytes_recv - - # Wait for 1 second - psutil.cpu_percent(interval=1) - - # Get the number of bytes sent and received after 1 second - new_stats = psutil.net_io_counters(pernic=True)[interface] - new_bytes_sent = new_stats.bytes_sent - new_bytes_recv = new_stats.bytes_recv - - # Calculate the bandwidth utilization in bits per second - bandwidth_utilization = (8 * (new_bytes_sent - bytes_sent + new_bytes_recv - bytes_recv)) / (1 * 1000 * 1000) - data.append([interface, bandwidth_utilization, 'none']) + sent_speed, recv_speed = calculate_network_traffic(interface) + bandwidth_utilization = sent_speed + recv_speed + data.append([interface, sent_speed, recv_speed, bandwidth_utilization, 'none']) exit_code = nagios.OK critical = [] @@ -53,7 +82,7 @@ def main(): perf_data = [] for i in range(len(data)): interface = data[i][0] - bandwidth_utilization = data[i][1] + bandwidth_utilization = data[i][3] if bandwidth_utilization >= crit_value: critical.append(interface) state = 'critical' @@ -67,7 +96,7 @@ def main(): ok.append(interface) state = 'ok' data[i][2] = f'[{state.upper()}]' - perf_data.append(f'{interface}={round(bandwidth_utilization, 2)}MB;{warn_value};{crit_value};{f"0;{args.max};" if args.max else ""} ') + perf_data.append(f'{interface}={round(bandwidth_utilization, 2)}Mbps;{warn_value};{crit_value};{f"0;{args.max};" if args.max else ""} ') if len(ok): print(f'OK: {", ".join(ok)}') diff --git a/check_opnsense_traffic_for_host.py b/check_opnsense_traffic_for_host.py index 8f90306..d67bb30 100755 --- a/check_opnsense_traffic_for_host.py +++ b/check_opnsense_traffic_for_host.py @@ -3,6 +3,7 @@ import argparse import sys import time import traceback +from ipaddress import ip_address, ip_network import numpy as np import requests @@ -13,6 +14,25 @@ from checker.markdown import list_to_markdown_table from checker.units import filesize +def is_internet_traffic(ip): + private_networks = [ + ip_network("10.0.0.0/8"), + ip_network("172.16.0.0/12"), + ip_network("192.168.0.0/16"), + ] + return not any(ip in network for network in private_networks) + + +def get_traffic_top(args, interface): + response = requests.get(f'https://{args.opnsense}/api/diagnostics/traffic/top/{interface}', + headers={'Accept': 'application/json'}, auth=(args.key, args.secret), verify=False, + timeout=10) + if response.status_code != 200: + print(f'UNKNOWN: unable to query OPNsense API for {interface}: {response.status_code}\n{response.text}') + sys.exit(nagios.UNKNOWN) + return response.json() + + def main(): parser = argparse.ArgumentParser(description='Check OPNsense network traffic for a host.') parser.add_argument('--opnsense', required=True, help='OPNsense hostname or IP address.') @@ -55,19 +75,24 @@ def main(): for name, interface in interface_names.items(): # Fetch the data - # TODO: account for network delays for the check duration traffic_data = [] for _ in range(args.duration): + start_time = time.time() response = requests.get(f'https://{args.opnsense}/api/diagnostics/traffic/top/{interface}', headers={'Accept': 'application/json'}, auth=(args.key, args.secret), verify=False, timeout=10) + end_time = time.time() + api_request_time = end_time - start_time + if response.status_code != 200: print(f'UNKNOWN: unable to query OPNsense API for {interface}: {response.status_code}\n{response.text}') sys.exit(nagios.UNKNOWN) for item in response.json().get(interface, {}).get('records', False): if item['address'] == args.host: traffic_data.append(item) - time.sleep(1) + + adjusted_sleep_duration = max(1 - api_request_time, 0) + time.sleep(adjusted_sleep_duration) if not len(traffic_data) and args.fail_empty: print('UNKNOWN: Interface or host not found in OPNsense API response. Raw response:') @@ -154,7 +179,9 @@ def main(): perf_data.append(f'\'{name}_cumulative_out\'={int(data["cumulative_out"])}B;{warn_b_value};{crit_b_value};0;') perf_data.append(f'\'{name}_connections\'={int(data["connections"])}B;{warn_b_value};{crit_b_value};0;') - output_table.append((args.host, name, filesize(data['rate_in']), filesize(data['rate_out']), filesize(data['cumulative_in']), filesize(data['cumulative_out']), data['connections'], status)) + output_table.append((args.host, name, filesize(data['rate_in']), filesize(data['rate_out']), + filesize(data['cumulative_in']), filesize(data['cumulative_out']), data['connections'], + status)) if len(critical): x = ['CRITICAL: ']