From a4464209b6741f2c1764aa432c56cfa8128852a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Floure?= Date: Thu, 22 Apr 2021 09:29:53 +0200 Subject: [PATCH] [scanner] add minimal error handling, consolidate CLI args processing --- cdist/argparse.py | 10 ++++-- cdist/scan/commandline.py | 19 ++++++----- cdist/scan/scan.py | 71 +++++++++++---------------------------- 3 files changed, 37 insertions(+), 63 deletions(-) diff --git a/cdist/argparse.py b/cdist/argparse.py index cadac39a..153e7864 100644 --- a/cdist/argparse.py +++ b/cdist/argparse.py @@ -492,12 +492,16 @@ def get_parsers(): help='Try to configure detected hosts') parser['scan'].add_argument( '-I', '--interfaces', - action='append', default=[], + action='append', default=[], required=True, help='On which interfaces to scan/trigger') parser['scan'].add_argument( '-d', '--delay', - action='store', default=3600, - help='How long to wait before reconfiguring after last try') + action='store', default=3600, type=int, + help='How long (seconds) to wait before reconfiguring after last try') + parser['scan'].add_argument( + '-t', '--trigger-delay', + action='store', default=5, type=int, + help='How long (seconds) to wait between ICMPv6 echo requests') parser['scan'].set_defaults(func=cdist.scan.commandline.commandline) for p in parser: diff --git a/cdist/scan/commandline.py b/cdist/scan/commandline.py index 0d7fb0ca..dead5292 100644 --- a/cdist/scan/commandline.py +++ b/cdist/scan/commandline.py @@ -24,26 +24,29 @@ import sys log = logging.getLogger("scan") - -# define this outside of the class to not handle scapy import errors by default +# CLI processing is defined outside of the main scan class to handle +# non-available optional scapy dependency (instead of crashing mid-flight). def commandline(args): log.debug(args) + # Check if we have the optional scapy dependency available. try: import cdist.scan.scan as scan except ModuleNotFoundError: - print('cdist scan requires scapy to be installed! Exiting.', - file=sys.stderr) + log.error('cdist scan requires scapy to be installed. Exiting.') sys.exit(1) - processes = [] - + # Default operation mode. if not args.mode: - # By default scan and trigger, but do not call any action + # By default scan and trigger, but do not call any action. args.mode = ['scan', 'trigger', ] + # We run each component in a separate process since they + # must not block on each other. + processes = [] + if 'trigger' in args.mode: - t = scan.Trigger(interfaces=args.interfaces) + t = scan.Trigger(interfaces=args.interfaces, sleeptime=args.trigger_delay) t.start() processes.append(t) log.debug("Trigger started") diff --git a/cdist/scan/scan.py b/cdist/scan/scan.py index faee8a56..633a5c06 100644 --- a/cdist/scan/scan.py +++ b/cdist/scan/scan.py @@ -61,20 +61,22 @@ import datetime import cdist.config +logging.basicConfig(level=logging.DEBUG) log = logging.getLogger("scan") - class Trigger(object): """ Trigger an ICMPv6EchoReply from all hosts that are alive """ - def __init__(self, interfaces=None, verbose=False): + def __init__(self, interfaces, sleeptime, verbose=False): self.interfaces = interfaces + + # Used by scapy / send in trigger/2. self.verbose = verbose - # Wait 5 seconds before triggering again - FIXME: add parameter - self.sleeptime = 5 + # Delay in seconds between sent ICMPv6EchoRequests. + self.sleeptime = sleeptime def start(self): self.processes = [] @@ -93,9 +95,12 @@ class Trigger(object): time.sleep(self.sleeptime) def trigger(self, interface): - packet = IPv6(dst="ff02::1{}".format(interface)) / ICMPv6EchoRequest() - log.debug("Sending request on %s", interface) - send(packet, verbose=self.verbose) + try: + log.debug("Sending ICMPv6EchoRequest on %s", interface) + packet = IPv6(dst="ff02::1%{}".format(interface)) / ICMPv6EchoRequest() + send(packet, verbose=self.verbose) + except Exception as e: + log.error( "Could not send ICMPv6EchoRequest: %s", e) class Scanner(object): @@ -103,7 +108,7 @@ class Scanner(object): Scan for replies of hosts, maintain the up-to-date database """ - def __init__(self, interfaces=None, args=None, outdir=None): + def __init__(self, interfaces, args=None, outdir=None): self.interfaces = interfaces if outdir: @@ -148,47 +153,9 @@ class Scanner(object): def scan(self): log.debug("Scanning - zzzzz") - sniff(iface=self.interfaces, - filter="icmp6", - prn=self.handle_pkg) - - -if __name__ == '__main__': - t = Trigger(interfaces=["wlan0"]) - t.start() - - # Scanner can listen on many interfaces at the same time - s = Scanner(interfaces=["wlan0"]) - s.scan() - - # Join back the trigger processes - t.join() - - # Test in my lan shows: - # [18:48] bridge:cdist% ls -1d fe80::* - # fe80::142d:f0a5:725b:1103 - # fe80::20d:b9ff:fe49:ac11 - # fe80::20d:b9ff:fe4c:547d - # fe80::219:d2ff:feb2:2e12 - # fe80::21b:fcff:feee:f446 - # fe80::21b:fcff:feee:f45c - # fe80::21b:fcff:feee:f4b1 - # fe80::21b:fcff:feee:f4ba - # fe80::21b:fcff:feee:f4bc - # fe80::21b:fcff:feee:f4c1 - # fe80::21d:72ff:fe86:46b - # fe80::42b0:34ff:fe6f:f6f0 - # fe80::42b0:34ff:fe6f:f863 - # fe80::42b0:34ff:fe6f:f9b2 - # fe80::4a5d:60ff:fea1:e55f - # fe80::77a3:5e3f:82cc:f2e5 - # fe80::9e93:4eff:fe6c:c1f4 - # fe80::ba69:f4ff:fec5:6041 - # fe80::ba69:f4ff:fec5:8db7 - # fe80::bad8:12ff:fe65:313d - # fe80::bad8:12ff:fe65:d9b1 - # fe80::ce2d:e0ff:fed4:2611 - # fe80::ce32:e5ff:fe79:7ea7 - # fe80::d66d:6dff:fe33:e00 - # fe80::e2ff:f7ff:fe00:20e6 - # fe80::f29f:c2ff:fe7c:275e + try: + sniff(iface=self.interfaces, + filter="icmp6", + prn=self.handle_pkg) + except Exception as e: + log.error( "Could not start listener: %s", e)