def __init__(self): # open syslog and notice startup syslog.openlog('captiveportal', logoption=syslog.LOG_DAEMON, facility=syslog.LOG_LOCAL4) syslog.syslog(syslog.LOG_NOTICE, 'starting captiveportal background process') # handles to ipfw, arp the config and the internal administration self.ipfw = IPFW() self.arp = ARP() self.cnf = Config() self.db = DB() self._conf_zone_info = self.cnf.get_zones()
def __init__(self): # open syslog and notice startup syslog.openlog('captiveportal', logoption=syslog.LOG_DAEMON) syslog.syslog(syslog.LOG_NOTICE, 'starting captiveportal background process') # handles to ipfw, arp the config and the internal administration self.ipfw = IPFW() self.arp = ARP() self.cnf = Config() self.db = DB() self._conf_zone_info = self.cnf.get_zones()
class CPBackgroundProcess(object): """ background process helper class """ def __init__(self): # open syslog and notice startup syslog.openlog('captiveportal', logoption=syslog.LOG_DAEMON) syslog.syslog(syslog.LOG_NOTICE, 'starting captiveportal background process') # handles to ipfw, arp the config and the internal administration self.ipfw = IPFW() self.arp = ARP() self.cnf = Config() self.db = DB() self._conf_zone_info = self.cnf.get_zones() def list_zone_ids(self): """ return zone numbers """ return self._conf_zone_info.keys() def initialize_fixed(self): """ initialize fixed ip / hosts per zone """ cpzones = self._conf_zone_info for zoneid in cpzones: for conf_section in ['allowedaddresses', 'allowedmacaddresses']: for address in cpzones[zoneid][conf_section]: if conf_section.find('mac') == -1: sessions = self.db.sessions_per_address(zoneid, ip_address=address) ip_address = address mac_address = None else: sessions = self.db.sessions_per_address(zoneid, mac_address=address) ip_address = None mac_address = address sessions_deleted = 0 for session in sessions: if session['authenticated_via'] not in ('---ip---', '---mac---'): sessions_deleted += 1 self.db.del_client(zoneid, session['sessionId']) if sessions_deleted == len(sessions) or len(sessions) == 0: # when there's no session active, add a new one # (only administrative, the sync process will add it if neccesary) if ip_address is not None: self.db.add_client(zoneid, "---ip---", "", ip_address, "") else: self.db.add_client(zoneid, "---mac---", "", "", mac_address) # cleanup removed static sessions for dbclient in self.db.list_clients(zoneid): if dbclient['authenticated_via'] == '---ip---' \ and dbclient['ipAddress'] not in cpzones[zoneid]['allowedaddresses']: self.ipfw.delete(zoneid, dbclient['ipAddress']) self.db.del_client(zoneid, dbclient['sessionId']) elif dbclient['authenticated_via'] == '---mac---' \ and dbclient['macAddress'] not in cpzones[zoneid]['allowedmacaddresses']: if dbclient['ipAddress'] != '': self.ipfw.delete(zoneid, dbclient['ipAddress']) self.db.del_client(zoneid, dbclient['sessionId']) def sync_zone(self, zoneid): """ Synchronize captiveportal zone. Handles timeouts and administrative changes to this zones sessions """ if zoneid in self._conf_zone_info: # fetch data for this zone cpzone_info = self._conf_zone_info[zoneid] registered_addresses = self.ipfw.list_table(zoneid) registered_addr_accounting = self.ipfw.list_accounting_info() expected_clients = self.db.list_clients(zoneid) concurrent_users = self.db.find_concurrent_user_sessions(zoneid) # handle connected clients, timeouts, address changes, etc. for db_client in expected_clients: # fetch ip address (or network) from database cpnet = db_client['ipAddress'].strip() # there are different reasons why a session should be removed, check for all reasons and # use the same method for the actual removal drop_session_reason = None # session cleanups, only for users not for static hosts/ranges. if db_client['authenticated_via'] not in ('---ip---', '---mac---'): # check if hardtimeout is set and overrun for this session if 'hardtimeout' in cpzone_info and str(cpzone_info['hardtimeout']).isdigit(): # hardtimeout should be set and we should have collected some session data from the client if int(cpzone_info['hardtimeout']) > 0 and float(db_client['startTime']) > 0: if (time.time() - float(db_client['startTime'])) / 60 > int(cpzone_info['hardtimeout']): drop_session_reason = "session %s hit hardtimeout" % db_client['sessionId'] # check if idletimeout is set and overrun for this session if 'idletimeout' in cpzone_info and str(cpzone_info['idletimeout']).isdigit(): # idletimeout should be set and we should have collected some session data from the client if int(cpzone_info['idletimeout']) > 0 and float(db_client['last_accessed']) > 0: if (time.time() - float(db_client['last_accessed'])) / 60 > int(cpzone_info['idletimeout']): drop_session_reason = "session %s hit idletimeout" % db_client['sessionId'] # cleanup concurrent users if 'concurrentlogins' in cpzone_info and int(cpzone_info['concurrentlogins']) == 0: if db_client['sessionId'] in concurrent_users: drop_session_reason = "remove concurrent session %s" % db_client['sessionId'] # if mac address changes, drop session. it's not the same client current_arp = self.arp.get_by_ipaddress(cpnet) if current_arp is not None and current_arp['mac'] != db_client['macAddress']: drop_session_reason = "mac address changed for session %s" % db_client['sessionId'] elif db_client['authenticated_via'] == '---mac---': # detect mac changes current_ip = self.arp.get_address_by_mac(db_client['macAddress']) if current_ip != None: if db_client['ipAddress'] != '': # remove old ip self.ipfw.delete(zoneid, db_client['ipAddress']) self.db.update_client_ip(zoneid, db_client['sessionId'], current_ip) self.ipfw.add_to_table(zoneid, current_ip) self.ipfw.add_accounting(current_ip) # check session, if it should be active, validate its properties if drop_session_reason is None: # registered client, but not active according to ipfw (after reboot) if cpnet not in registered_addresses: self.ipfw.add_to_table(zoneid, cpnet) # is accounting rule still available? need to reapply after reload / reboot if cpnet not in registered_addr_accounting: self.ipfw.add_accounting(cpnet) else: # remove session syslog.syslog(syslog.LOG_NOTICE, drop_session_reason) self.ipfw.delete(zoneid, cpnet) self.db.del_client(zoneid, db_client['sessionId']) # if there are addresses/networks in the underlying ipfw table which are not in our administration, # remove them from ipfw. for registered_address in registered_addresses: address_active = False for db_client in expected_clients: if registered_address == db_client['ipAddress']: address_active = True break if not address_active: self.ipfw.delete(zoneid, registered_address)
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------------- return a list of all available rrd files including additional definition data """ from lib.arp import ARP import sys import ujson arp_list = ARP().list_items() if len(sys.argv) > 1 and sys.argv[1].trim().lower() == 'json': # dump as json print(ujson.dumps(arp_list)) else: print( '------------------------- ARP table content -------------------------' ) for address in arp_list: print('[%10s] %-20s %-20s' % (arp_list[address]['intf'], address, arp_list[address]['mac']))
'output_type': 'plain' } current_param = None for param in sys.argv[1:]: if len(param) > 1 and param[0] == '/': current_param = param[1:].lower() elif current_param is not None: if current_param in parameters: parameters[current_param] = param.strip() current_param = None # create new session if parameters['ip_address'] is not None and parameters['zoneid'] is not None: cpDB = DB() cpIPFW = IPFW() arp_entry = ARP().get_by_ipaddress(parameters['ip_address']) if arp_entry is not None: mac_address = arp_entry['mac'] else: mac_address = None response = cpDB.add_client( zoneid=parameters['zoneid'], authenticated_via=parameters['authenticated_via'], username=parameters['username'], ip_address=parameters['ip_address'], mac_address=mac_address) # check if address is not already registered before adding it to the ipfw table if not cpIPFW.ip_or_net_in_table(table_number=parameters['zoneid'], address=parameters['ip_address']): cpIPFW.add_to_table(table_number=parameters['zoneid'],
class CPBackgroundProcess(object): """ background process helper class """ def __init__(self): # open syslog and notice startup syslog.openlog('captiveportal', logoption=syslog.LOG_DAEMON, facility=syslog.LOG_LOCAL4) syslog.syslog(syslog.LOG_NOTICE, 'starting captiveportal background process') # handles to ipfw, arp the config and the internal administration self.ipfw = IPFW() self.arp = ARP() self.cnf = Config() self.db = DB() self._conf_zone_info = self.cnf.get_zones() def list_zone_ids(self): """ return zone numbers """ return self._conf_zone_info.keys() def initialize_fixed(self): """ initialize fixed ip / hosts per zone """ cpzones = self._conf_zone_info for zoneid in cpzones: for conf_section in ['allowedaddresses', 'allowedmacaddresses']: for address in cpzones[zoneid][conf_section]: if conf_section.find('mac') == -1: sessions = self.db.sessions_per_address(zoneid, ip_address=address) ip_address = address mac_address = None else: sessions = self.db.sessions_per_address(zoneid, mac_address=address) ip_address = None mac_address = address sessions_deleted = 0 for session in sessions: if session['authenticated_via'] not in ('---ip---', '---mac---'): sessions_deleted += 1 self.db.del_client(zoneid, session['sessionId']) if sessions_deleted == len(sessions) or len(sessions) == 0: # when there's no session active, add a new one # (only administrative, the sync process will add it if neccesary) if ip_address is not None: self.db.add_client(zoneid, "---ip---", "", ip_address, "") else: self.db.add_client(zoneid, "---mac---", "", "", mac_address) # cleanup removed static sessions for dbclient in self.db.list_clients(zoneid): if dbclient['authenticated_via'] == '---ip---' \ and dbclient['ipAddress'] not in cpzones[zoneid]['allowedaddresses']: self.ipfw.delete(zoneid, dbclient['ipAddress']) self.db.del_client(zoneid, dbclient['sessionId']) elif dbclient['authenticated_via'] == '---mac---' \ and dbclient['macAddress'] not in cpzones[zoneid]['allowedmacaddresses']: if dbclient['ipAddress'] != '': self.ipfw.delete(zoneid, dbclient['ipAddress']) self.db.del_client(zoneid, dbclient['sessionId']) def sync_zone(self, zoneid): """ Synchronize captiveportal zone. Handles timeouts and administrative changes to this zones sessions """ if zoneid in self._conf_zone_info: # fetch data for this zone cpzone_info = self._conf_zone_info[zoneid] registered_addresses = self.ipfw.list_table(zoneid) registered_addr_accounting = self.ipfw.list_accounting_info() expected_clients = self.db.list_clients(zoneid) concurrent_users = self.db.find_concurrent_user_sessions(zoneid) # handle connected clients, timeouts, address changes, etc. for db_client in expected_clients: # fetch ip address (or network) from database cpnet = db_client['ipAddress'].strip() # there are different reasons why a session should be removed, check for all reasons and # use the same method for the actual removal drop_session_reason = None # session cleanups, only for users not for static hosts/ranges. if db_client['authenticated_via'] not in ('---ip---', '---mac---'): # check if hardtimeout is set and overrun for this session if 'hardtimeout' in cpzone_info and str(cpzone_info['hardtimeout']).isdigit(): # hardtimeout should be set and we should have collected some session data from the client if int(cpzone_info['hardtimeout']) > 0 and float(db_client['startTime']) > 0: if (time.time() - float(db_client['startTime'])) / 60 > int(cpzone_info['hardtimeout']): drop_session_reason = "session %s hit hardtimeout" % db_client['sessionId'] # check if idletimeout is set and overrun for this session if 'idletimeout' in cpzone_info and str(cpzone_info['idletimeout']).isdigit(): # idletimeout should be set and we should have collected some session data from the client if int(cpzone_info['idletimeout']) > 0 and float(db_client['last_accessed']) > 0: if (time.time() - float(db_client['last_accessed'])) / 60 > int(cpzone_info['idletimeout']): drop_session_reason = "session %s hit idletimeout" % db_client['sessionId'] # cleanup concurrent users if 'concurrentlogins' in cpzone_info and int(cpzone_info['concurrentlogins']) == 0: if db_client['sessionId'] in concurrent_users: drop_session_reason = "remove concurrent session %s" % db_client['sessionId'] # if mac address changes, drop session. it's not the same client current_arp = self.arp.get_by_ipaddress(cpnet) if current_arp is not None and current_arp['mac'] != db_client['macAddress']: drop_session_reason = "mac address changed for session %s" % db_client['sessionId'] # session accounting if db_client['acc_session_timeout'] is not None \ and time.time() - float(db_client['startTime']) > db_client['acc_session_timeout']: drop_session_reason = "accounting limit reached for session %s" % db_client['sessionId'] elif db_client['authenticated_via'] == '---mac---': # detect mac changes current_ip = self.arp.get_address_by_mac(db_client['macAddress']) if current_ip is not None: if db_client['ipAddress'] != '': # remove old ip self.ipfw.delete(zoneid, db_client['ipAddress']) self.db.update_client_ip(zoneid, db_client['sessionId'], current_ip) self.ipfw.add_to_table(zoneid, current_ip) self.ipfw.add_accounting(current_ip) # check session, if it should be active, validate its properties if drop_session_reason is None: # registered client, but not active according to ipfw (after reboot) if cpnet not in registered_addresses: self.ipfw.add_to_table(zoneid, cpnet) # is accounting rule still available? need to reapply after reload / reboot if cpnet not in registered_addr_accounting: self.ipfw.add_accounting(cpnet) else: # remove session syslog.syslog(syslog.LOG_NOTICE, drop_session_reason) self.ipfw.delete(zoneid, cpnet) self.db.del_client(zoneid, db_client['sessionId']) # if there are addresses/networks in the underlying ipfw table which are not in our administration, # remove them from ipfw. for registered_address in registered_addresses: address_active = False for db_client in expected_clients: if registered_address == db_client['ipAddress']: address_active = True break if not address_active: self.ipfw.delete(zoneid, registered_address)
def main(): syslog.syslog(syslog.LOG_ERR, 'starting captiveportal background process') # handle to ipfw, arp and the config ipfw = IPFW() arp = ARP() cnf = Config() while True: try: # construct objects db = DB() # update accounting info db.update_accounting_info(ipfw.list_accounting_info()) # process sessions per zone arp.reload() cpzones = cnf.get_zones() for zoneid in cpzones: registered_addresses = ipfw.list_table(zoneid) registered_add_accounting = ipfw.list_accounting_info() expected_clients = db.list_clients(zoneid) # handle connected clients, timeouts, address changes, etc. for db_client in expected_clients: # fetch ip address (or network) from database cpnet = db_client['ipAddress'].strip() # there are different reasons why a session should be removed, check for all reasons and # use the same method for the actual removal drop_session = False # todo, static ip and addresses shouldn't be affected by the timeout rules below. # check if hardtimeout is set and overrun for this session if 'hardtimeout' in cpzones[zoneid] and str(cpzones[zoneid]['hardtimeout']).isdigit(): # hardtimeout should be set and we should have collected some session data from the client if int(cpzones[zoneid]['hardtimeout']) > 0 and float(db_client['startTime']) > 0: if (time.time() - float(db_client['startTime'])) / 60 > int(cpzones[zoneid]['hardtimeout']): drop_session = True # check if idletimeout is set and overrun for this session if 'idletimeout' in cpzones[zoneid] and str(cpzones[zoneid]['idletimeout']).isdigit(): # idletimeout should be set and we should have collected some session data from the client if int(cpzones[zoneid]['idletimeout']) > 0 and float(db_client['last_accessed']) > 0: if (time.time() - float(db_client['last_accessed'])) / 60 > int(cpzones[zoneid]['idletimeout']): drop_session = True # check session, if it should be active, validate its properties if not drop_session: # registered client, but not active according to ipfw (after reboot) if cpnet not in registered_addresses: ipfw.add_to_table(zoneid, cpnet) # is accounting rule still available? need to reapply after reload / reboot if cpnet not in registered_add_accounting and db_client['ipAddress'] not in registered_add_accounting: ipfw.add_accounting(cpnet) else: # remove session db.del_client(zoneid, db_client['sessionId']) ipfw.delete_from_table(zoneid, cpnet) ipfw.del_accounting(cpnet) # cleanup, destruct del db # sleep time.sleep(5) except KeyboardInterrupt: break except SystemExit: break except: syslog.syslog(syslog.LOG_ERR, traceback.format_exc())