def _buildAcls(self): for aclname, aclrules in self.config.match.iteritems(): key = eossdk.AclKey(aclname, eossdk.ACL_TYPE_IPV4) # todo support ipv6 also for i, rule in enumerate(aclrules): aclRule = eossdk.AclRuleIp() if rule.proto: pr = PROTOCOLS.get(rule.proto.lower()) if pr: aclRule.ip_protocol_is(pr) else: sys.stderr.write('Invalid protocol name "%s"', rule.proto) if rule.src_ip: try: srcPfx = eossdk.IpPrefix(rule.src_ip) addr = eossdk.IpAddrMask(srcPfx.network(), srcPfx.prefix_length()) aclRule.source_addr_is(addr) except eossdk.Error: sys.stderr.write('bad IP address: %s\n', rule.src_ip) continue if rule.dst_ip: try: dstPfx = eossdk.IpPrefix(rule.dst_ip) addr = eossdk.IpAddrMask(dstPfx.network(), dstPfx.prefix_length()) aclRule.destination_addr_is(addr) except eossdk.Error: sys.stderr.write('bad IP address: %s\n', rule.dst_ip) continue if rule.sport: try: spec = self._buildPortSpec(rule.sport) except ConfigError as e: sys.stderr.write('Invalid port spec %r: %s\n', spec, e) else: aclRule.source_port_is(spec) if rule.dport: try: spec = self._buildPortSpec(rule.dport) except ConfigError as e: sys.stderr.write('Invalid port spec %r: %s\n', spec, e) else: aclRule.destination_port_is(spec) self.acl_mgr.acl_rule_set(key, i, aclRule) self.acl_mgr.acl_commit()
def _buildClassMaps(self): classifiers = self.config_.classifiers for name, classifier in classifiers.iteritems(): key = eossdk.PolicyMapKey(name, eossdk.POLICY_FEATURE_PBR) class_map = eossdk.ClassMap(key) for rule_index, match in enumerate(classifier.matches): print 'Adding to class map:', name, 'seq:', str(rule_index + 1), match rule_key = eossdk.AclKey(match.acl_name, eossdk.ACL_TYPE_IPV4) rule = eossdk.ClassMapRule(rule_key) # Set the action for the rule class_map.rule_set(rule_index + 1, rule) self.class_map_mgr.class_map_is(class_map) cm = self.class_map_mgr.class_map(key) print 'Set class map:', name, 'now with', len(cm.rules()), 'rules'
def on_initialized(self): self.tracer.trace1("Initialized") self.agentMgr_.status_set("Status:", "Administratively Up") #Set up all our options. global REDIRECTCOMMUNITY if self.agentMgr_.agent_option("REDIRECTCOMMUNITY"): self.on_agent_option( "REDIRECTCOMMUNITY", self.agentMgr_.agent_option("REDIRECTCOMMUNITY")) else: #global REDIRECTCOMMUNITY #We'll just use the default community specified by global variable self.agentMgr_.status_set("REDIRECTCOMMUNITY:", "%s" % REDIRECTCOMMUNITY) # initialize the ACL global REDIRECTACL self.aclKey = eossdk.AclKey(REDIRECTACL, eossdk.ACL_TYPE_IPV4)
def process_config(self): """Critical function; processes configuration and rules description files. Called upon initialization and then subsequently whenever inotify indicates the configuration file has changed on disk.""" self.tracer.trace0("Processing config") syslog.syslog("Attempting to process configuration file(s)") # Time stamp for performance evaluation start_time = time.time() self.start_time = start_time # Attempt to parse ACLerate_config_file # Initially, attempt to acquire the lock to ensure file not modified # by another entity while it is being processed here. syslog.syslog("Attempting to open, lock and parse %s" % self.config_file) try: with open(self.config_file) as acl_config_file: for i in xrange(file_lock_attempt): try: fcntl.flock(acl_config_file, fcntl.LOCK_EX | fcntl.LOCK_NB) break except IOError: sys.stderr.write("Attempt %s to lock %s " "failed\n" % (str(i+1), self.config_file)) time.sleep(file_lock_interval) else: syslog.syslog("All %s attempts to lock %s " "failed" % (str(file_lock_attempt), self.config_file)) sys.stderr.write("All %s attempts to lock %s " "failed\n" % (str(file_lock_attempt), self.config_file)) return syslog.syslog("%s opened & locked successfully. Now parse" % self.config_file) acl_config_list = json.load(acl_config_file) except IOError: syslog.syslog("Cannot open %s" % self.config_file) sys.stderr.write("Cannot open %s\n" % self.config_file) return for acl_config in acl_config_list: command = acl_config.get("command") name = acl_config.get("name") acl_type = acl_config.get("type") interface = acl_config.get("interface") operation = acl_config.get("operation") direction = acl_config.get("direction") rules_file = acl_config.get("rules") counting = acl_config.get("counting") # Sanity check ACL parameters (before potentially # iterating over thousands of rules!). If invalid, skip # processing rest of this ACL and continue to next ACL if not acl_validate(command, name, acl_type, direction, counting): syslog.syslog("Invalid ACL input data") sys.stderr.write("Invalid ACL input data\n") continue syslog.syslog("Processing %s command for %s ACL %s" % (command, acl_type, name)) # Convert external ACL type to SDK's type sdk_type = acl_type_convert(acl_type) if sdk_type is None: syslog.syslog("Invalid ACL type") sys.stderr.write("Invalid ACL type specified\n") continue # Get handle to ACL. acl_key = eossdk.AclKey(str(name), sdk_type) # If input command is to delete the ACL, simply call the appropriate # SDK API and continue onto next ACL in the list. i.e. no need to be # concerned with interfaces, rules etc. if command.lower() == "delete-acl": syslog.syslog("About to delete %s ACL %s" % (acl_type, name)) self.acl_mgr.acl_del(acl_key) # Now call commit to actually push changes to HW. self.acl_mgr.acl_commit() continue intf_id = None # Next, if an interface is specified, verify it actually exists # and correct parameters have been specified if interface: intf_id = self.interface_validate(interface, operation, direction) if intf_id: # Convert external direction to SDK's type sdk_direction = direction_convert(direction) if sdk_direction is None: syslog.syslog("Invalid direction %s specified" % direction) sys.stderr.write("Invalid direction %s specified\n" % direction) continue else: syslog.syslog("Invalid interface %s specified" % interface) sys.stderr.write("Invalid interface %s specified\n" % interface) continue # Set ACL counting behaviour if specified. If not, will # simply fallback to default ACL behaviour. if counting: if counting.lower() == "true": self.acl_mgr.acl_counters_enabled_set(acl_key, True) if counting.lower() == "false": self.acl_mgr.acl_counters_enabled_set(acl_key, False) # Rules files is needed. Does it actually exist? # Is a comprehensive unwind needed in the error case? if rules_file is None: syslog.syslog("Need to add/remove rules but no rule info file specified") sys.stderr.write("Need to add/remove rules but no rule info file specified\n") continue # Attempt to parse rules_file # Initially, attempt to acquire the lock to ensure file not modified # by another entity while it is being processed here. syslog.syslog("Attempting to open, lock and parse %s" % rules_file) try: with open(rules_file) as rule_listing_file: for i in xrange(file_lock_attempt): try: fcntl.flock(rule_listing_file, fcntl.LOCK_EX | fcntl.LOCK_NB) break except IOError: sys.stderr.write("Attempt %s to lock %s " "failed\n" % (str(i+1), rule_file)) time.sleep(file_lock_interval) else: syslog.syslog("All %s attempts to lock %s " "failed" % (str(file_lock_attempt), rules_file)) sys.stderr.write("All %s attempts to lock %s " "failed\n" % (str(file_lock_attempt), rules_file)) return syslog.syslog("%s opened & locked successfully. Now parse" % rules_file) rule_list = json.load(rule_listing_file) except IOError: syslog.syslog("Cannot open %s" % rules_file) sys.stderr.write("Cannot open %s\n" % rules_file) continue # Now iterate over all rules for index, rule in enumerate(rule_list): number = rule.get("number") source = rule.get("source") destination = rule.get("destination") protocol = rule.get("protocol") action = rule.get("action") log = rule.get("log") # Rule must have a sequence number. if number is None: syslog.syslog("Rule must have a number") sys.stderr.write("Rule must have a number\n") continue # If input command is to delete the rule, simply call the SDK API # and continue to next rule in the list. # i.e. no need to be concerned with addresses, protocols etc. if command.lower() == "delete-rule": self.acl_mgr.acl_rule_del(acl_key, int(number)) continue # If here, then the command must be to add rule. # So verify if rule data supplied is valid. if rule_validate(number, source, destination, action) == False: syslog.syslog("Invalid rule input data") sys.stderr.write("Invalid rule input data\n") continue # Create IP ACL rule object. Used for IPv4 and IPv6 ACLs but # different object needed for Ethernet ACLs. # (Perhaps create object with all rule info and invoke # functions to process for ACL, IPv4, IPv6 etc? # Needs more thought on optimal way to handle different ACL type) acl_rule = eossdk.AclRuleIp() # Now parse the rule data and invoke appropriate SDK # APIs to create requisite data structures. if source: try: # How is "any" handled? 0.0.0.0/0? if source == "any": source = "0.0.0.0/0" # String to IP address conversion of source? sdk_prefix = eossdk.IpPrefix(str(source)) sdk_addr = eossdk.IpAddrMask(sdk_prefix.network(), sdk_prefix.prefix_length()) acl_rule.source_addr_is(sdk_addr) except eossdk.Error: syslog.syslog("Error processing source address %s" % source) sys.stderr.write("Error processing source address %s\n" % source) continue if destination: try: # How is "any" handled? 0.0.0.0/0? if destination == "any": destination = "0.0.0.0/0" # String to IP address conversion of destination? sdk_prefix = eossdk.IpPrefix(str(destination)) sdk_addr = eossdk.IpAddrMask(sdk_prefix.network(), sdk_prefix.prefix_length()) acl_rule.destination_addr_is(sdk_addr) except eossdk.Error: syslog.syslog("Error processing destination address %s" % destination) sys.stderr.write("Error processing destination address %s\n" % destination) continue if protocol: protocol_number = protocol_validate(protocol, sdk_type) if protocol_number: acl_rule.ip_protocol_is(protocol_number) else: syslog.syslog("Error processing protocol %s" % protocol) sys.stderr.write("Error processing protocol %s\n" % protocol) continue # Previously verified that action is 'permit' or 'deny' if action: try: if action.lower() == "permit": acl_rule.action_is(eossdk.ACL_PERMIT) else: acl_rule.action_is(eossdk.ACL_DENY) except: syslog.syslog("Error processing action %s" % action) sys.stderr.write("Error processing action %s\n" % action) continue if log: try: if log.lower() == "true": acl_rule.log_is(True) else: acl_rule.log_is(False) except: syslog.syslog("Error processing log %s" % log) sys.stderr.write("Error processing log %s\n" % log) continue # Now add this rule to ACL, with appropriate sequence number # Should only ever be here when adding rules.... paranoid check. if command.lower() == "add-rule": self.acl_mgr.acl_rule_set(acl_key, int(number), acl_rule) parsing_time = time.time() self.parsing_time = parsing_time self.parsing_duration = parsing_time - start_time syslog.syslog("Time to parse config files for ACL %s " "is %ss" % (name, self.parsing_duration)) self.rule_count = index+1 syslog.syslog("Processing %s rules complete. " "Now commit ACL %s to HW" % (index+1, name)) # Now call commit to actually push changes to HW. self.acl_mgr.acl_commit() # Should ACL be attached or detached from interface? if intf_id: if operation.lower() == "attach": syslog.syslog("Attaching ACL %s to interface %s " "%sbound" % (name, interface, direction.lower())) self.acl_mgr.acl_apply(acl_key, intf_id, sdk_direction, True) if operation.lower() == "detach": syslog.syslog("Detaching ACL %s from interface %s " "%sbound" % (name, interface, direction.lower())) self.acl_mgr.acl_apply(acl_key, intf_id, sdk_direction, False)