def _do_mod(self, ipaddr, port, proto, tcpmss, tcpsack, tcpwscale): # Modify a connection based on given parameters # Define inserting position based on n-tuple match if ipaddr != '0.0.0.0' and port != 0: # 3-tuple connection, higher priority pos = 1 elif ipaddr != '0.0.0.0' and port == 0: # 3-tuple connection with wildcard port, higher priority than default pos = -3 elif ipaddr == '0.0.0.0': # Default connection, lowest priority pos = -2 else: raise Exception('Unsupported! {}'.format(ipaddr, proto, port, tcpmss, tcpsack, tcpwscale)) if self.connectiontable.has((ipaddr, port, proto)): connection = self.connectiontable.get((ipaddr, port, proto)) # Check if the existing parameters are the same if connection.tcpmss == tcpmss and connection.tcpsack == tcpsack and connection.tcpwscale == tcpwscale: self._logger.debug( '[mod] Modify not required : {} / {}'.format( connection, (tcpmss, tcpsack, tcpwscale))) return True # Create iptables rule and update connection object old_ipt_rule = connection.ipt_rule new_ipt_rule = self._build_ipt_synproxy_rule( ipaddr, port, proto, tcpmss, tcpsack, tcpwscale) self._logger.info('Modify connection: {} / {}'.format( connection, (tcpmss, tcpsack, tcpwscale))) connection.ipt_rule = new_ipt_rule # Replace rule iptc_helper3.replace_rule('filter', 'synproxy_chain', old_ipt_rule, new_ipt_rule, ipv6=False) else: # Create iptables rule and connection object ipt_rule = self._build_ipt_synproxy_rule(ipaddr, port, proto, tcpmss, tcpsack, tcpwscale) connection = SYNProxyConnectionTCP(ipaddr, port, proto, tcpmss, tcpsack, tcpwscale, ipt_rule) self.connectiontable.add(connection) # Insert rule at precise position self._logger.info('Add connection: {}'.format(connection)) iptc_helper3.add_rule('filter', 'synproxy_chain', ipt_rule, position=pos, ipv6=False) return True
def _generic_setup_provisioning(self, population, depth, token): ''' Recursive function to walk tree structure for given depth and population, use carry-me-token for inner leafs Use token = '' for building next level keys based on iterative concatenation of index keys Use token = None for building next level keys based on local index keys ''' assert (depth >= 0) # Generate chain name based on tree position for table in ['raw', 'filter']: chain = self._whoami(table, token) iptc_helper3.add_chain(table, chain, ipv6=False, silent=False) iptc_helper3.flush_chain(table, chain, ipv6=False, silent=False) self.logger.debug('create&flush {}.{}'.format(table, chain)) # Add records to classifier for fast indexing if depth == 0: self.provisioning_d[table][token] = chain if depth == 0: return # Walk towards each child, on return, link to them from the present chain for child in population: # Black magic for carry-me-token if token is not None: child = token_child = '{}{}'.format( token, child) # Recalculate new token and use as local key else: token_child = token # Carry same token to next level # Execute function for each children, before walking pass # Walk child self._generic_setup_provisioning(population, depth - 1, token_child) # Execute function for each children, after walking for table in ['raw', 'filter']: chain = self._whoami(table, token) target_chain = self._whoami(table, token_child) classify_rule = { 'in-interface': 'qbr{}+'.format(token_child), 'target': target_chain } self.logger.debug('add_rule / @{} {}'.format( chain, classify_rule)) iptc_helper3.add_rule(table, chain, classify_rule, ipv6=False)
def _do_add(self, ipaddr, port, proto, tcpmss, tcpsack, tcpwscale): # Add a connection based on given parameters # Define inserting position based on n-tuple match if ipaddr != '0.0.0.0' and port != 0: # 3-tuple connection, higher priority pos = 1 elif ipaddr != '0.0.0.0' and port == 0: # 3-tuple connection with wildcard port, higher priority than default pos = -3 elif ipaddr == '0.0.0.0': # Default connection, lowest priority pos = -2 else: raise Exception('Unsupported! {}'.format(ipaddr, proto, port, tcpmss, tcpsack, tcpwscale)) if self.connectiontable.has((ipaddr, port, proto)): connection = self.connectiontable.get((ipaddr, port, proto)) self._logger.debug('[add] Conflict exists for {} / {}'.format( (ipaddr, port, proto, tcpmss, tcpsack, tcpwscale), connection)) return False # Create iptables rule and connection object ipt_rule = self._build_ipt_synproxy_rule(ipaddr, port, proto, tcpmss, tcpsack, tcpwscale) connection = SYNProxyConnectionTCP(ipaddr, port, proto, tcpmss, tcpsack, tcpwscale, ipt_rule) self.connectiontable.add(connection) # Insert rule at precise position self._logger.info('Add connection: {}'.format(connection)) iptc_helper3.add_rule('filter', 'synproxy_chain', ipt_rule, position=pos, ipv6=False) return True
def setup_provisioning(self): self.logger.info('Installing common provisioning') # Store key value of classifying uuid to chain, for each table self.provisioning_d = {'raw': {}, 'filter': {}} # Generate population base self.population = self._generate_population_base( self.args.classify_base) # Do custom provisioning based on optimization model self.logger.info('Installing optimization specific provisioning') self._generic_setup_provisioning(self.population, self.args.classify_level, '') # Insert hooks in raw and filter tables rule = {'in-interface': 'qbr+', 'target': CUSTOM_RAW_PRE} iptc_helper3.add_rule('raw', NEUTRON_RAW_PRE_HOOK, rule, NEUTRON_RAW_PRE_HOOK_i, ipv6=False) rule = {'in-interface': 'qbr+', 'target': CUSTOM_FILTER_FWD} iptc_helper3.add_rule('filter', NEUTRON_FILTER_FWD_HOOK, rule, NEUTRON_FILTER_FWD_HOOK_i, ipv6=False)
def ipt_init_flows(self): # Specific TCP flows are inserted/appended to filter.synproxy_chain """ self._logger.info('Initialize iptables chains and rules') # Add custom chains iptc_helper3.add_chain('raw', 'synproxy_chain', ipv6=False, silent=True) iptc_helper3.add_chain('filter', 'synproxy_chain', ipv6=False, silent=True) # Flush chains iptc_helper3.flush_chain('raw', 'PREROUTING', ipv6=False, silent=False) iptc_helper3.flush_chain('filter', 'FORWARD', ipv6=False, silent=False) iptc_helper3.flush_chain('raw', 'synproxy_chain', ipv6=False, silent=False) iptc_helper3.flush_chain('filter', 'synproxy_chain', ipv6=False, silent=False) # Populate chains with basic rules ## raw.PREROUTING ### Add a rate limitation to raw.PREROUTING via hashlimit module _above = '{}/sec'.format(self.ratelimit[0]) _burst = '{}'.format(self.ratelimit[1]) rule_d = { 'in-interface': 'mitm0', 'protocol': 'tcp', 'tcp': { 'tcp-flags': ['FIN,SYN,RST,ACK', 'SYN'] }, 'target': 'DROP', 'hashlimit': { 'hashlimit-above': _above, 'hashlimit-burst': _burst, 'hashlimit-name': 'internet_syn', 'hashlimit-mode': 'srcip', 'hashlimit-htable-size': '2097152', 'hashlimit-srcmask': '24', 'hashlimit-htable-expire': '1001' } } iptc_helper3.add_rule('raw', 'PREROUTING', rule_d, position=0, ipv6=False) ### rule_d = { 'in-interface': 'mitm0', 'protocol': 'tcp', 'target': 'synproxy_chain' } iptc_helper3.add_rule('raw', 'PREROUTING', rule_d, position=0, ipv6=False) rule_d = { 'in-interface': 'mitm1', 'protocol': 'tcp', 'target': 'synproxy_chain' } iptc_helper3.add_rule('raw', 'PREROUTING', rule_d, position=0, ipv6=False) ## raw.synproxy_chain rule_d = { 'in-interface': 'mitm0', 'protocol': 'tcp', 'tcp': { 'tcp-flags': ['FIN,SYN,RST,ACK', 'SYN'] }, 'target': { 'CT': { 'notrack': '' } }, } iptc_helper3.add_rule('raw', 'synproxy_chain', rule_d, position=0, ipv6=False) rule_d = {'target': 'ACCEPT'} iptc_helper3.add_rule('raw', 'synproxy_chain', rule_d, position=0, ipv6=False) ## filter.FORWARD rule_d = { 'in-interface': 'mitm0', 'protocol': 'tcp', 'target': 'synproxy_chain' } iptc_helper3.add_rule('filter', 'FORWARD', rule_d, position=0, ipv6=False) rule_d = { 'in-interface': 'mitm1', 'protocol': 'tcp', 'target': 'synproxy_chain' } iptc_helper3.add_rule('filter', 'FORWARD', rule_d, position=0, ipv6=False) ## filter.synproxy_chain rule_d = { 'protocol': 'tcp', 'conntrack': { 'ctstate': ['INVALID'] }, 'target': 'DROP' } iptc_helper3.add_rule('filter', 'synproxy_chain', rule_d, position=0, ipv6=False)
def _apply_optimized_rules(self, default, optimized): ''' Receives dicts of (uuid, rule) and applies diff following simple optimization ''' # Calculate new sets for added & removed default_s = set(default.keys()) optimized_s = set(optimized.keys()) removed_s = optimized_s - default_s added_s = default_s - optimized_s if removed_s: self.logger.info('>> Remove old VM(s)! {}'.format(removed_s)) if added_s: self.logger.info('>> Add new VM(s)! {}'.format(added_s)) # Remove old entries for i, _uuid in enumerate(removed_s): self.logger.info('>> [#{}] Removing VM {}'.format(i + 1, _uuid)) vm_chain = GENERATE_VM_CHAIN_NAME(_uuid) _rule = optimized[_uuid] # Iterate insertion tables and do cleanup for table in ['raw', 'filter']: cl_chain = self._lookup_chain_by_uuid(table, _uuid) if not cl_chain: self.logger.critical('UUID {} not found in base {}'.format( _uuid, self.args.classify_base)) continue # Delete trigger rule from classifying chain self.logger.debug('>>> delete rule: {}.{} {}'.format( table, cl_chain, _rule)) iptc_helper3.delete_rule(table, cl_chain, _rule, ipv6=False) # Delete VM optimized chain self.logger.debug('>>> flush & delete chain: {}.{}'.format( table, vm_chain)) iptc_helper3.flush_chain(table, vm_chain, ipv6=False) iptc_helper3.delete_chain(table, vm_chain, ipv6=False) # Add new entries for i, _uuid in enumerate(added_s): self.logger.info('>> [#{}] Adding VM {}'.format(i + 1, _uuid)) vm_chain = GENERATE_VM_CHAIN_NAME(_uuid) ### RAW TABLE ### # Select classifying table to insert triggers table = 'raw' cl_chain = self._lookup_chain_by_uuid(table, _uuid) if not cl_chain: self.logger.critical('UUID {} not found in base {}'.format( _uuid, self.args.classify_base)) continue self.logger.debug('Selected classifying chain {}'.format(cl_chain)) ## Create & populate VM optimized chain with simplest rules self.logger.debug('>>> add chain: {}.{}'.format(table, vm_chain)) iptc_helper3.add_chain(table, vm_chain, ipv6=False) _rule = dict(default[_uuid]) if 'physdev' in _rule: del _rule['physdev'] if 'in-interface' in _rule: del _rule['in-interface'] iptc_helper3.add_rule(table, vm_chain, _rule, ipv6=False) _rule = { 'comment': { 'comment': 'Accept early' }, 'target': 'ACCEPT' } iptc_helper3.add_rule(table, vm_chain, _rule, ipv6=False) ## Add trigger rule to classifying chain _rule = dict(default[_uuid]) if 'physdev' in _rule: del _rule['physdev'] _rule['in-interface'] = 'qbr{}'.format(_uuid) _rule['target'] = vm_chain self.logger.debug('>>> add rule: {}.{} {}'.format( table, cl_chain, _rule)) iptc_helper3.add_rule(table, cl_chain, _rule, ipv6=False) ### FILTER TABLE ### # Select classifying table to insert triggers table = 'filter' cl_chain = self._lookup_chain_by_uuid(table, _uuid) if not cl_chain: self.logger.critical('UUID {} not found in base {}'.format( _uuid, self.args.classify_base)) continue self.logger.debug('Selected classifying chain {}'.format(cl_chain)) ## Create & populate VM optimized chain with simplest rules self.logger.debug('>>> add chain: {}.{}'.format(table, vm_chain)) iptc_helper3.add_chain(table, vm_chain, ipv6=False) _rule = { 'comment': { 'comment': 'Jump to the VM specific chain.' }, 'physdev': { 'physdev-is-bridged': [], 'physdev-in': 'tap{}'.format(_uuid) }, 'target': GEN_NEUTRON_SG_EGRESS(_uuid) } iptc_helper3.add_rule(table, vm_chain, _rule, ipv6=False) _rule = { 'comment': { 'comment': 'Jump to the VM specific chain.' }, 'physdev': { 'physdev-is-bridged': '', 'physdev-out': 'tap{}'.format(_uuid) }, 'target': GEN_NEUTRON_SG_INGRESS(_uuid) } iptc_helper3.add_rule(table, vm_chain, _rule, ipv6=False) _rule = { 'comment': { 'comment': 'Accept early' }, 'target': 'ACCEPT' } iptc_helper3.add_rule(table, vm_chain, _rule, ipv6=False) ## Add trigger rule to classifying chain _rule = dict(default[_uuid]) if 'physdev' in _rule: del _rule['physdev'] _rule['in-interface'] = 'qbr{}'.format(_uuid) _rule['target'] = vm_chain self.logger.debug('>>> add rule: {}.{} {}'.format( table, cl_chain, _rule)) iptc_helper3.add_rule(table, cl_chain, _rule, ipv6=False) # Return number of optimizations performed return len(added_s)