[scanner] add minimal error handling, consolidate CLI args processing

This commit is contained in:
fnux 2021-04-22 09:29:53 +02:00
parent acf9bf91f1
commit a4464209b6
No known key found for this signature in database
GPG key ID: 4502C902C00A1E12
3 changed files with 37 additions and 63 deletions

View file

@ -492,12 +492,16 @@ def get_parsers():
help='Try to configure detected hosts') help='Try to configure detected hosts')
parser['scan'].add_argument( parser['scan'].add_argument(
'-I', '--interfaces', '-I', '--interfaces',
action='append', default=[], action='append', default=[], required=True,
help='On which interfaces to scan/trigger') help='On which interfaces to scan/trigger')
parser['scan'].add_argument( parser['scan'].add_argument(
'-d', '--delay', '-d', '--delay',
action='store', default=3600, action='store', default=3600, type=int,
help='How long to wait before reconfiguring after last try') 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) parser['scan'].set_defaults(func=cdist.scan.commandline.commandline)
for p in parser: for p in parser:

View file

@ -24,26 +24,29 @@ import sys
log = logging.getLogger("scan") log = logging.getLogger("scan")
# CLI processing is defined outside of the main scan class to handle
# define this outside of the class to not handle scapy import errors by default # non-available optional scapy dependency (instead of crashing mid-flight).
def commandline(args): def commandline(args):
log.debug(args) log.debug(args)
# Check if we have the optional scapy dependency available.
try: try:
import cdist.scan.scan as scan import cdist.scan.scan as scan
except ModuleNotFoundError: except ModuleNotFoundError:
print('cdist scan requires scapy to be installed! Exiting.', log.error('cdist scan requires scapy to be installed. Exiting.')
file=sys.stderr)
sys.exit(1) sys.exit(1)
processes = [] # Default operation mode.
if not args.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', ] 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: if 'trigger' in args.mode:
t = scan.Trigger(interfaces=args.interfaces) t = scan.Trigger(interfaces=args.interfaces, sleeptime=args.trigger_delay)
t.start() t.start()
processes.append(t) processes.append(t)
log.debug("Trigger started") log.debug("Trigger started")

View file

@ -61,20 +61,22 @@ import datetime
import cdist.config import cdist.config
logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger("scan") log = logging.getLogger("scan")
class Trigger(object): class Trigger(object):
""" """
Trigger an ICMPv6EchoReply from all hosts that are alive 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 self.interfaces = interfaces
# Used by scapy / send in trigger/2.
self.verbose = verbose self.verbose = verbose
# Wait 5 seconds before triggering again - FIXME: add parameter # Delay in seconds between sent ICMPv6EchoRequests.
self.sleeptime = 5 self.sleeptime = sleeptime
def start(self): def start(self):
self.processes = [] self.processes = []
@ -93,9 +95,12 @@ class Trigger(object):
time.sleep(self.sleeptime) time.sleep(self.sleeptime)
def trigger(self, interface): def trigger(self, interface):
packet = IPv6(dst="ff02::1{}".format(interface)) / ICMPv6EchoRequest() try:
log.debug("Sending request on %s", interface) log.debug("Sending ICMPv6EchoRequest on %s", interface)
send(packet, verbose=self.verbose) 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): class Scanner(object):
@ -103,7 +108,7 @@ class Scanner(object):
Scan for replies of hosts, maintain the up-to-date database 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 self.interfaces = interfaces
if outdir: if outdir:
@ -148,47 +153,9 @@ class Scanner(object):
def scan(self): def scan(self):
log.debug("Scanning - zzzzz") log.debug("Scanning - zzzzz")
sniff(iface=self.interfaces, try:
filter="icmp6", sniff(iface=self.interfaces,
prn=self.handle_pkg) filter="icmp6",
prn=self.handle_pkg)
except Exception as e:
if __name__ == '__main__': log.error( "Could not start listener: %s", e)
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