[scanner] add to beta commands
This commit is contained in:
parent
91d99bf08a
commit
09dfcfe81e
4 changed files with 124 additions and 6 deletions
|
@ -8,10 +8,11 @@ import cdist.configuration
|
||||||
import cdist.log
|
import cdist.log
|
||||||
import cdist.preos
|
import cdist.preos
|
||||||
import cdist.info
|
import cdist.info
|
||||||
|
import cdist.scan.commandline
|
||||||
|
|
||||||
|
|
||||||
# set of beta sub-commands
|
# set of beta sub-commands
|
||||||
BETA_COMMANDS = set(('install', 'inventory', ))
|
BETA_COMMANDS = set(('install', 'inventory', 'scan', ))
|
||||||
# set of beta arguments for sub-commands
|
# set of beta arguments for sub-commands
|
||||||
BETA_ARGS = {
|
BETA_ARGS = {
|
||||||
'config': set(('tag', 'all_tagged_hosts', 'use_archiving', )),
|
'config': set(('tag', 'all_tagged_hosts', 'use_archiving', )),
|
||||||
|
@ -470,6 +471,35 @@ def get_parsers():
|
||||||
'pattern', nargs='?', help='Glob pattern.')
|
'pattern', nargs='?', help='Glob pattern.')
|
||||||
parser['info'].set_defaults(func=cdist.info.Info.commandline)
|
parser['info'].set_defaults(func=cdist.info.Info.commandline)
|
||||||
|
|
||||||
|
# Scan = config + further
|
||||||
|
parser['scan'] = parser['sub'].add_parser('scan', add_help=False,
|
||||||
|
parents=[parser['config']])
|
||||||
|
|
||||||
|
parser['scan'] = parser['sub'].add_parser(
|
||||||
|
'scan', parents=[parser['loglevel'],
|
||||||
|
parser['beta'],
|
||||||
|
parser['colored_output'],
|
||||||
|
parser['common'],
|
||||||
|
parser['config_main']])
|
||||||
|
|
||||||
|
parser['scan'].add_argument(
|
||||||
|
'-m', '--mode', help='Which modes should run',
|
||||||
|
action='append', default=[],
|
||||||
|
choices=['scan', 'trigger'])
|
||||||
|
parser['scan'].add_argument(
|
||||||
|
'--config',
|
||||||
|
action='store_true',
|
||||||
|
help='Try to configure detected hosts')
|
||||||
|
parser['scan'].add_argument(
|
||||||
|
'-I', '--interfaces',
|
||||||
|
action='append', default=[],
|
||||||
|
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')
|
||||||
|
parser['scan'].set_defaults(func=cdist.scan.commandline.commandline)
|
||||||
|
|
||||||
for p in parser:
|
for p in parser:
|
||||||
parser[p].epilog = EPILOG
|
parser[p].epilog = EPILOG
|
||||||
|
|
||||||
|
|
55
cdist/scan/commandline.py
Normal file
55
cdist/scan/commandline.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# 2020 Nico Schottelius (nico-cdist at schottelius.org)
|
||||||
|
#
|
||||||
|
# This file is part of cdist.
|
||||||
|
#
|
||||||
|
# cdist 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, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# cdist is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
log = logging.getLogger("scan")
|
||||||
|
|
||||||
|
|
||||||
|
# define this outside of the class to not handle scapy import errors by default
|
||||||
|
def commandline(args):
|
||||||
|
log.debug(args)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import cdist.scan.scan as scan
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
print('cdist scan requires scapy to be installed')
|
||||||
|
|
||||||
|
processes = []
|
||||||
|
|
||||||
|
if not args.mode:
|
||||||
|
# By default scan and trigger, but do not call any action
|
||||||
|
args.mode = ['scan', 'trigger' ]
|
||||||
|
|
||||||
|
if 'trigger' in args.mode:
|
||||||
|
t = scan.Trigger(interfaces=args.interfaces)
|
||||||
|
t.start()
|
||||||
|
processes.append(t)
|
||||||
|
log.debug("Trigger started")
|
||||||
|
|
||||||
|
if 'scan' in args.mode:
|
||||||
|
s = scan.Scanner(interfaces=args.interfaces, args=args)
|
||||||
|
s.start()
|
||||||
|
processes.append(s)
|
||||||
|
log.debug("Scanner started")
|
||||||
|
|
||||||
|
for process in processes:
|
||||||
|
process.join()
|
|
@ -50,13 +50,14 @@
|
||||||
|
|
||||||
from multiprocessing import Process
|
from multiprocessing import Process
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
# FIXME: fail gracefully if non existent - i.e. "scapy required for scanner - please install python3-scapy"
|
|
||||||
from scapy.all import *
|
from scapy.all import *
|
||||||
|
|
||||||
# Datetime overwrites scapy.all.datetime - needs to be imported AFTER
|
# Datetime overwrites scapy.all.datetime - needs to be imported AFTER
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
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
|
||||||
|
@ -87,6 +88,7 @@ class Trigger(object):
|
||||||
|
|
||||||
def trigger(self, interface):
|
def trigger(self, interface):
|
||||||
packet = IPv6(dst=f"ff02::1%{interface}") / ICMPv6EchoRequest()
|
packet = IPv6(dst=f"ff02::1%{interface}") / ICMPv6EchoRequest()
|
||||||
|
log.debug(f"Sending request on {interface}")
|
||||||
send(packet, verbose=self.verbose)
|
send(packet, verbose=self.verbose)
|
||||||
|
|
||||||
class Scanner(object):
|
class Scanner(object):
|
||||||
|
@ -94,18 +96,18 @@ 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, outdir=None):
|
def __init__(self, interfaces=None, args=None, outdir=None):
|
||||||
self.interfaces = interfaces
|
self.interfaces = interfaces
|
||||||
|
|
||||||
if outdir:
|
if outdir:
|
||||||
self.outdir = outdir
|
self.outdir = outdir
|
||||||
else:
|
else:
|
||||||
self.outdir = "."
|
self.outdir = os.path.join(os.environ['HOME'], '.cdist', 'scan')
|
||||||
|
|
||||||
def handle_pkg(self, pkg):
|
def handle_pkg(self, pkg):
|
||||||
if ICMPv6EchoReply in pkg:
|
if ICMPv6EchoReply in pkg:
|
||||||
host = pkg['IPv6'].src
|
host = pkg['IPv6'].src
|
||||||
print(f"Host {host} is alive")
|
log.verbose(f"Host {host} is alive")
|
||||||
|
|
||||||
dir = os.path.join(self.outdir, host)
|
dir = os.path.join(self.outdir, host)
|
||||||
fname = os.path.join(dir, "last_seen")
|
fname = os.path.join(dir, "last_seen")
|
||||||
|
@ -118,13 +120,21 @@ class Scanner(object):
|
||||||
with open(fname, "w") as fd:
|
with open(fname, "w") as fd:
|
||||||
fd.write(f"{now}\n")
|
fd.write(f"{now}\n")
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.process = Process(target=self.scan)
|
||||||
|
self.process.start()
|
||||||
|
|
||||||
|
def join(self):
|
||||||
|
self.process.join()
|
||||||
|
|
||||||
def scan(self):
|
def scan(self):
|
||||||
|
log.debug("Scanning - zzzzz")
|
||||||
sniff(iface=self.interfaces,
|
sniff(iface=self.interfaces,
|
||||||
filter="icmp6",
|
filter="icmp6",
|
||||||
prn=self.handle_pkg)
|
prn=self.handle_pkg)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
t = Trigger(interfaces=["wlan0"])
|
t = Trigger(interfaces=["wlan0"])
|
||||||
t.start()
|
t.start()
|
|
@ -32,3 +32,26 @@
|
||||||
- Record when configured (successfully)
|
- Record when configured (successfully)
|
||||||
- Record when seen
|
- Record when seen
|
||||||
- Enables configurations in stateless environments
|
- Enables configurations in stateless environments
|
||||||
|
** Sample output v2020-10-29
|
||||||
|
23:14] bridge:~% sudo cdist scan -b -I wlan0 -vv
|
||||||
|
VERBOSE: cdist: version 6.8.0-36-g91d99bf0
|
||||||
|
VERBOSE: scan: Host fe80::21d:72ff:fe86:46b is alive
|
||||||
|
VERBOSE: scan: Host fe80::ce2d:e0ff:fed4:2611 is alive
|
||||||
|
VERBOSE: scan: Host fe80::21b:fcff:feee:f4c1 is alive
|
||||||
|
VERBOSE: scan: Host fe80::e2ff:f7ff:fe00:20e6 is alive
|
||||||
|
VERBOSE: scan: Host fe80::20d:b9ff:fe49:ac11 is alive
|
||||||
|
VERBOSE: scan: Host fe80::9e93:4eff:fe6c:c1f4 is alive
|
||||||
|
VERBOSE: scan: Host fe80::ce32:e5ff:fe79:7ea7 is alive
|
||||||
|
VERBOSE: scan: Host fe80::219:d2ff:feb2:2e12 is alive
|
||||||
|
VERBOSE: scan: Host fe80::d66d:6dff:fe33:e00 is alive
|
||||||
|
VERBOSE: scan: Host fe80::21b:fcff:feee:f446 is alive
|
||||||
|
VERBOSE: scan: Host fe80::21b:fcff:feee:f4b1 is alive
|
||||||
|
VERBOSE: scan: Host fe80::20d:b9ff:fe4c:547d is alive
|
||||||
|
VERBOSE: scan: Host fe80::bad8:12ff:fe65:313d is alive
|
||||||
|
VERBOSE: scan: Host fe80::42b0:34ff:fe6f:f6f0 is alive
|
||||||
|
VERBOSE: scan: Host fe80::ba69:f4ff:fec5:6041 is alive
|
||||||
|
VERBOSE: scan: Host fe80::f29f:c2ff:fe7c:275e is alive
|
||||||
|
VERBOSE: scan: Host fe80::ba69:f4ff:fec5:8db7 is alive
|
||||||
|
VERBOSE: scan: Host fe80::42b0:34ff:fe6f:f863 is alive
|
||||||
|
VERBOSE: scan: Host fe80::21b:fcff:feee:f4bc is alive
|
||||||
|
...
|
||||||
|
|
Loading…
Reference in a new issue