Example #1
0
    def _create(self):
        """ create DySKT and member processes """
        # read in and validate the conf file
        self._readconf()

        # intialize shared objects
        self._halt = mp.Event()  # our stop event
        self._ic = mp.Queue()  # comms for children
        self._pConns = {}  # dict of connections to children

        # initialize children
        # Each child is expected to initialize in the _init_ function and throw
        # a RuntimeError failure
        logging.info("Initializing subprocess...")
        try:
            # start RTO first
            logging.info("Starting RTO")
            (conn1, conn2) = mp.Pipe()
            self._pConns['rto'] = conn1
            self._rto = RTO(self._ic, conn2, self._conf)

            # set the region? if so, do it prior to starting the RadioController
            rd = self._conf['local']['region']
            if rd:
                logging.info("Setting regulatory domain to %s", rd)
                self._rd = iw.regget()
                iw.regset(rd)
                if iw.regget() != rd:
                    logging.warn("Regulatory domain %s may not have been set",
                                 rd)
                else:
                    logging.info("Regulatory domain set to %s", rd)

            # recon radio is mandatory
            logging.info("Starting Reconnaissance Radio")
            (conn1, conn2) = mp.Pipe()
            self._pConns['recon'] = conn1
            self._rr = RadioController(self._ic, conn2, self._conf['recon'])

            # collection if present
            if self._conf['collection']:
                try:
                    logging.info("Starting Collection Radio")
                    (conn1, conn2) = mp.Pipe()
                    self._pConns['collection'] = conn1
                    self._cr = RadioController(self._ic, conn2,
                                               self._conf['collection'])
                except RuntimeError as e:
                    # continue without collector, but log it
                    logging.warning(
                        "Collection Radio (%s), continuing without", e)
        except RuntimeError as e:
            # e should have the form "Major:Minor:Description"
            ms = e.message.split(':')
            logging.error("%s (%s) %s", ms[0], ms[1], ms[2])
            self._state = DYSKT_INVALID
        except Exception as e:
            # catchall
            logging.error(e)
            self._state = DYSKT_INVALID
        else:
            # start children execution
            self._state = DYSKT_CREATED
            self._rr.start()
            if self._cr: self._cr.start()
            self._rto.start()
Example #2
0
class DySKT(object):
    """ DySKT - primary process of the Wraith sensor """
    def __init__(self, conf=None):
        """ initialize variables """
        # get parameters
        self._cpath = conf if conf else os.path.join(GPATH, 'dyskt.conf')

        # internal variables
        self._state = DYSKT_INVALID  # current state
        self._conf = {}  # dyskt configuration dict
        self._halt = None  # the stop event
        self._pConns = None  # token pipes for children
        self._ic = None  # internal comms queue
        self._rto = None  # data collation/forwarding
        self._rr = None  # recon radio
        self._cr = None  # collection radio
        self._rd = None  # regulatory domain

    def _create(self):
        """ create DySKT and member processes """
        # read in and validate the conf file
        self._readconf()

        # intialize shared objects
        self._halt = mp.Event()  # our stop event
        self._ic = mp.Queue()  # comms for children
        self._pConns = {}  # dict of connections to children

        # initialize children
        # Each child is expected to initialize in the _init_ function and throw
        # a RuntimeError failure
        logging.info("Initializing subprocess...")
        try:
            # start RTO first
            logging.info("Starting RTO")
            (conn1, conn2) = mp.Pipe()
            self._pConns['rto'] = conn1
            self._rto = RTO(self._ic, conn2, self._conf)

            # set the region? if so, do it prior to starting the RadioController
            rd = self._conf['local']['region']
            if rd:
                logging.info("Setting regulatory domain to %s", rd)
                self._rd = iw.regget()
                iw.regset(rd)
                if iw.regget() != rd:
                    logging.warn("Regulatory domain %s may not have been set",
                                 rd)
                else:
                    logging.info("Regulatory domain set to %s", rd)

            # recon radio is mandatory
            logging.info("Starting Reconnaissance Radio")
            (conn1, conn2) = mp.Pipe()
            self._pConns['recon'] = conn1
            self._rr = RadioController(self._ic, conn2, self._conf['recon'])

            # collection if present
            if self._conf['collection']:
                try:
                    logging.info("Starting Collection Radio")
                    (conn1, conn2) = mp.Pipe()
                    self._pConns['collection'] = conn1
                    self._cr = RadioController(self._ic, conn2,
                                               self._conf['collection'])
                except RuntimeError as e:
                    # continue without collector, but log it
                    logging.warning(
                        "Collection Radio (%s), continuing without", e)
        except RuntimeError as e:
            # e should have the form "Major:Minor:Description"
            ms = e.message.split(':')
            logging.error("%s (%s) %s", ms[0], ms[1], ms[2])
            self._state = DYSKT_INVALID
        except Exception as e:
            # catchall
            logging.error(e)
            self._state = DYSKT_INVALID
        else:
            # start children execution
            self._state = DYSKT_CREATED
            self._rr.start()
            if self._cr: self._cr.start()
            self._rto.start()

    def _destroy(self):
        """ destroy DySKT cleanly """
        # change our state
        self._state = DYSKT_EXITING

        # reset regulatory domain if necessary
        if self._rd:
            try:
                logging.info("Resetting regulatory domain")
                iw.regset(self._rd)
                if iw.regget() != self._rd: raise RuntimeError
            except:
                logging.warn("Failed to reset regulatory domain")

        # halt main execution loop & send out poison pills
        # put a token on the internal comms from us to break the RTO out of
        # any holding for data block
        logging.info("Stopping Sub-processes")
        self._halt.set()
        self._ic.put(('dyskt', time.time(), '!CHECK!', []))
        for key in self._pConns:
            try:
                self._pConns[key].send('!STOP!')
            except IOError:
                # ignore any broken pipe errors
                pass
            self._pConns[key].close()
        while mp.active_children():
            time.sleep(0.5)

        # change our state
        self._state = DYSKT_DESTROYED

    @property
    def state(self):
        return self._state

    def start(self):
        """ start execution """
        # setup signal handlers for pause(s),resume(s),stop
        signal.signal(signal.SIGINT, self.stop)  # CTRL-C and kill -INT stop
        signal.signal(signal.SIGTERM, self.stop)  # kill -TERM stop

        # initialize, quit on failure
        logging.info("**** Starting DySKT %s ****" % dyskt.__version__)
        self._create()
        if self.state == DYSKT_INVALID:
            # make sure we do not leave system in corrupt state (i.e. no wireless nics)
            self._destroy()
            raise DySKTRuntimeException(
                "DySKT failed to initialize, shutting down")

        # set state to running
        self._state = DYSKT_RUNNING

        # execution loop
        while not self._halt.is_set():
            # get message a tuple: (level,originator,type,message)
            for key in self._pConns:
                try:
                    if self._pConns[key].poll():
                        (l, o, t, m) = self._pConns[key].recv()
                        if l == "err":
                            # only process errors involved during execution
                            if DYSKT_CREATED < self.state < DYSKT_EXITING:
                                if o == 'collection':
                                    # allow collection radio to fail and still continue
                                    logging.warning(
                                        "Collection radio dropped. Continuing..."
                                    )
                                    self._pConns['collection'].send('!STOP!')
                                    self._pConns['collection'].close()
                                    del self._pConns['collection']
                                    mp.active_children()
                                else:
                                    logging.error("%s failed. (%s) %s", o, t,
                                                  m)
                                    self.stop()
                        elif l == "warn":
                            logging.warning("%s: (%s) %s", o, t, m)
                        elif l == "info":
                            logging.info("%s: (%s) %s", o, t, m)
                except Exception as e:
                    # blanke exception
                    logging.error("DySKT failed. (Unknown) %s", e)
            time.sleep(1)

    # noinspection PyUnusedLocal
    def stop(self, signum=None, stack=None):
        """ stop execution """
        if DYSKT_RUNNING <= self.state < DYSKT_EXITING:
            logging.info("**** Stopping DySKT ****")
            self._destroy()

    def _readconf(self):
        """ read in config file at cpath """
        logging.info("Reading configuration file...")
        conf = ConfigParser.RawConfigParser()
        if not conf.read(self._cpath):
            raise DySKTConfException('%s is invalid' % self._cpath)

        # intialize conf to empty dict
        self._conf = {}

        try:
            # read in the recon radio configuration
            self._conf['recon'] = self._readradio(conf, 'Recon')
            try:
                # catch any collection exceptions and log a warning
                if conf.has_section('Collection'):
                    self._conf['collection'] = self._readradio(
                        conf, 'Collection')
                else:
                    self._conf['collection'] = None
                    logging.info("No collection radio specified")
            except (ConfigParser.NoSectionError, ConfigParser.NoOptionError,
                    RuntimeError, ValueError):
                logging.warning(
                    "Invalid collection specification. Continuing without...")

            # GPS section
            self._conf['gps'] = {}
            self._conf['gps']['fixed'] = conf.getboolean('GPS', 'fixed')
            if self._conf['gps']['fixed']:
                self._conf['gps']['lat'] = conf.getfloat('GPS', 'lat')
                self._conf['gps']['lon'] = conf.getfloat('GPS', 'lon')
                self._conf['gps']['alt'] = conf.getfloat('GPS', 'alt')
                self._conf['gps']['dir'] = conf.getfloat('GPS', 'heading')
            else:
                self._conf['gps']['port'] = conf.getint('GPS', 'port')
                self._conf['gps']['id'] = conf.get('GPS', 'devid')
                self._conf['gps']['poll'] = conf.getfloat('GPS', 'poll')
                self._conf['gps']['epx'] = conf.getfloat('GPS', 'epx')
                self._conf['gps']['epy'] = conf.getfloat('GPS', 'epy')

            # Storage section
            self._conf['store'] = {
                'host': conf.get('Storage', 'host'),
                'port': conf.getint('Storage', 'port')
            }

            # Local section
            self._conf['local'] = {'region': None, 'c2c': None}
            if conf.has_option('Lcoal', 'C2C'):
                self._conf['local']['c2c'] = conf.getint('Local', 'C2C')
            if conf.has_option('Local', 'region'):
                reg = conf.get('Local', 'region')
                if len(reg) != 2:
                    logging.warn("Regulatory domain %s is invalid" % reg)
                else:
                    self._conf['local']['region'] = conf.get('Local', 'region')
        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e:
            raise DySKTConfException("%s" % e)
        except (RuntimeError, ValueError) as e:
            raise DySKTConfException("%s" % e)

    def _readradio(self, conf, rtype='Recon'):
        """ read in the rtype radio configuration from conf and return parsed dict """
        # don't bother if specified radio is not present
        if not conf.get(rtype, 'nic') in wifaces():
            raise RuntimeError("Radio %s not present/not wireless" %
                               conf.get(rtype, 'nic'))

        # get nic and set role setting default antenna config
        r = {
            'nic': conf.get(rtype, 'nic'),
            'spoofed': None,
            'ant_gain': 0.0,
            'ant_loss': 0.0,
            'ant_offset': 0.0,
            'ant_type': 0.0,
            'desc': "unknown",
            'scan_start': None,
            'role': rtype.lower(),
            'antennas': {}
        }

        # get optional properties
        if conf.has_option(rtype, 'spoof'):
            r['spoofed'] = conf.get(rtype, 'spoof')
        if conf.has_option(rtype, 'desc'): r['desc'] = conf.get(rtype, 'desc')

        # process antennas - get the number first
        try:
            nA = conf.getint(rtype, 'antennas') if conf.has_option(
                rtype, 'antennas') else 0
        except ValueError:
            nA = 0

        if nA:
            # antennas has been specified, force correct/valid specifications
            try:
                gain = map(float, conf.get(rtype, 'antenna_gain').split(','))
                if len(gain) != nA: raise RuntimeError('gain')
                atype = conf.get(rtype, 'antenna_type').split(',')
                if len(atype) != nA: raise RuntimeError('type')
                loss = map(float, conf.get(rtype, 'antenna_loss').split(','))
                if len(loss) != nA: raise RuntimeError('loss')
                rs = conf.get(rtype, 'antenna_xyz').split(',')
                xyz = []
                for t in rs:
                    xyz.append(tuple(map(int, t.split(':'))))
                if len(xyz) != nA: raise RuntimeError('xyz')
            except ConfigParser.NoOptionError as e:
                logging.warn("Antenna %s not specified" % e)
                #raise DySKTConfException("%s" % e)
            except ValueError as e:
                logging.warn("Invalid type for %s antenna configuration - %s")
            except RuntimeError as e:
                logging.warn(
                    "Antenna %s has invalid number of specifications" % e)
            else:
                r['antennas']['num'] = nA
                r['antennas']['gain'] = gain
                r['antennas']['type'] = atype
                r['antennas']['loss'] = loss
                r['antennas']['xyz'] = xyz
        else:
            # none, set all empty
            r['antennas']['num'] = 0
            r['antennas']['gain'] = []
            r['antennas']['type'] = []
            r['antennas']['loss'] = []
            r['antennas']['xyz'] = []

        # get scan pattern
        r['dwell'] = conf.getfloat(rtype, 'dwell')
        if r['dwell'] <= 0: raise ValueError("dwell must be > 0")
        r['scan'] = parsechlist(conf.get(rtype, 'scan'), 'scan')
        r['pass'] = parsechlist(conf.get(rtype, 'pass'), 'pass')
        if conf.has_option(rtype, 'scan_start'):
            try:
                scanspec = conf.get(rtype, 'scan_start')
                if ':' in scanspec:
                    (ch, chw) = scanspec.split(':')
                else:
                    ch = scanspec
                    chw = None
                ch = int(ch) if ch else r['scan'][0][0]
                if not chw in iw.IW_CHWS: chw = r['scan'][0][1]
                r['scan_start'] = (ch,
                                   chw) if (ch,
                                            chw) in r['scan'] else r['scan'][0]
            except ValueError:
                # use default
                r['scan_start'] = r['scan'][0]
        else:
            r['scan_start'] = r['scan'][0]

        return r
Example #3
0
    def _create(self):
        """ create DySKT and member processes """
        # read in and validate the conf file 
        self._readconf()

        # intialize shared objects
        self._halt = mp.Event() # our stop event
        self._ic = mp.Queue()   # comms for children
        self._pConns = {}       # dict of connections to children

        # initialize children
        # Each child is expected to initialize in the _init_ function and throw
        # a RuntimeError failure
        logging.info("Initializing subprocess...")
        try:
            # start RTO first
            logging.info("Starting RTO")
            (conn1,conn2) = mp.Pipe()
            self._pConns['rto'] = conn1
            self._rto = RTO(self._ic,conn2,self._conf)

            # set the region? if so, do it prior to starting the RadioController
            rd = self._conf['local']['region']
            if rd:
                logging.info("Setting regulatory domain to %s",rd)
                self._rd = iw.regget()
                iw.regset(rd)
                if iw.regget() != rd:
                    logging.warn("Regulatory domain %s may not have been set",rd)
                else:
                    logging.info("Regulatory domain set to %s",rd)

            # recon radio is mandatory
            logging.info("Starting Reconnaissance Radio")
            (conn1,conn2) = mp.Pipe()
            self._pConns['recon'] = conn1
            self._rr = RadioController(self._ic,conn2,self._conf['recon'])

            # collection if present
            if self._conf['collection']:
                try:
                    logging.info("Starting Collection Radio")
                    (conn1,conn2) = mp.Pipe()
                    self._pConns['collection'] = conn1
                    self._cr = RadioController(self._ic,conn2,self._conf['collection'])
                except RuntimeError as e:
                    # continue without collector, but log it
                    logging.warning("Collection Radio (%s), continuing without",e)
        except RuntimeError as e:
            # e should have the form "Major:Minor:Description"
            ms = e.message.split(':')
            logging.error("%s (%s) %s",ms[0],ms[1],ms[2])
            self._state = DYSKT_INVALID
        except Exception as e:
            # catchall
            logging.error(e)
            self._state = DYSKT_INVALID
        else:
            # start children execution
            self._state = DYSKT_CREATED
            self._rr.start()
            if self._cr: self._cr.start()
            self._rto.start()
Example #4
0
class DySKT(object):
    """ DySKT - primary process of the Wraith sensor """
    def __init__(self,conf=None):
        """ initialize variables """
        # get parameters
        self._cpath = conf if conf else os.path.join(GPATH,'dyskt.conf')
        
        # internal variables
        self._state = DYSKT_INVALID # current state
        self._conf = {}             # dyskt configuration dict
        self._halt = None           # the stop event
        self._pConns = None         # token pipes for children
        self._ic = None             # internal comms queue
        self._rto = None            # data collation/forwarding
        self._rr = None             # recon radio
        self._cr = None             # collection radio
        self._rd = None             # regulatory domain
    
    def _create(self):
        """ create DySKT and member processes """
        # read in and validate the conf file 
        self._readconf()

        # intialize shared objects
        self._halt = mp.Event() # our stop event
        self._ic = mp.Queue()   # comms for children
        self._pConns = {}       # dict of connections to children

        # initialize children
        # Each child is expected to initialize in the _init_ function and throw
        # a RuntimeError failure
        logging.info("Initializing subprocess...")
        try:
            # start RTO first
            logging.info("Starting RTO")
            (conn1,conn2) = mp.Pipe()
            self._pConns['rto'] = conn1
            self._rto = RTO(self._ic,conn2,self._conf)

            # set the region? if so, do it prior to starting the RadioController
            rd = self._conf['local']['region']
            if rd:
                logging.info("Setting regulatory domain to %s",rd)
                self._rd = iw.regget()
                iw.regset(rd)
                if iw.regget() != rd:
                    logging.warn("Regulatory domain %s may not have been set",rd)
                else:
                    logging.info("Regulatory domain set to %s",rd)

            # recon radio is mandatory
            logging.info("Starting Reconnaissance Radio")
            (conn1,conn2) = mp.Pipe()
            self._pConns['recon'] = conn1
            self._rr = RadioController(self._ic,conn2,self._conf['recon'])

            # collection if present
            if self._conf['collection']:
                try:
                    logging.info("Starting Collection Radio")
                    (conn1,conn2) = mp.Pipe()
                    self._pConns['collection'] = conn1
                    self._cr = RadioController(self._ic,conn2,self._conf['collection'])
                except RuntimeError as e:
                    # continue without collector, but log it
                    logging.warning("Collection Radio (%s), continuing without",e)
        except RuntimeError as e:
            # e should have the form "Major:Minor:Description"
            ms = e.message.split(':')
            logging.error("%s (%s) %s",ms[0],ms[1],ms[2])
            self._state = DYSKT_INVALID
        except Exception as e:
            # catchall
            logging.error(e)
            self._state = DYSKT_INVALID
        else:
            # start children execution
            self._state = DYSKT_CREATED
            self._rr.start()
            if self._cr: self._cr.start()
            self._rto.start()

    def _destroy(self):
        """ destroy DySKT cleanly """
        # change our state
        self._state = DYSKT_EXITING

        # reset regulatory domain if necessary
        if self._rd:
            try:
                logging.info("Resetting regulatory domain")
                iw.regset(self._rd)
                if iw.regget() != self._rd: raise RuntimeError
            except:
                logging.warn("Failed to reset regulatory domain")

        # halt main execution loop & send out poison pills
        # put a token on the internal comms from us to break the RTO out of
        # any holding for data block
        logging.info("Stopping Sub-processes")
        self._halt.set()
        self._ic.put(('dyskt',time.time(),'!CHECK!',[]))
        for key in self._pConns:
            try:
                self._pConns[key].send('!STOP!')
            except IOError:
                # ignore any broken pipe errors
                pass
            self._pConns[key].close()
        while mp.active_children(): time.sleep(0.5)

        # change our state
        self._state = DYSKT_DESTROYED

    @property
    def state(self): return self._state

    def start(self):
        """ start execution """
        # setup signal handlers for pause(s),resume(s),stop
        signal.signal(signal.SIGINT,self.stop)   # CTRL-C and kill -INT stop
        signal.signal(signal.SIGTERM,self.stop)  # kill -TERM stop

        # initialize, quit on failure
        logging.info("**** Starting DySKT %s ****" % dyskt.__version__)
        self._create()
        if self.state == DYSKT_INVALID:
            # make sure we do not leave system in corrupt state (i.e. no wireless nics)
            self._destroy()
            raise DySKTRuntimeException("DySKT failed to initialize, shutting down")

        # set state to running
        self._state = DYSKT_RUNNING

        # execution loop
        while not self._halt.is_set():
            # get message a tuple: (level,originator,type,message)
            for key in self._pConns:
                try:
                    if self._pConns[key].poll():
                        (l,o,t,m) = self._pConns[key].recv()
                        if l == "err":
                            # only process errors involved during execution
                            if DYSKT_CREATED < self.state < DYSKT_EXITING:
                                if o == 'collection':
                                    # allow collection radio to fail and still continue
                                    logging.warning("Collection radio dropped. Continuing...")
                                    self._pConns['collection'].send('!STOP!')
                                    self._pConns['collection'].close()
                                    del self._pConns['collection']
                                    mp.active_children()
                                else:
                                    logging.error("%s failed. (%s) %s",o,t,m)
                                    self.stop()
                        elif l == "warn": logging.warning("%s: (%s) %s",o,t,m)
                        elif l == "info": logging.info("%s: (%s) %s",o,t,m)
                except Exception as e:
                    # blanke exception
                    logging.error("DySKT failed. (Unknown) %s",e)
            time.sleep(1)

    # noinspection PyUnusedLocal
    def stop(self,signum=None,stack=None):
        """ stop execution """
        if DYSKT_RUNNING <= self.state < DYSKT_EXITING:
            logging.info("**** Stopping DySKT ****")
            self._destroy()

    def _readconf(self):
        """ read in config file at cpath """
        logging.info("Reading configuration file...")
        conf = ConfigParser.RawConfigParser()
        if not conf.read(self._cpath):
            raise DySKTConfException('%s is invalid' % self._cpath)

        # intialize conf to empty dict
        self._conf = {}

        try:
            # read in the recon radio configuration
            self._conf['recon'] = self._readradio(conf,'Recon')
            try:
                # catch any collection exceptions and log a warning
                if conf.has_section('Collection'):
                    self._conf['collection'] = self._readradio(conf,'Collection')
                else:
                    self._conf['collection'] = None
                    logging.info("No collection radio specified")
            except (ConfigParser.NoSectionError,ConfigParser.NoOptionError,
                    RuntimeError,ValueError):
                logging.warning("Invalid collection specification. Continuing without...")

            # GPS section
            self._conf['gps'] = {}
            self._conf['gps']['fixed'] = conf.getboolean('GPS','fixed')
            if self._conf['gps']['fixed']:
                self._conf['gps']['lat'] = conf.getfloat('GPS','lat')
                self._conf['gps']['lon'] = conf.getfloat('GPS','lon')
                self._conf['gps']['alt'] = conf.getfloat('GPS','alt')
                self._conf['gps']['dir'] = conf.getfloat('GPS','heading')
            else:
                self._conf['gps']['port'] = conf.getint('GPS','port')
                self._conf['gps']['id'] = conf.get('GPS','devid')
                self._conf['gps']['poll'] = conf.getfloat('GPS','poll')
                self._conf['gps']['epx'] = conf.getfloat('GPS','epx')
                self._conf['gps']['epy'] = conf.getfloat('GPS','epy')

            # Storage section
            self._conf['store'] = {'host':conf.get('Storage','host'),
                                   'port':conf.getint('Storage','port')}

            # Local section
            self._conf['local'] = {'region':None,'c2c':None}
            if conf.has_option('Lcoal','C2C'):
                self._conf['local']['c2c'] = conf.getint('Local','C2C')
            if conf.has_option('Local','region'):
                reg = conf.get('Local','region')
                if len(reg) != 2:
                    logging.warn("Regulatory domain %s is invalid" % reg)
                else:
                    self._conf['local']['region'] = conf.get('Local','region')
        except (ConfigParser.NoSectionError,ConfigParser.NoOptionError) as e:
            raise DySKTConfException("%s" % e)
        except (RuntimeError,ValueError) as e:
            raise DySKTConfException("%s" % e)

    def _readradio(self,conf,rtype='Recon'):
        """ read in the rtype radio configuration from conf and return parsed dict """
        # don't bother if specified radio is not present
        if not conf.get(rtype,'nic') in wifaces():
            raise RuntimeError("Radio %s not present/not wireless" % conf.get(rtype,'nic'))

        # get nic and set role setting default antenna config
        r = {'nic':conf.get(rtype,'nic'),
             'spoofed':None,
             'ant_gain':0.0,
             'ant_loss':0.0,
             'ant_offset':0.0,
             'ant_type':0.0,
             'desc':"unknown",
             'scan_start':None,
             'role':rtype.lower(),
             'antennas':{}}

        # get optional properties
        if conf.has_option(rtype,'spoof'): r['spoofed'] = conf.get(rtype,'spoof')
        if conf.has_option(rtype,'desc'):  r['desc'] = conf.get(rtype,'desc')

        # process antennas - get the number first
        try:
            nA = conf.getint(rtype,'antennas') if conf.has_option(rtype,'antennas') else 0
        except ValueError:
            nA = 0

        if nA:
            # antennas has been specified, force correct/valid specifications
            try:
                gain = map(float,conf.get(rtype,'antenna_gain').split(','))
                if len(gain) != nA: raise RuntimeError('gain')
                atype = conf.get(rtype,'antenna_type').split(',')
                if len(atype) != nA: raise RuntimeError('type')
                loss = map(float,conf.get(rtype,'antenna_loss').split(','))
                if len(loss) != nA: raise RuntimeError('loss')
                rs = conf.get(rtype,'antenna_xyz').split(',')
                xyz = []
                for t in rs: xyz.append(tuple(map(int,t.split(':'))))
                if len(xyz) != nA: raise RuntimeError('xyz')
            except ConfigParser.NoOptionError as e:
                logging.warn("Antenna %s not specified" % e)
                #raise DySKTConfException("%s" % e)
            except ValueError as e:
                logging.warn("Invalid type for %s antenna configuration - %s")
            except RuntimeError as e:
                logging.warn("Antenna %s has invalid number of specifications" % e)
            else:
                r['antennas']['num'] = nA
                r['antennas']['gain'] = gain
                r['antennas']['type'] = atype
                r['antennas']['loss'] = loss
                r['antennas']['xyz'] = xyz
        else:
            # none, set all empty
            r['antennas']['num'] = 0
            r['antennas']['gain'] = []
            r['antennas']['type'] = []
            r['antennas']['loss'] = []
            r['antennas']['xyz'] = []

        # get scan pattern
        r['dwell'] = conf.getfloat(rtype,'dwell')
        if r['dwell'] <= 0: raise ValueError("dwell must be > 0")
        r['scan'] = parsechlist(conf.get(rtype,'scan'),'scan')
        r['pass'] = parsechlist(conf.get(rtype,'pass'),'pass')
        if conf.has_option(rtype,'scan_start'):
            try:
                scanspec = conf.get(rtype,'scan_start')
                if ':' in scanspec:
                    (ch,chw) = scanspec.split(':')
                else:
                    ch = scanspec
                    chw = None
                ch = int(ch) if ch else r['scan'][0][0]
                if not chw in iw.IW_CHWS: chw = r['scan'][0][1]
                r['scan_start'] = (ch,chw) if (ch,chw) in r['scan'] else r['scan'][0]
            except ValueError:
                # use default
                r['scan_start'] = r['scan'][0]
        else:
            r['scan_start'] = r['scan'][0]

        return r
Example #5
0
    def _create(self):
        """ create DySKT and member processes """
        # read in and validate the conf file
        self._readconf()

        # intialize shared objects
        self._halt = mp.Event() # our stop event
        self._ic = mp.Queue()   # comms for children
        self._pConns = {}       # dict of connections to children

        # initialize children
        # Each child is expected to initialize in the _init_ function and throw
        # a RuntimeError on failure. For non-mandatory components, all exceptions
        # are caught in in inner exceptions, mandatory are caught in the the outer
        # exception and result in shutting down
        logging.info("Initializing subprocess...")
        try:
            # start RTO first (IOT accept comms from the radios)
            logging.info("Starting RTO...")
            (conn1,conn2) = mp.Pipe()
            self._pConns['rto'] = conn1
            self._rto = RTO(self._ic,conn2,self._conf)
            logging.info("RTO started")

            # set the region? if so, do it prior to starting the radio(s)
            rd = self._conf['local']['region']
            if rd:
                logging.info("Setting regulatory domain to %s...",rd)
                self._rd = iw.regget()
                iw.regset(rd)
                if iw.regget() != rd:
                    logging.warning("Regulatory domain %s may not have been set",rd)
                else:
                    logging.info("Regulatory domain set to %s",rd)

            # recon radio is mandatory
            mode = 'paused' if self._conf['recon']['paused'] else 'scan'
            logging.info("Starting Reconnaissance Radio...")
            (conn1,conn2) = mp.Pipe()
            self._pConns['recon'] = conn1
            self._rr = RadioController(self._ic,conn2,self._conf['recon'])
            logging.info("Reconnaissance Radio started in %s mode",mode)

            # collection if present
            if self._conf['collection']:
                try:
                    mode = 'paused' if self._conf['collection']['paused'] else 'scan'
                    logging.info("Starting Collection Radio...")
                    (conn1,conn2) = mp.Pipe()
                    self._pConns['collection'] = conn1
                    self._cr = RadioController(self._ic,conn2,self._conf['collection'])
                except RuntimeError as e:
                    # continue without collector, but log it
                    logging.warning("Collection Radio failed: %s, continuing w/out",e)
                else:
                    logging.info("Collection Radio started in %s mode",mode)

            # c2c socket -> on failure log it and keep going
            logging.info("Starting C2C...")
            try:
                (conn1,conn2) = mp.Pipe()
                self._pConns['c2c'] = conn1
                self._c2c = C2C(conn2,self._conf['local']['c2c'])
                self._c2c.start()
            except Exception as e:
                logging.warn("C2C failed: %s, continuing without" % e)
            else:
                 logging.info("C2C listening on port %d" % self._conf['local']['c2c'])
        except RuntimeError as e:
            # e should have the form "Major:Minor:Description"
            ms = e.message.split(':')
            logging.error("%s (%s) %s",ms[0],ms[1],ms[2])
            self._state = DYSKT_INVALID
        except Exception as e:
            # catchall
            logging.error(e)
            self._state = DYSKT_INVALID
        else:
            # start children execution
            self._state = DYSKT_CREATED
            self._rr.start()
            if self._cr: self._cr.start()
            self._rto.start()
Example #6
0
class DySKT(object):
    """ DySKT - primary process of the Wraith sensor """
    def __init__(self,conf=None):
        """ initialize variables """
        # get parameters
        self._cpath = conf if conf else os.path.join(GPATH,'dyskt.conf')
        
        # internal variables
        self._state = DYSKT_INVALID # current state
        self._conf = {}             # dyskt configuration dict
        self._halt = None           # the stop event
        self._pConns = None         # token pipes for children (& c2c)
        self._ic = None             # internal comms queue
        self._rto = None            # data collation/forwarding
        self._rr = None             # recon radio
        self._cr = None             # collection radio
        self._rd = None             # regulatory domain
        self._c2c = None            # our c2c

    @property
    def state(self): return self._state

    def run(self):
        """ start execution """
        # setup signal handlers for pause(s),resume(s),stop
        signal.signal(signal.SIGINT,self.stop)   # CTRL-C and kill -INT stop
        signal.signal(signal.SIGTERM,self.stop)  # kill -TERM stop

        # initialize, quit on failure
        logging.info("**** Starting DySKT %s ****",dyskt.__version__)
        self._create()
        if self.state == DYSKT_INVALID:
            # make sure we do not leave system in corrupt state (i.e. w/o nics)
            self._destroy()
            raise DySKTRuntimeException("DySKT failed to initialize, shutting down")

        # set state to running
        self._state = DYSKT_RUNNING

        # execution loop
        while not self._halt.is_set():
            # get message(s) a tuple: (level,originator,type,message) from children
            rs = [] # hide pycharm unreferenced variable warning
            try:
                rs,_,_ = select.select(self._pConns.values(),[],[],0.5)
            except select.error as e: # hide (4,'Interupted system call') errors
                if e[0] == 4: continue

            for r in rs:
                try:
                    l,o,t,m = r.recv()
                    if l == 'err':
                        # only process errors invovled during execution
                        if DYSKT_CREATED < self.state < DYSKT_EXITING:
                            # continue w/out collection if it fails
                            if o == 'collection':
                                logging.warning("Collection radio dropped: %s",m)
                                self._pConns['collection'].send('!STOP!')
                                self._pConns['collection'].close()
                                del self._pConns['collection']
                                mp.active_children()
                                self._cr = None
                            else:
                                logging.error("%s failed (%) %s",o,t,m)
                        else:
                            logging.warning("Uninitiated error (%) %s from %s",t,m,o)
                    elif l == 'warn': logging.warning("%s: (%s) %s",o,t,m)
                    elif l == 'info': logging.info("%s: (%s) %s",o,t,m)
                    elif l == 'cmd':
                        cid,cmd,rdos,ps = self._processcmd(m)
                        if cid is None: continue
                        for rdo in rdos:
                            logging.info("Client sent %s to %s",cmd,rdo)
                            self._pConns[rdo].send('%s:%d:%s' % (cmd,cid,'-'.join(ps)))
                    elif l == 'cmderr':
                        self._pConns['c2c'].send("ERR %d \001%s\001\n" % (t,m))
                    elif l == 'cmdack':
                        self._pConns['c2c'].send("OK %d \001%s\001\n" % (t,m))
                except Exception as e:
                    # blanket catch all
                    logging.error("DySKT failed. (Unknown) %s->%s", type(e),e)

    # noinspection PyUnusedLocal
    def stop(self,signum=None,stack=None):
        """ stop execution """
        if DYSKT_RUNNING <= self.state < DYSKT_EXITING:
            logging.info("**** Stopping DySKT ****")
            self._destroy()

    #### PRIVATE HELPERS

    def _create(self):
        """ create DySKT and member processes """
        # read in and validate the conf file
        self._readconf()

        # intialize shared objects
        self._halt = mp.Event() # our stop event
        self._ic = mp.Queue()   # comms for children
        self._pConns = {}       # dict of connections to children

        # initialize children
        # Each child is expected to initialize in the _init_ function and throw
        # a RuntimeError on failure. For non-mandatory components, all exceptions
        # are caught in in inner exceptions, mandatory are caught in the the outer
        # exception and result in shutting down
        logging.info("Initializing subprocess...")
        try:
            # start RTO first (IOT accept comms from the radios)
            logging.info("Starting RTO...")
            (conn1,conn2) = mp.Pipe()
            self._pConns['rto'] = conn1
            self._rto = RTO(self._ic,conn2,self._conf)
            logging.info("RTO started")

            # set the region? if so, do it prior to starting the radio(s)
            rd = self._conf['local']['region']
            if rd:
                logging.info("Setting regulatory domain to %s...",rd)
                self._rd = iw.regget()
                iw.regset(rd)
                if iw.regget() != rd:
                    logging.warning("Regulatory domain %s may not have been set",rd)
                else:
                    logging.info("Regulatory domain set to %s",rd)

            # recon radio is mandatory
            mode = 'paused' if self._conf['recon']['paused'] else 'scan'
            logging.info("Starting Reconnaissance Radio...")
            (conn1,conn2) = mp.Pipe()
            self._pConns['recon'] = conn1
            self._rr = RadioController(self._ic,conn2,self._conf['recon'])
            logging.info("Reconnaissance Radio started in %s mode",mode)

            # collection if present
            if self._conf['collection']:
                try:
                    mode = 'paused' if self._conf['collection']['paused'] else 'scan'
                    logging.info("Starting Collection Radio...")
                    (conn1,conn2) = mp.Pipe()
                    self._pConns['collection'] = conn1
                    self._cr = RadioController(self._ic,conn2,self._conf['collection'])
                except RuntimeError as e:
                    # continue without collector, but log it
                    logging.warning("Collection Radio failed: %s, continuing w/out",e)
                else:
                    logging.info("Collection Radio started in %s mode",mode)

            # c2c socket -> on failure log it and keep going
            logging.info("Starting C2C...")
            try:
                (conn1,conn2) = mp.Pipe()
                self._pConns['c2c'] = conn1
                self._c2c = C2C(conn2,self._conf['local']['c2c'])
                self._c2c.start()
            except Exception as e:
                logging.warn("C2C failed: %s, continuing without" % e)
            else:
                 logging.info("C2C listening on port %d" % self._conf['local']['c2c'])
        except RuntimeError as e:
            # e should have the form "Major:Minor:Description"
            ms = e.message.split(':')
            logging.error("%s (%s) %s",ms[0],ms[1],ms[2])
            self._state = DYSKT_INVALID
        except Exception as e:
            # catchall
            logging.error(e)
            self._state = DYSKT_INVALID
        else:
            # start children execution
            self._state = DYSKT_CREATED
            self._rr.start()
            if self._cr: self._cr.start()
            self._rto.start()

    def _destroy(self):
        """ destroy DySKT cleanly """
        # change our state
        self._state = DYSKT_EXITING

        # reset regulatory domain if necessary
        if self._rd:
            try:
                logging.info("Resetting regulatory domain...")
                iw.regset(self._rd)
                if iw.regget() != self._rd: raise RuntimeError
            except:
                logging.warning("Failed to reset regulatory domain")
            else:
                logging.info("Regulatory domain reset")

        # halt main execution loop & send out poison pills
        logging.info("Stopping Sub-processes...")
        self._halt.set()
        for key in self._pConns:
            try:
                self._pConns[key].send('!STOP!')
                self._pConns[key].close()
            except IOError:
                # ignore any broken pipe errors
                pass

        # active_children has the side effect of joining the processes
        while mp.active_children(): time.sleep(0.5)
        while self._c2c.is_alive(): self._c2c.join(0.5)
        logging.info("Sub-processes stopped")

        # change our state
        self._state = DYSKT_DESTROYED

    def _readconf(self):
        """ read in config file at cpath """
        logging.info("Reading configuration file...")
        conf = ConfigParser.RawConfigParser()
        if not conf.read(self._cpath):
            raise DySKTConfException('%s is invalid' % self._cpath)

        # intialize conf to empty dict
        self._conf = {}

        try:
            # read in the recon radio configuration
            self._conf['recon'] = self._readradio(conf,'Recon')
            try:
                # catch any collection exceptions and log a warning
                if conf.has_section('Collection'):
                    self._conf['collection'] = self._readradio(conf,'Collection')
                else:
                    self._conf['collection'] = None
                    logging.info("No collection radio specified")
            except (ConfigParser.NoSectionError,ConfigParser.NoOptionError,
                    RuntimeError,ValueError):
                logging.warning("Invalid collection specification. Continuing without...")

            # GPS section
            self._conf['gps'] = {}
            self._conf['gps']['fixed'] = conf.getboolean('GPS','fixed')
            if self._conf['gps']['fixed']:
                self._conf['gps']['lat'] = conf.getfloat('GPS','lat')
                self._conf['gps']['lon'] = conf.getfloat('GPS','lon')
                self._conf['gps']['alt'] = conf.getfloat('GPS','alt')
                self._conf['gps']['dir'] = conf.getfloat('GPS','heading')
            else:
                self._conf['gps']['port'] = conf.getint('GPS','port')
                self._conf['gps']['id'] = conf.get('GPS','devid')
                self._conf['gps']['poll'] = conf.getfloat('GPS','poll')
                self._conf['gps']['epx'] = conf.getfloat('GPS','epx')
                self._conf['gps']['epy'] = conf.getfloat('GPS','epy')

            # Storage section
            self._conf['store'] = {'host':conf.get('Storage','host'),
                                   'port':conf.getint('Storage','port')}

            # Local section
            self._conf['local'] = {'region':None,'c2c':2526}

            # c2c
            if conf.has_option('Local','C2C'):
                try:
                    self._conf['local']['c2c'] = conf.getint('Local','C2C')
                except:
                    logging.warning("Invalid C2C port specification. Using default")

            # region
            if conf.has_option('Local','region'):
                reg = conf.get('Local','region')
                if len(reg) != 2:
                    logging.warning("Regulatory domain %s is invalid",reg)
                else:
                    self._conf['local']['region'] = conf.get('Local','region')
        except (ConfigParser.NoSectionError,ConfigParser.NoOptionError) as e:
            raise DySKTConfException("%s" % e)
        except (RuntimeError,ValueError) as e:
            raise DySKTConfException("%s" % e)

    # noinspection PyMethodMayBeStatic
    def _readradio(self,conf,rtype='Recon'):
        """ read in the rtype radio configuration from conf and return parsed dict """
        # don't bother if specified radio is not present
        if not conf.get(rtype,'nic') in wifaces():
            raise RuntimeError("Radio %s not present/not wireless" % conf.get(rtype,'nic'))

        # get nic and set role setting default antenna config
        r = {'nic':conf.get(rtype,'nic'),
             'paused':False,
             'spoofed':None,
             'ant_gain':0.0,
             'ant_loss':0.0,
             'ant_offset':0.0,
             'ant_type':0.0,
             'desc':"unknown",
             'scan_start':None,
             'role':rtype.lower(),
             'antennas':{}}

        # get optional properties
        if conf.has_option(rtype,'spoof'):
            spoof = conf.get(rtype,'spoof').upper()
            if re.match(MACADDR,spoof) is None:
                logging.warn("Invalid spoofed MAC addr %s specified",spoof)
            else:
                r['spoofed'] = spoof
        if conf.has_option(rtype,'desc'): r['desc'] = conf.get(rtype,'desc')
        if conf.has_option(rtype,'paused'): r['paused'] = conf.getboolean(rtype,'paused')

        # process antennas - get the number first
        try:
            nA = conf.getint(rtype,'antennas') if conf.has_option(rtype,'antennas') else 0
        except ValueError:
            nA = 0

        if nA:
            # antennas has been specified, warn (and ignore) invalid specifications
            try:
                gain = map(float,conf.get(rtype,'antenna_gain').split(','))
                if len(gain) != nA: raise RuntimeError('gain')
                atype = conf.get(rtype,'antenna_type').split(',')
                if len(atype) != nA: raise RuntimeError('type')
                loss = map(float,conf.get(rtype,'antenna_loss').split(','))
                if len(loss) != nA: raise RuntimeError('loss')
                rs = conf.get(rtype,'antenna_xyz').split(',')
                xyz = []
                for t in rs: xyz.append(tuple(map(int,t.split(':'))))
                if len(xyz) != nA: raise RuntimeError('xyz')
            except ConfigParser.NoOptionError as e:
                logging.warning("Antenna %s not specified",e)
            except ValueError as e:
                logging.warning("Invalid type %s for %s antenna configuration",e,rtype)
            except RuntimeError as e:
                logging.warning("Antenna %s has invalid number of specifications",e)
            else:
                r['antennas']['num'] = nA
                r['antennas']['gain'] = gain
                r['antennas']['type'] = atype
                r['antennas']['loss'] = loss
                r['antennas']['xyz'] = xyz
        else:
            # none, set all empty
            r['antennas']['num'] = 0
            r['antennas']['gain'] = []
            r['antennas']['type'] = []
            r['antennas']['loss'] = []
            r['antennas']['xyz'] = []

        # get scan pattern
        r['dwell'] = conf.getfloat(rtype,'dwell')
        if r['dwell'] <= 0: raise ValueError("dwell must be > 0")
        r['scan'] = parsechlist(conf.get(rtype,'scan'),'scan')
        r['pass'] = parsechlist(conf.get(rtype,'pass'),'pass')
        if conf.has_option(rtype,'scan_start'):
            try:
                scanspec = conf.get(rtype,'scan_start')
                if ':' in scanspec:
                    (ch,chw) = scanspec.split(':')
                else:
                    ch = scanspec
                    chw = None
                ch = int(ch) if ch else r['scan'][0][0]
                if not chw in iw.IW_CHWS: chw = r['scan'][0][1]
                r['scan_start'] = (ch,chw) if (ch,chw) in r['scan'] else r['scan'][0]
            except ValueError:
                # use default
                r['scan_start'] = r['scan'][0]
        else:
            r['scan_start'] = r['scan'][0]

        return r

    def _processcmd(self,msg):
        """ parse and process command from c2c socket """
        # ensure there are at least 3 tokens
        tkns = None
        try:
            tkns = msg.split(' ')
        except:
            return None,None,None,None

        if len(tkns) < 3:
            self._pConns['c2c'].send("ERR ? \001invalid command format\001\n")
            return None,None,None,None

        # and a cmdid is present
        cmdid = None
        try:
            cmdid = int(tkns[0].replace('!',''))
        except:
            self._pConns['c2c'].send("ERR ? \001invalid command format\001\n")
            return None,None,None,None

        # ATT we can error/ack with associated cmd id
        cmd = tkns[1].strip().lower()
        if cmd not in ['state','scan','hold','listen','pause','txpwr','spoof']:
            self._pConns['c2c'].send("ERR %d \001invalid command\001\n" % cmdid)
            return None,None,None,None

        if cmd == 'txpwr' or cmd == 'spoof':
            self._pConns['c2c'].send("ERR %d \001%s not implemented\001\n" % (cmdid,cmd))
            return None,None,None,None

        radios = []
        radio = tkns[2].strip().lower()
        if radio not in ['both','all','recon','collection']:
            self._pConns['c2c'].send("ERR %d \001invalid radio specification: %s \001\n" % (cmdid,radio))
            return None,None,None,None

        if radio == 'all':
            radios = ['recon']
            if self._cr is not None: radios.append('collection')
        elif radio == 'both': radios = ['recon','collection']
        else: radios = [radio]
        if 'collection' in radios and self._cr is None:
            self._pConns['c2c'].send("ERR %d \001collection radio not present\001\n" % cmdid)
            return None,None,None,None

        # ensure any params are valid
        ps = []
        if cmd in ['listen','txpwr','spoof']:
            try:
                ps = tkns[3].strip().split(':')
                if len(ps) not in [1,2,6]: raise RuntimeError
                if cmd == 'listen':
                    # listen will be ch:chw or ch only
                    ps[0] = int(ps[0])
                    # ensure chwidth is correct or not specified set to None
                    if len(ps) == 2:
                        if ps[1] not in ['HT20','HT40+','HT40-']: raise RuntimeError
                    else:
                        ps.append(None)
                elif cmd == 'txpwr':
                    # txpwr will be pwr:opt
                    ps[0] = int(ps[0])
                    if ps[1] not in ['fixed','auto','limit']: raise RuntimeError
                elif cmd == 'spoof':
                    # spoof will be macaddr
                    if len(ps) != 6: raise RuntimeError
                    ps = [ps.join(':')] # rejoin the mac
                    if re.match(MACADDR,ps[0].upper()) is None: raise RuntimeError
            except:
                self._pConns['c2c'].send("ERR %d \001invalid params\001\n" % cmdid)
                return None,None,None,None

        # all good
        return cmdid,cmd,radios,ps